From 2d68e5e669587eaeed019094a3c66df50b66ccbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Tue, 10 Feb 2026 19:03:22 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=88=98=EC=A3=BC=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=20=EC=8B=9C=20=EA=B2=AC=EC=A0=81=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?=ED=95=B4=EC=A0=9C=20+=20=EC=83=9D=EC=82=B0=EC=A7=80=EC=8B=9C?= =?UTF-8?q?=20=EA=B3=B5=EC=A0=95=20=EB=A7=A4=ED=95=91=20=EB=B3=B4=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OrderService::destroy()에서 견적 order_id/status 초기화 - StoreOrderRequest/UpdateOrderRequest에 floor_code, symbol_code, item_code 추가 - createProductionOrder()에 item_code fallback 공정 매핑 추가 Co-Authored-By: Claude Opus 4.6 --- app/Http/Requests/Order/StoreOrderRequest.php | 3 ++ .../Requests/Order/UpdateOrderRequest.php | 3 ++ app/Services/OrderService.php | 53 +++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/app/Http/Requests/Order/StoreOrderRequest.php b/app/Http/Requests/Order/StoreOrderRequest.php index 0265aed..dbe67ff 100644 --- a/app/Http/Requests/Order/StoreOrderRequest.php +++ b/app/Http/Requests/Order/StoreOrderRequest.php @@ -58,6 +58,7 @@ public function rules(): array // 품목 배열 'items' => 'nullable|array', 'items.*.item_id' => 'nullable|integer|exists:items,id', + 'items.*.item_code' => 'nullable|string|max:50', 'items.*.item_name' => 'required|string|max:200', 'items.*.specification' => 'nullable|string|max:500', 'items.*.quantity' => 'required|numeric|min:0', @@ -66,6 +67,8 @@ public function rules(): array 'items.*.supply_amount' => 'nullable|numeric|min:0', 'items.*.tax_amount' => 'nullable|numeric|min:0', 'items.*.total_amount' => 'nullable|numeric|min:0', + 'items.*.floor_code' => 'nullable|string|max:50', + 'items.*.symbol_code' => 'nullable|string|max:50', ]; } diff --git a/app/Http/Requests/Order/UpdateOrderRequest.php b/app/Http/Requests/Order/UpdateOrderRequest.php index 7f40a32..a865312 100644 --- a/app/Http/Requests/Order/UpdateOrderRequest.php +++ b/app/Http/Requests/Order/UpdateOrderRequest.php @@ -52,6 +52,7 @@ public function rules(): array // 품목 배열 (전체 교체) 'items' => 'nullable|array', 'items.*.item_id' => 'nullable|integer|exists:items,id', + 'items.*.item_code' => 'nullable|string|max:50', 'items.*.item_name' => 'required|string|max:200', 'items.*.specification' => 'nullable|string|max:500', 'items.*.quantity' => 'required|numeric|min:0', @@ -60,6 +61,8 @@ public function rules(): array 'items.*.supply_amount' => 'nullable|numeric|min:0', 'items.*.tax_amount' => 'nullable|numeric|min:0', 'items.*.total_amount' => 'nullable|numeric|min:0', + 'items.*.floor_code' => 'nullable|string|max:50', + 'items.*.symbol_code' => 'nullable|string|max:50', ]; } diff --git a/app/Services/OrderService.php b/app/Services/OrderService.php index 151c2f2..836dea8 100644 --- a/app/Services/OrderService.php +++ b/app/Services/OrderService.php @@ -284,6 +284,17 @@ public function destroy(int $id) } return DB::transaction(function () use ($order, $userId) { + // 0. 연결된 견적의 수주 연결 해제 (order_id → null, status → finalized) + if ($order->quote_id) { + Quote::withoutGlobalScopes() + ->where('id', $order->quote_id) + ->where('order_id', $order->id) + ->update([ + 'order_id' => null, + 'status' => Quote::STATUS_FINALIZED, + ]); + } + // 1. order_item_components soft delete foreach ($order->items as $item) { $item->components()->update(['deleted_by' => $userId]); @@ -872,6 +883,20 @@ public function createProductionOrder(int $orderId, array $data) } } + // item_code → item_id 매핑 구축 (fallback용) + $codeToIdMap = []; + if (! empty($bomItemIds)) { + $codeToIdRows = DB::table('items') + ->where('tenant_id', $tenantId) + ->whereIn('id', $bomItemIds) + ->whereNull('deleted_at') + ->select('id', 'code') + ->get(); + foreach ($codeToIdRows as $row) { + $codeToIdMap[$row->code] = $row->id; + } + } + // order_items를 공정별로 그룹화 (BOM item_id → process 매핑 활용) $itemsByProcess = []; foreach ($order->items as $orderItem) { @@ -890,6 +915,34 @@ public function createProductionOrder(int $orderId, array $data) } } + // 3. fallback: item_code로 items 마스터 조회 → process_items 매핑 + if ($processId === null && $orderItem->item_code) { + $resolvedId = $codeToIdMap[$orderItem->item_code] ?? null; + if (! $resolvedId) { + $resolvedId = DB::table('items') + ->where('tenant_id', $tenantId) + ->where('code', $orderItem->item_code) + ->whereNull('deleted_at') + ->value('id'); + if ($resolvedId) { + $codeToIdMap[$orderItem->item_code] = $resolvedId; + } + } + if ($resolvedId && isset($itemProcessMap[$resolvedId])) { + $processId = $itemProcessMap[$resolvedId]; + } elseif ($resolvedId) { + // process_items에서도 조회 + $pi = DB::table('process_items') + ->where('item_id', $resolvedId) + ->where('is_active', true) + ->value('process_id'); + if ($pi) { + $processId = $pi; + $itemProcessMap[$resolvedId] = $pi; + } + } + } + $key = $processId ?? 'none'; if (! isset($itemsByProcess[$key])) {