From 16fb78fe5e999cdaaf886529d06cfc9f575b6806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Sat, 7 Feb 2026 03:27:32 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=AC=B8=EC=84=9C=20=EC=96=91=EC=8B=9D?= =?UTF-8?q?=20=EA=B4=80=EB=A6=AC=20=EB=B0=8F=20=EC=88=98=EC=9E=85=EA=B2=80?= =?UTF-8?q?=EC=82=AC=20=EC=96=91=EC=8B=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 문서 양식 API 컨트롤러 및 뷰 개선 - 수입검사 양식 시더 업데이트 - 문서 미리보기 뷰 개선 Co-Authored-By: Claude Opus 4.6 --- .../Admin/DocumentTemplateApiController.php | 1 + .../DocumentTemplateController.php | 44 +++++++++++- .../IncomingInspectionTemplateSeeder.php | 5 +- .../views/document-templates/edit.blade.php | 42 +++++++++-- .../views/document-templates/index.blade.php | 15 ++++ .../partials/table.blade.php | 18 ++--- resources/views/documents/show.blade.php | 71 +++++++++++++------ 7 files changed, 155 insertions(+), 41 deletions(-) diff --git a/app/Http/Controllers/Api/Admin/DocumentTemplateApiController.php b/app/Http/Controllers/Api/Admin/DocumentTemplateApiController.php index c686b4c3..b543af2a 100644 --- a/app/Http/Controllers/Api/Admin/DocumentTemplateApiController.php +++ b/app/Http/Controllers/Api/Admin/DocumentTemplateApiController.php @@ -625,6 +625,7 @@ private function saveRelations(DocumentTemplate $template, array $data, bool $de 'frequency_c' => $item['frequency_c'] ?? null, 'frequency' => $item['frequency'] ?? '', 'regulation' => $item['regulation'] ?? '', + 'field_values' => $item['field_values'] ?? null, 'sort_order' => $iIndex, ]); } diff --git a/app/Http/Controllers/DocumentTemplateController.php b/app/Http/Controllers/DocumentTemplateController.php index 4c755f0c..c6fe3a83 100644 --- a/app/Http/Controllers/DocumentTemplateController.php +++ b/app/Http/Controllers/DocumentTemplateController.php @@ -213,7 +213,18 @@ private function prepareTemplateData(DocumentTemplate $template): array 'is_required' => $f->is_required, ]; })->toArray(), - 'template_links' => $template->links->map(function ($l) { + 'template_links' => $this->buildTemplateLinks($template), + ]; + } + + /** + * template_links 데이터 빌드 (linked_item_ids 레거시 호환) + */ + private function buildTemplateLinks(DocumentTemplate $template): array + { + // 신규 template_links가 있으면 그대로 사용 + if ($template->links->isNotEmpty()) { + return $template->links->map(function ($l) { $values = $l->linkValues->map(function ($v) use ($l) { $displayText = $this->resolveDisplayText($l->source_table, $v->linkable_id, $l->display_fields); @@ -235,8 +246,35 @@ private function prepareTemplateData(DocumentTemplate $template): array 'is_required' => $l->is_required, 'values' => $values, ]; - })->toArray(), - ]; + })->toArray(); + } + + // 레거시: linked_item_ids만 있고 template_links가 없는 경우 가상 엔트리 생성 + $linkedItemIds = $template->linked_item_ids; + if (! empty($linkedItemIds) && is_array($linkedItemIds)) { + $displayFields = ['title' => 'name', 'subtitle' => 'code']; + $values = collect($linkedItemIds)->map(function ($itemId) use ($displayFields) { + return [ + 'id' => $itemId, + 'linkable_id' => $itemId, + 'display_text' => $this->resolveDisplayText('items', $itemId, $displayFields), + ]; + })->toArray(); + + return [[ + 'id' => 'legacy', + 'link_key' => 'items', + 'label' => '연결 품목', + 'link_type' => 'multiple', + 'source_table' => 'items', + 'search_params' => null, + 'display_fields' => $displayFields, + 'is_required' => false, + 'values' => $values, + ]]; + } + + return []; } /** diff --git a/database/seeders/IncomingInspectionTemplateSeeder.php b/database/seeders/IncomingInspectionTemplateSeeder.php index e44ab4fc..6c3dfe5e 100644 --- a/database/seeders/IncomingInspectionTemplateSeeder.php +++ b/database/seeders/IncomingInspectionTemplateSeeder.php @@ -12,7 +12,7 @@ class IncomingInspectionTemplateSeeder extends Seeder { - private int $tenantId = 1; + private int $tenantId = 287; public function run(): void { @@ -97,6 +97,7 @@ private function getTemplateDefinitions(): array 'method' => '공급업체 밀시트', 'frequency' => '입고시', 'regulation' => 'KS D 3528', + 'field_values' => ['reference_attribute' => 'thickness'], ], [ 'category' => '도금', @@ -296,4 +297,4 @@ private function cleanupExisting(string $name): void DocumentTemplateApprovalLine::where('template_id', $existing->id)->delete(); $existing->forceDelete(); } -} \ No newline at end of file +} diff --git a/resources/views/document-templates/edit.blade.php b/resources/views/document-templates/edit.blade.php index e3702c18..26a2359a 100644 --- a/resources/views/document-templates/edit.blade.php +++ b/resources/views/document-templates/edit.blade.php @@ -1136,6 +1136,15 @@ function removeSubLabel(colId, idx) { } } + // template_links의 items 값을 linked_item_ids로 동기화 + function syncLinkedItemIds() { + const link = getMainLink(); + if (link && link.source_table === 'items' && link.values && link.values.length > 0) { + return link.values.map(v => v.linkable_id || v.id).filter(Boolean); + } + return templateState.linked_item_ids; + } + // ===== 저장 ===== function saveTemplate() { const name = document.getElementById('name').value.trim(); @@ -1156,7 +1165,7 @@ function saveTemplate() { footer_judgement_label: document.getElementById('footer_judgement_label').value, footer_judgement_options: templateState.footer_judgement_options.filter(o => o.trim() !== ''), is_active: document.getElementById('is_active').checked, - linked_item_ids: templateState.linked_item_ids, + linked_item_ids: syncLinkedItemIds(), linked_process_id: templateState.linked_process_id, approval_lines: templateState.approval_lines, basic_fields: templateState.basic_fields, @@ -1850,7 +1859,7 @@ function updateDynamicField(sectionId, itemId, fieldKey, value) { // 기존 컬럼에도 업데이트 (하위 호환) item[fieldKey] = value; // field_values에도 업데이트 - if (!item.field_values) item.field_values = {}; + if (!item.field_values || Array.isArray(item.field_values)) item.field_values = {}; item.field_values[fieldKey] = value; } @@ -1862,6 +1871,16 @@ function calcTableWidth() { return Math.max(width, 600); } + // 참조속성 업데이트 + function updateReferenceAttribute(sectionId, itemId, value) { + const section = templateState.sections.find(s => s.id == sectionId); + if (!section) return; + const item = section.items.find(i => i.id == itemId); + if (!item) return; + if (!item.field_values || Array.isArray(item.field_values)) item.field_values = {}; + item.field_values.reference_attribute = value || null; + } + function renderDynamicFieldInput(field, sectionId, item) { const val = getItemFieldValue(item, field.field_key); const fk = field.field_key; @@ -1907,12 +1926,23 @@ class="w-full px-2 py-1 border border-gray-200 rounded text-xs">`; return renderToleranceInput(sid, iid, val || item.tolerance); case 'text_with_criteria': { - // 상단: 검사기준 텍스트, 하단: 기준범위 min/max + // 상단: 참조속성 + 검사기준 텍스트, 하단: 기준범위 min/max const c = getItemFieldValue(item, 'standard_criteria') || item.standard_criteria; + const refAttr = (item.field_values && item.field_values.reference_attribute) || ''; return `
- +
+ + +