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:
@@ -753,9 +753,9 @@ public function createProductionOrder(int $orderId, array $data)
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
// 수주 조회
|
||||
// 수주 + 노드 조회
|
||||
$order = Order::where('tenant_id', $tenantId)
|
||||
->with('items')
|
||||
->with(['items', 'rootNodes'])
|
||||
->find($orderId);
|
||||
|
||||
if (! $order) {
|
||||
@@ -776,16 +776,31 @@ public function createProductionOrder(int $orderId, array $data)
|
||||
throw new BadRequestHttpException(__('error.order.production_order_already_exists'));
|
||||
}
|
||||
|
||||
// order_items의 item_id를 기반으로 공정별 자동 분류
|
||||
$itemIds = $order->items->pluck('item_id')->filter()->unique()->values()->toArray();
|
||||
// order_nodes의 BOM 결과를 기반으로 공정별 자동 분류
|
||||
$bomItemIds = [];
|
||||
$nodesBomMap = []; // node_id => [item_name => bom_item]
|
||||
|
||||
foreach ($order->rootNodes as $node) {
|
||||
$bomResult = $node->options['bom_result'] ?? [];
|
||||
$bomItems = $bomResult['items'] ?? [];
|
||||
|
||||
foreach ($bomItems as $bomItem) {
|
||||
if (! empty($bomItem['item_id'])) {
|
||||
$bomItemIds[] = $bomItem['item_id'];
|
||||
$nodesBomMap[$node->id][$bomItem['item_name']] = $bomItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$bomItemIds = array_unique($bomItemIds);
|
||||
|
||||
// process_items 테이블에서 item_id → process_id 매핑 조회
|
||||
$itemProcessMap = [];
|
||||
if (! empty($itemIds)) {
|
||||
if (! empty($bomItemIds)) {
|
||||
$processItems = DB::table('process_items as pi')
|
||||
->join('processes as p', 'pi.process_id', '=', 'p.id')
|
||||
->where('p.tenant_id', $tenantId)
|
||||
->whereIn('pi.item_id', $itemIds)
|
||||
->whereIn('pi.item_id', $bomItemIds)
|
||||
->where('pi.is_active', true)
|
||||
->select('pi.item_id', 'pi.process_id')
|
||||
->get();
|
||||
@@ -795,11 +810,25 @@ public function createProductionOrder(int $orderId, array $data)
|
||||
}
|
||||
}
|
||||
|
||||
// order_items를 공정별로 그룹화
|
||||
// order_items를 공정별로 그룹화 (BOM item_id → process 매핑 활용)
|
||||
$itemsByProcess = [];
|
||||
foreach ($order->items as $orderItem) {
|
||||
$processId = $itemProcessMap[$orderItem->item_id] ?? null;
|
||||
$key = $processId ?? 'none'; // null은 'none' 키로 그룹화
|
||||
$processId = null;
|
||||
|
||||
// 1. order_item의 item_id가 있으면 직접 매핑
|
||||
if ($orderItem->item_id && isset($itemProcessMap[$orderItem->item_id])) {
|
||||
$processId = $itemProcessMap[$orderItem->item_id];
|
||||
}
|
||||
// 2. item_id가 없으면 노드의 BOM에서 item_name으로 찾기
|
||||
elseif ($orderItem->order_node_id && isset($nodesBomMap[$orderItem->order_node_id])) {
|
||||
$nodeBom = $nodesBomMap[$orderItem->order_node_id];
|
||||
$bomItem = $nodeBom[$orderItem->item_name] ?? null;
|
||||
if ($bomItem && ! empty($bomItem['item_id']) && isset($itemProcessMap[$bomItem['item_id']])) {
|
||||
$processId = $itemProcessMap[$bomItem['item_id']];
|
||||
}
|
||||
}
|
||||
|
||||
$key = $processId ?? 'none';
|
||||
|
||||
if (! isset($itemsByProcess[$key])) {
|
||||
$itemsByProcess[$key] = [
|
||||
@@ -810,7 +839,7 @@ public function createProductionOrder(int $orderId, array $data)
|
||||
$itemsByProcess[$key]['items'][] = $orderItem;
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($order, $data, $tenantId, $userId, $itemsByProcess) {
|
||||
return DB::transaction(function () use ($order, $data, $tenantId, $userId, $itemsByProcess, $nodesBomMap) {
|
||||
$workOrders = [];
|
||||
|
||||
foreach ($itemsByProcess as $key => $group) {
|
||||
@@ -840,11 +869,18 @@ public function createProductionOrder(int $orderId, array $data)
|
||||
// work_order_items에 아이템 추가
|
||||
$sortOrder = 1;
|
||||
foreach ($items as $orderItem) {
|
||||
// item_id 결정: order_item에 있으면 사용, 없으면 BOM에서 가져오기
|
||||
$itemId = $orderItem->item_id;
|
||||
if (! $itemId && $orderItem->order_node_id && isset($nodesBomMap[$orderItem->order_node_id])) {
|
||||
$bomItem = $nodesBomMap[$orderItem->order_node_id][$orderItem->item_name] ?? null;
|
||||
$itemId = $bomItem['item_id'] ?? null;
|
||||
}
|
||||
|
||||
DB::table('work_order_items')->insert([
|
||||
'tenant_id' => $tenantId,
|
||||
'work_order_id' => $workOrder->id,
|
||||
'source_order_item_id' => $orderItem->id,
|
||||
'item_id' => $orderItem->item_id,
|
||||
'item_id' => $itemId,
|
||||
'item_name' => $orderItem->item_name,
|
||||
'specification' => $orderItem->specification,
|
||||
'quantity' => $orderItem->quantity,
|
||||
|
||||
Reference in New Issue
Block a user