fix:FQC 문서 기본필드 키 형식 수정 (bf_라벨 → bf_ID)

- 제품검사 문서 생성 시 bf_납품명 형식 → bf_{field->id} 형식으로 변경
- 템플릿 basicFields를 로드하여 field_key 기반 매핑
- mng show.blade.php와 키 형식 통일

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 00:32:41 +09:00
parent 9af48a15af
commit 441359f5fd

View File

@@ -445,6 +445,195 @@ public function cancel(int $id): Document
});
}
// =========================================================================
// FQC 일괄생성 (제품검사)
// =========================================================================
/**
* 수주 개소별 제품검사 문서 일괄생성
*
* Order의 OrderItem 수만큼 Document를 DRAFT 상태로 생성.
* 기본필드(납품명, 제품명, 발주처, LOT NO, 로트크기) 자동매핑.
*/
public function bulkCreateFqc(array $data): array
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
$templateId = $data['template_id'];
$orderId = $data['order_id'];
// 템플릿 존재 확인
$template = DocumentTemplate::where('tenant_id', $tenantId)
->where('is_active', true)
->findOrFail($templateId);
// 수주 + 개소 조회
$order = \App\Models\Orders\Order::where('tenant_id', $tenantId)
->with('items')
->findOrFail($orderId);
if ($order->items->isEmpty()) {
throw new BadRequestHttpException(__('error.document.no_order_items'));
}
// 이미 생성된 문서 확인 (중복 방지)
$existingLinkableIds = Document::where('tenant_id', $tenantId)
->where('template_id', $templateId)
->where('linkable_type', \App\Models\Orders\OrderItem::class)
->whereIn('linkable_id', $order->items->pluck('id'))
->pluck('linkable_id')
->toArray();
$itemsToCreate = $order->items->reject(function ($item) use ($existingLinkableIds) {
return in_array($item->id, $existingLinkableIds);
});
if ($itemsToCreate->isEmpty()) {
throw new BadRequestHttpException(__('error.document.already_created'));
}
// 템플릿의 기본필드 로드 (bf_{id} 형식으로 저장하기 위해)
$template = DocumentTemplate::with('basicFields')->find($templateId);
return DB::transaction(function () use ($itemsToCreate, $order, $templateId, $tenantId, $userId, $template) {
$documents = [];
foreach ($itemsToCreate as $orderItem) {
// 문서번호 생성
$documentNo = $this->generateDocumentNo($tenantId, $templateId);
// 개소 식별 문자열
$locationLabel = trim("{$orderItem->floor_code}-{$orderItem->symbol_code}");
$specLabel = $orderItem->specification ?? '';
$titleSuffix = $specLabel ? "{$locationLabel} ({$specLabel})" : $locationLabel;
// Document 생성
$document = Document::create([
'tenant_id' => $tenantId,
'template_id' => $templateId,
'document_no' => $documentNo,
'title' => "제품검사 - {$titleSuffix}",
'status' => Document::STATUS_DRAFT,
'linkable_type' => \App\Models\Orders\OrderItem::class,
'linkable_id' => $orderItem->id,
'created_by' => $userId,
'updated_by' => $userId,
]);
// 기본필드 자동매핑 (bf_{id} 형식, mng show.blade.php 호환)
$resolveMap = [
'product_name' => $orderItem->item_name,
'client' => $order->client_name,
'lot_no' => $order->order_no,
'lot_size' => '1 EA',
'site_name' => $order->site_name ?? '',
];
if ($template && $template->basicFields) {
foreach ($template->basicFields as $field) {
$value = $resolveMap[$field->field_key] ?? '';
// field_key가 없는 필드는 라벨 매칭
if (! $value && ! $field->field_key) {
if (str_contains($field->label, '납품')) {
$value = $order->site_name ?? $order->order_no;
}
}
if ($value) {
DocumentData::create([
'document_id' => $document->id,
'section_id' => null,
'column_id' => null,
'row_index' => 0,
'field_key' => "bf_{$field->id}",
'field_value' => (string) $value,
]);
}
}
}
$documents[] = $document;
}
return [
'created_count' => count($documents),
'skipped_count' => count($existingLinkableIds ?? []),
'total_items' => count($documents) + count($existingLinkableIds ?? []),
'documents' => collect($documents)->map(fn ($doc) => [
'id' => $doc->id,
'document_no' => $doc->document_no,
'title' => $doc->title,
'status' => $doc->status,
'linkable_id' => $doc->linkable_id,
])->toArray(),
];
});
}
/**
* 수주 개소별 FQC 진행현황 조회
*/
public function fqcStatus(int $orderId, int $templateId): array
{
$tenantId = $this->tenantId();
$order = \App\Models\Orders\Order::where('tenant_id', $tenantId)
->with('items')
->findOrFail($orderId);
// 해당 수주의 FQC 문서 조회
$documents = Document::where('tenant_id', $tenantId)
->where('template_id', $templateId)
->where('linkable_type', \App\Models\Orders\OrderItem::class)
->whereIn('linkable_id', $order->items->pluck('id'))
->with('data')
->get()
->keyBy('linkable_id');
$items = $order->items->map(function ($orderItem) use ($documents) {
$doc = $documents->get($orderItem->id);
// 종합판정 값 추출
$judgement = null;
if ($doc) {
$judgementData = $doc->data->firstWhere('field_key', 'footer_judgement');
$judgement = $judgementData?->field_value;
}
return [
'order_item_id' => $orderItem->id,
'floor_code' => $orderItem->floor_code,
'symbol_code' => $orderItem->symbol_code,
'specification' => $orderItem->specification,
'item_name' => $orderItem->item_name,
'document_id' => $doc?->id,
'document_no' => $doc?->document_no,
'status' => $doc?->status ?? 'NONE',
'judgement' => $judgement,
];
});
// 통계
$total = $items->count();
$created = $items->where('status', '!=', 'NONE')->count();
$approved = $items->where('status', 'APPROVED')->count();
$passed = $items->where('judgement', '합격')->count();
$failed = $items->where('judgement', '불합격')->count();
return [
'order_id' => $orderId,
'order_no' => $order->order_no,
'total' => $total,
'created' => $created,
'approved' => $approved,
'passed' => $passed,
'failed' => $failed,
'pending' => $total - $created,
'items' => $items->toArray(),
];
}
// =========================================================================
// Resolve/Upsert (React 연동용)
// =========================================================================