From 897511cb5582a729727c13c3a5f5dac9179b0d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Wed, 4 Mar 2026 23:28:03 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[production]=20=EC=A0=88=EA=B3=A1=20?= =?UTF-8?q?=EA=B2=80=EC=82=AC=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20item=20=EB=B3=B5=EC=A0=9C=20+=20bending=20EAV=20?= =?UTF-8?q?=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - storeItemInspection: bending/bending_wip 시 동일 작업지시 모든 item에 복제 저장 - transformBendingProductsToRecords: products 배열 → bending EAV 레코드 변환 - getMaterialInputLots: 품목코드별 그룹핑으로 변경 Co-Authored-By: Claude Opus 4.6 --- app/Services/WorkOrderService.php | 127 +++++++++++++++++++++++++++--- 1 file changed, 117 insertions(+), 10 deletions(-) diff --git a/app/Services/WorkOrderService.php b/app/Services/WorkOrderService.php index e5d8601c..e35f3573 100644 --- a/app/Services/WorkOrderService.php +++ b/app/Services/WorkOrderService.php @@ -1837,25 +1837,25 @@ public function getMaterialInputLots(int $workOrderId): array ->orderBy('created_at') ->get(['id', 'lot_no', 'item_code', 'item_name', 'qty', 'stock_lot_id', 'created_at']); - // LOT 번호별 그룹핑 (동일 LOT에서 여러번 투입 가능) - $lotMap = []; + // 품목코드별 그룹핑 (작업일지에서 item_code → lot_no 매핑에 사용) + $itemMap = []; foreach ($transactions as $tx) { - $lotNo = $tx->lot_no; - if (! isset($lotMap[$lotNo])) { - $lotMap[$lotNo] = [ - 'lot_no' => $lotNo, - 'item_code' => $tx->item_code, + $itemCode = $tx->item_code; + if (! isset($itemMap[$itemCode])) { + $itemMap[$itemCode] = [ + 'item_code' => $itemCode, + 'lot_no' => $tx->lot_no, 'item_name' => $tx->item_name, 'total_qty' => 0, 'input_count' => 0, 'first_input_at' => $tx->created_at, ]; } - $lotMap[$lotNo]['total_qty'] += abs((float) $tx->qty); - $lotMap[$lotNo]['input_count']++; + $itemMap[$itemCode]['total_qty'] += abs((float) $tx->qty); + $itemMap[$itemCode]['input_count']++; } - return array_values($lotMap); + return array_values($itemMap); } // ────────────────────────────────────────────────────────────── @@ -1890,6 +1890,16 @@ public function storeItemInspection(int $workOrderId, int $itemId, array $data): $item->setInspectionData($inspectionData); $item->save(); + // 절곡 공정: 수주 단위 검사 → 동일 작업지시의 모든 item에 검사 데이터 복제 + $processType = $data['process_type'] ?? ''; + if (in_array($processType, ['bending', 'bending_wip'])) { + $otherItems = $workOrder->items()->where('id', '!=', $itemId)->get(); + foreach ($otherItems as $otherItem) { + $otherItem->setInspectionData($inspectionData); + $otherItem->save(); + } + } + // 감사 로그 $this->auditLogger->log( $tenantId, @@ -2492,10 +2502,107 @@ private function transformInspectionDataToDocumentRecords(array $rawItems, int $ ], $rawItems); } + // 절곡 products 배열 감지 → bending 전용 EAV 레코드 생성 + $productsItem = collect($rawItems)->first(fn ($item) => isset($item['products']) && is_array($item['products'])); + if ($productsItem) { + return $this->transformBendingProductsToRecords($productsItem, $templateId); + } + // 레거시 형식: templateValues/values 기반 → 정규화 변환 return $this->normalizeOldFormatRecords($rawItems, $templateId); } + /** + * 절곡 products 배열 → bending 전용 EAV 레코드 변환 + * + * InspectionInputModal이 저장하는 products 형식: + * [{ id, bendingStatus: '양호'|'불량', lengthMeasured, widthMeasured, gapPoints: [{point, designValue, measured}] }] + * + * 프론트엔드 TemplateInspectionContent가 기대하는 EAV field_key 형식: + * b{productIdx}_ok / b{productIdx}_ng, b{productIdx}_n1, b{productIdx}_p{pointIdx}_n1 + */ + private function transformBendingProductsToRecords(array $item, int $templateId): array + { + $template = DocumentTemplate::with(['columns'])->find($templateId); + if (! $template) { + return []; + } + + // 컬럼 식별 (column_type + sort_order 기반) + $checkCol = $template->columns->firstWhere('column_type', 'check'); + $complexCols = $template->columns->where('column_type', 'complex')->sortBy('sort_order')->values(); + // complex 컬럼 순서: 길이(0), 너비(1), 간격(2) + $lengthCol = $complexCols->get(0); + $widthCol = $complexCols->get(1); + $gapCol = $complexCols->get(2); + + $records = []; + $products = $item['products']; + + foreach ($products as $productIdx => $product) { + // 절곡상태 → check column + if ($checkCol) { + if (($product['bendingStatus'] ?? null) === '양호') { + $records[] = [ + 'section_id' => null, 'column_id' => $checkCol->id, + 'row_index' => $productIdx, 'field_key' => "b{$productIdx}_ok", 'field_value' => 'OK', + ]; + } elseif (($product['bendingStatus'] ?? null) === '불량') { + $records[] = [ + 'section_id' => null, 'column_id' => $checkCol->id, + 'row_index' => $productIdx, 'field_key' => "b{$productIdx}_ng", 'field_value' => 'NG', + ]; + } + } + + // 길이 → first complex column + if ($lengthCol && ! empty($product['lengthMeasured'])) { + $records[] = [ + 'section_id' => null, 'column_id' => $lengthCol->id, + 'row_index' => $productIdx, 'field_key' => "b{$productIdx}_n1", 'field_value' => (string) $product['lengthMeasured'], + ]; + } + + // 너비 → second complex column + if ($widthCol && ! empty($product['widthMeasured'])) { + $records[] = [ + 'section_id' => null, 'column_id' => $widthCol->id, + 'row_index' => $productIdx, 'field_key' => "b{$productIdx}_n1", 'field_value' => (string) $product['widthMeasured'], + ]; + } + + // 간격 포인트 → third complex column (gap) + if ($gapCol && ! empty($product['gapPoints'])) { + foreach ($product['gapPoints'] as $pointIdx => $gp) { + if (! empty($gp['measured'])) { + $records[] = [ + 'section_id' => null, 'column_id' => $gapCol->id, + 'row_index' => $productIdx, 'field_key' => "b{$productIdx}_p{$pointIdx}_n1", 'field_value' => (string) $gp['measured'], + ]; + } + } + } + } + + // 전체 판정 + if (isset($item['judgment'])) { + $records[] = [ + 'section_id' => null, 'column_id' => null, + 'row_index' => 0, 'field_key' => 'overall_result', 'field_value' => (string) $item['judgment'], + ]; + } + + // 부적합 내용 + if (! empty($item['nonConformingContent'])) { + $records[] = [ + 'section_id' => null, 'column_id' => null, + 'row_index' => 0, 'field_key' => 'remark', 'field_value' => (string) $item['nonConformingContent'], + ]; + } + + return $records; + } + /** * 레거시 형식(section_X_item_Y 키)을 정규화 레코드로 변환 */