feat: 생산지시 생성 시 공정 자동 분류 및 아이템 연결
- OrderService: 생산지시 생성 로직 개선 - order_items.item_id → process_items 테이블에서 공정 자동 조회 - 공정별로 아이템 그룹화 (미지정 아이템은 별도 그룹) - 각 공정별 작업지시 생성 - work_order_items에 해당 공정의 아이템들 자동 추가 - WorkOrderService: 목록 조회 시 관계 추가 - items 관계 추가 (틀수 계산용) - process.department 필드 추가 (부서 표시용) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -612,29 +612,29 @@ public function syncFromQuote(Quote $quote, int $revision): ?Order
|
||||
$floorCode = null;
|
||||
$symbolCode = null;
|
||||
|
||||
// formula_source에서 제품 인덱스 추출
|
||||
$productIndex = 0;
|
||||
$formulaSource = $quoteItem->formula_source ?? '';
|
||||
if (preg_match('/product_(\d+)/', $formulaSource, $matches)) {
|
||||
$productIndex = (int) $matches[1];
|
||||
// 1순위: note에서 floor/code 파싱 (가장 정확한 정보)
|
||||
// note 형식: "4F FSS-01" (공백으로 구분)
|
||||
$note = trim($quoteItem->note ?? '');
|
||||
if ($note !== '') {
|
||||
$parts = preg_split('/\s+/', $note, 2);
|
||||
$floorCode = $parts[0] ?? null;
|
||||
$symbolCode = $parts[1] ?? null;
|
||||
}
|
||||
|
||||
// calculation_inputs에서 floor/code 가져오기
|
||||
if (isset($productItems[$productIndex])) {
|
||||
$floorCode = $productItems[$productIndex]['floor'] ?? null;
|
||||
$symbolCode = $productItems[$productIndex]['code'] ?? null;
|
||||
} elseif (count($productItems) === 1) {
|
||||
$floorCode = $productItems[0]['floor'] ?? null;
|
||||
$symbolCode = $productItems[0]['code'] ?? null;
|
||||
}
|
||||
|
||||
// note에서 파싱 시도
|
||||
// 2순위: formula_source에서 제품 인덱스 추출하여 calculation_inputs에서 가져오기
|
||||
if (empty($floorCode) && empty($symbolCode)) {
|
||||
$note = trim($quoteItem->note ?? '');
|
||||
if ($note !== '') {
|
||||
$parts = preg_split('/\s+/', $note, 2);
|
||||
$floorCode = $parts[0] ?? null;
|
||||
$symbolCode = $parts[1] ?? null;
|
||||
$productIndex = 0;
|
||||
$formulaSource = $quoteItem->formula_source ?? '';
|
||||
if (preg_match('/product_(\d+)/', $formulaSource, $matches)) {
|
||||
$productIndex = (int) $matches[1];
|
||||
}
|
||||
|
||||
if (isset($productItems[$productIndex])) {
|
||||
$floorCode = $productItems[$productIndex]['floor'] ?? null;
|
||||
$symbolCode = $productItems[$productIndex]['code'] ?? null;
|
||||
} elseif (count($productItems) === 1) {
|
||||
$floorCode = $productItems[0]['floor'] ?? null;
|
||||
$symbolCode = $productItems[0]['code'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,21 +721,47 @@ public function createProductionOrder(int $orderId, array $data)
|
||||
throw new BadRequestHttpException(__('error.order.production_order_already_exists'));
|
||||
}
|
||||
|
||||
// process_ids 배열 또는 단일 process_id 처리
|
||||
$processIds = $data['process_ids'] ?? [];
|
||||
if (empty($processIds) && ! empty($data['process_id'])) {
|
||||
$processIds = [$data['process_id']];
|
||||
// order_items의 item_id를 기반으로 공정별 자동 분류
|
||||
$itemIds = $order->items->pluck('item_id')->filter()->unique()->values()->toArray();
|
||||
|
||||
// process_items 테이블에서 item_id → process_id 매핑 조회
|
||||
$itemProcessMap = [];
|
||||
if (! empty($itemIds)) {
|
||||
$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)
|
||||
->where('pi.is_active', true)
|
||||
->select('pi.item_id', 'pi.process_id')
|
||||
->get();
|
||||
|
||||
foreach ($processItems as $pi) {
|
||||
$itemProcessMap[$pi->item_id] = $pi->process_id;
|
||||
}
|
||||
}
|
||||
|
||||
// 공정이 없으면 null로 하나만 생성
|
||||
if (empty($processIds)) {
|
||||
$processIds = [null];
|
||||
// order_items를 공정별로 그룹화
|
||||
$itemsByProcess = [];
|
||||
foreach ($order->items as $orderItem) {
|
||||
$processId = $itemProcessMap[$orderItem->item_id] ?? null;
|
||||
$key = $processId ?? 'none'; // null은 'none' 키로 그룹화
|
||||
|
||||
if (! isset($itemsByProcess[$key])) {
|
||||
$itemsByProcess[$key] = [
|
||||
'process_id' => $processId,
|
||||
'items' => [],
|
||||
];
|
||||
}
|
||||
$itemsByProcess[$key]['items'][] = $orderItem;
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($order, $data, $tenantId, $userId, $processIds) {
|
||||
return DB::transaction(function () use ($order, $data, $tenantId, $userId, $itemsByProcess) {
|
||||
$workOrders = [];
|
||||
|
||||
foreach ($processIds as $processId) {
|
||||
foreach ($itemsByProcess as $key => $group) {
|
||||
$processId = $group['process_id'];
|
||||
$items = $group['items'];
|
||||
|
||||
// 작업지시번호 생성
|
||||
$workOrderNo = $this->generateWorkOrderNo($tenantId);
|
||||
|
||||
@@ -756,6 +782,25 @@ public function createProductionOrder(int $orderId, array $data)
|
||||
'updated_by' => $userId,
|
||||
]);
|
||||
|
||||
// work_order_items에 아이템 추가
|
||||
$sortOrder = 1;
|
||||
foreach ($items as $orderItem) {
|
||||
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_name' => $orderItem->item_name,
|
||||
'specification' => $orderItem->specification,
|
||||
'quantity' => $orderItem->quantity,
|
||||
'unit' => $orderItem->unit,
|
||||
'sort_order' => $sortOrder++,
|
||||
'status' => 'pending',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
$workOrders[] = $workOrder->load(['assignee:id,name', 'team:id,name', 'process:id,process_name,process_code']);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ public function index(array $params)
|
||||
'team:id,name',
|
||||
'salesOrder:id,order_no,client_id,client_name',
|
||||
'salesOrder.client:id,name',
|
||||
'process:id,process_name,process_code',
|
||||
'process:id,process_name,process_code,department',
|
||||
'items:id,work_order_id,item_name,quantity',
|
||||
]);
|
||||
|
||||
// 검색어
|
||||
@@ -66,8 +67,14 @@ public function index(array $params)
|
||||
}
|
||||
|
||||
// 공정 필터 (process_id)
|
||||
// - 'none' 또는 '0': 공정 미지정 (process_id IS NULL)
|
||||
// - 숫자: 해당 공정 ID로 필터
|
||||
if ($processId !== null) {
|
||||
$query->where('process_id', $processId);
|
||||
if ($processId === 'none' || $processId === '0' || $processId === 0) {
|
||||
$query->whereNull('process_id');
|
||||
} else {
|
||||
$query->where('process_id', $processId);
|
||||
}
|
||||
}
|
||||
|
||||
// 공정 코드 필터 (process_code) - 대시보드용
|
||||
@@ -143,9 +150,10 @@ public function show(int $id)
|
||||
'assignee:id,name',
|
||||
'assignees.user:id,name',
|
||||
'team:id,name',
|
||||
'salesOrder:id,order_no,site_name,client_id',
|
||||
'salesOrder:id,order_no,site_name,client_id,client_contact,received_at,writer_id,created_at,quantity',
|
||||
'salesOrder.client:id,name',
|
||||
'process:id,process_name,process_code,work_steps',
|
||||
'salesOrder.writer:id,name',
|
||||
'process:id,process_name,process_code,work_steps,department',
|
||||
'items',
|
||||
'bendingDetail',
|
||||
'issues' => fn ($q) => $q->orderByDesc('created_at'),
|
||||
|
||||
Reference in New Issue
Block a user