fix: [production] 절곡 검사 데이터 전체 item 복제 + bending EAV 변환

- storeItemInspection: bending/bending_wip 시 동일 작업지시 모든 item에 복제 저장
- transformBendingProductsToRecords: products 배열 → bending EAV 레코드 변환
- getMaterialInputLots: 품목코드별 그룹핑으로 변경

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 23:28:03 +09:00
parent 5ee97c2d74
commit 897511cb55

View File

@@ -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 키)을 정규화 레코드로 변환
*/