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:
@@ -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 연동용)
|
||||
// =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user