feat: 견적확정 밸리데이션, 작업지시 통계 공정별 카운트, 입고/재고 개선
- 견적확정 시 업체명/현장명/담당자/연락처 필수 검증 추가 (QuoteService) - 작업지시 stats API에 by_process 공정별 카운트 반환 추가 - 작업지시 목록/상세 쿼리에 수주 개소(rootNodes) 연관 로딩 - 작업지시 품목에 sourceOrderItem.node 관계 추가 - 입고관리 완료건 수정 허용 및 재고 차이 조정 - work_order_step_progress 테이블 마이그레이션 - receivings 테이블 options 컬럼 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -313,6 +313,107 @@ public function increaseFromReceiving(Receiving $receiving): StockLot
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 입고 수정 시 재고 조정 (차이만큼 증감)
|
||||
*
|
||||
* - completed→completed 수량변경: 차이만큼 조정 (50→60 = +10)
|
||||
* - completed→대기: 전량 차감 (newQty = 0)
|
||||
*
|
||||
* @param Receiving $receiving 입고 레코드
|
||||
* @param float $newQty 새 수량 (상태가 completed가 아니면 0)
|
||||
*/
|
||||
public function adjustFromReceiving(Receiving $receiving, float $newQty): void
|
||||
{
|
||||
if (! $receiving->item_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
DB::transaction(function () use ($receiving, $newQty, $tenantId, $userId) {
|
||||
// 1. 해당 입고로 생성된 StockLot 조회
|
||||
$stockLot = StockLot::where('tenant_id', $tenantId)
|
||||
->where('receiving_id', $receiving->id)
|
||||
->first();
|
||||
|
||||
if (! $stockLot) {
|
||||
Log::warning('StockLot not found for receiving adjustment', [
|
||||
'receiving_id' => $receiving->id,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$stock = Stock::where('id', $stockLot->stock_id)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
|
||||
if (! $stock) {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldQty = (float) $stockLot->qty;
|
||||
$diff = $newQty - $oldQty;
|
||||
|
||||
// 차이가 없으면 스킵
|
||||
if (abs($diff) < 0.001) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. StockLot 수량 조정
|
||||
$stockLot->qty = $newQty;
|
||||
$stockLot->available_qty = max(0, $newQty - $stockLot->reserved_qty);
|
||||
$stockLot->updated_by = $userId;
|
||||
|
||||
if ($newQty <= 0) {
|
||||
$stockLot->qty = 0;
|
||||
$stockLot->available_qty = 0;
|
||||
$stockLot->reserved_qty = 0;
|
||||
$stockLot->status = 'used';
|
||||
} else {
|
||||
$stockLot->status = 'available';
|
||||
}
|
||||
|
||||
$stockLot->save();
|
||||
|
||||
// 3. Stock 정보 갱신
|
||||
$stock->refreshFromLots();
|
||||
|
||||
// 4. 거래 이력 기록
|
||||
$this->recordTransaction(
|
||||
stock: $stock,
|
||||
type: $diff > 0 ? StockTransaction::TYPE_IN : StockTransaction::TYPE_OUT,
|
||||
qty: $diff,
|
||||
reason: StockTransaction::REASON_RECEIVING,
|
||||
referenceType: 'receiving',
|
||||
referenceId: $receiving->id,
|
||||
lotNo: $receiving->lot_no,
|
||||
stockLotId: $stockLot->id
|
||||
);
|
||||
|
||||
// 5. 감사 로그 기록
|
||||
$this->logStockChange(
|
||||
stock: $stock,
|
||||
action: $diff > 0 ? 'stock_increase' : 'stock_decrease',
|
||||
reason: 'receiving_adjustment',
|
||||
referenceType: 'receiving',
|
||||
referenceId: $receiving->id,
|
||||
qtyChange: $diff,
|
||||
lotNo: $receiving->lot_no
|
||||
);
|
||||
|
||||
Log::info('Stock adjusted from receiving modification', [
|
||||
'receiving_id' => $receiving->id,
|
||||
'item_id' => $receiving->item_id,
|
||||
'stock_id' => $stock->id,
|
||||
'old_qty' => $oldQty,
|
||||
'new_qty' => $newQty,
|
||||
'diff' => $diff,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stock 조회 또는 생성
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user