fix(WEB): 수주 완전삭제(force) 시 생산지시완료 상태 처리 및 skip 응답 반영
- bulkDestroy force=true일 때 상태 체크 bypass, 연관 작업지시 데이터 모두 삭제 - forceDeleteWorkOrders() 헬퍼: 자재투입 재고복구, 문서, 부속데이터 정리 후 hard delete Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -428,31 +428,34 @@ public function bulkDestroy(array $ids, bool $force = false): array
|
||||
$deletedCount = 0;
|
||||
$skippedIds = [];
|
||||
|
||||
return DB::transaction(function () use ($orders, $force, $userId, &$deletedCount, &$skippedIds) {
|
||||
return DB::transaction(function () use ($orders, $force, $userId, $tenantId, &$deletedCount, &$skippedIds) {
|
||||
foreach ($orders as $order) {
|
||||
// 상태 검증: DRAFT/CONFIRMED/CANCELLED만 삭제 가능
|
||||
if (! in_array($order->status_code, [
|
||||
Order::STATUS_DRAFT,
|
||||
Order::STATUS_CONFIRMED,
|
||||
Order::STATUS_CANCELLED,
|
||||
])) {
|
||||
$skippedIds[] = $order->id;
|
||||
if ($force) {
|
||||
// force=true (개발환경 완전삭제): 모든 상태 허용, 연관 데이터 모두 삭제
|
||||
$this->forceDeleteWorkOrders($order, $tenantId);
|
||||
} else {
|
||||
// 일반 삭제: 상태/작업지시/출하 검증
|
||||
if (! in_array($order->status_code, [
|
||||
Order::STATUS_DRAFT,
|
||||
Order::STATUS_CONFIRMED,
|
||||
Order::STATUS_CANCELLED,
|
||||
])) {
|
||||
$skippedIds[] = $order->id;
|
||||
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 작업지시 존재 시 skip
|
||||
if ($order->workOrders()->exists()) {
|
||||
$skippedIds[] = $order->id;
|
||||
if ($order->workOrders()->exists()) {
|
||||
$skippedIds[] = $order->id;
|
||||
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 출하 존재 시 skip
|
||||
if ($order->shipments()->exists()) {
|
||||
$skippedIds[] = $order->id;
|
||||
if ($order->shipments()->exists()) {
|
||||
$skippedIds[] = $order->id;
|
||||
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 견적 연결 해제
|
||||
@@ -500,6 +503,72 @@ public function bulkDestroy(array $ids, bool $force = false): array
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 작업지시 및 연관 데이터 강제 삭제 (개발환경 완전삭제용)
|
||||
*/
|
||||
private function forceDeleteWorkOrders(Order $order, int $tenantId): void
|
||||
{
|
||||
$workOrderIds = WorkOrder::where('tenant_id', $tenantId)
|
||||
->where('sales_order_id', $order->id)
|
||||
->pluck('id')
|
||||
->toArray();
|
||||
|
||||
if (empty($workOrderIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 자재 투입 재고 복구 + 삭제
|
||||
$materialInputs = WorkOrderMaterialInput::whereIn('work_order_id', $workOrderIds)->get();
|
||||
if ($materialInputs->isNotEmpty()) {
|
||||
$stockService = app(StockService::class);
|
||||
foreach ($materialInputs as $input) {
|
||||
try {
|
||||
$stockService->increaseToLot(
|
||||
stockLotId: $input->stock_lot_id,
|
||||
qty: (float) $input->qty,
|
||||
reason: 'work_order_input_cancel',
|
||||
referenceId: $input->work_order_id
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
Log::warning('완전삭제: 재고 복원 실패', [
|
||||
'input_id' => $input->id,
|
||||
'stock_lot_id' => $input->stock_lot_id,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
WorkOrderMaterialInput::whereIn('work_order_id', $workOrderIds)->delete();
|
||||
}
|
||||
|
||||
// 2. 문서 삭제
|
||||
$documentIds = Document::where('linkable_type', 'work_order')
|
||||
->whereIn('linkable_id', $workOrderIds)
|
||||
->pluck('id')
|
||||
->toArray();
|
||||
|
||||
if (! empty($documentIds)) {
|
||||
DocumentData::whereIn('document_id', $documentIds)->delete();
|
||||
DocumentApproval::whereIn('document_id', $documentIds)->delete();
|
||||
Document::whereIn('id', $documentIds)->forceDelete();
|
||||
}
|
||||
|
||||
// 3. 출하 참조 해제
|
||||
DB::table('shipments')
|
||||
->whereIn('work_order_id', $workOrderIds)
|
||||
->update(['work_order_id' => null]);
|
||||
|
||||
// 4. 부속 데이터 삭제
|
||||
DB::table('work_order_step_progress')->whereIn('work_order_id', $workOrderIds)->delete();
|
||||
DB::table('work_order_assignees')->whereIn('work_order_id', $workOrderIds)->delete();
|
||||
DB::table('work_order_bending_details')->whereIn('work_order_id', $workOrderIds)->delete();
|
||||
DB::table('work_order_issues')->whereIn('work_order_id', $workOrderIds)->delete();
|
||||
DB::table('work_results')->whereIn('work_order_id', $workOrderIds)->delete();
|
||||
|
||||
// 5. 작업지시 품목 → 작업지시 삭제
|
||||
DB::table('work_order_items')->whereIn('work_order_id', $workOrderIds)->delete();
|
||||
WorkOrder::whereIn('id', $workOrderIds)->forceDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태 변경
|
||||
*/
|
||||
@@ -783,6 +852,17 @@ public function createFromQuote(int $quoteId, array $data = [])
|
||||
? intdiv($quote->items->count(), $locationCount)
|
||||
: 0;
|
||||
|
||||
// DEBUG: 분배 로직 디버깅 (임시)
|
||||
\Log::info('[createFromQuote] Distribution params', [
|
||||
'quoteId' => $quote->id,
|
||||
'itemCount' => $quote->items->count(),
|
||||
'locationCount' => $locationCount,
|
||||
'hasFormulaSource' => $hasFormulaSource,
|
||||
'itemsPerLocation' => $itemsPerLocation,
|
||||
'collectionKeys_first5' => $quote->items->keys()->take(5)->all(),
|
||||
'nodeMapKeys' => array_keys($nodeMap),
|
||||
]);
|
||||
|
||||
foreach ($quote->items as $index => $quoteItem) {
|
||||
$floorCode = null;
|
||||
$symbolCode = null;
|
||||
@@ -799,6 +879,11 @@ public function createFromQuote(int $quoteId, array $data = [])
|
||||
$locIdx = min(intdiv($index, $itemsPerLocation), $locationCount - 1);
|
||||
}
|
||||
|
||||
// DEBUG: 처음 3개와 전환점(17-19) 로깅 (임시)
|
||||
if ($index < 3 || ($index >= 17 && $index <= 19)) {
|
||||
\Log::info("[createFromQuote] item idx={$index} locIdx={$locIdx} fs='{$formulaSource}'");
|
||||
}
|
||||
|
||||
// calculation_inputs에서 floor/code 가져오기
|
||||
if (isset($productItems[$locIdx])) {
|
||||
$floorCode = $productItems[$locIdx]['floor'] ?? null;
|
||||
|
||||
Reference in New Issue
Block a user