Files
sam-docs/sam/docs/features/documents/mng-document-system.md
김보곤 78cfc292a9 docs: [documents] MNG 문서관리 시스템 상세 기술문서 추가
- 탭별 기능 (문서목록, 서식관리, FQC현황)
- EAV 데이터 저장 패턴 상세 설명
- 서식 빌더 (Legacy/Block) 아키텍처
- 결재 워크플로우 및 자재 LOT 추적
- README에 관련 문서 링크 추가
2026-03-06 08:36:46 +09:00

739 lines
29 KiB
Markdown

# MNG 문서관리 시스템 상세 기술 명세
> **작성일**: 2026-03-06
> **상태**: 운영 중
> **프로젝트**: SAM MNG (관리자 웹)
> **관련**: [README.md](README.md) (API 명세)
---
## 1. 개요
### 1.1 목적
블라인드/스크린 제조 현장의 **검사 성적서, 작업일지, 수입검사 기록** 등 품질/생산 문서를 전자화하여 관리하는 시스템. 문서 양식(Template)을 정의하면 EAV 패턴으로 데이터를 동적 저장하며, 다단계 결재 워크플로우를 지원한다.
### 1.2 핵심 특징
| 특징 | 설명 |
|------|------|
| **EAV 패턴** | 양식별로 다른 필드를 하나의 `document_data` 테이블에 저장 |
| **2가지 양식 빌더** | 레거시 빌더 (DB 정규화) + 블록 빌더 (A4 JSON 스키마) |
| **결재 워크플로우** | 작성 → 검토 → 승인 (다단계 순차 결재) |
| **자동 데이터 매핑** | 작업지시서/수주 데이터에서 기본필드 자동 채움 |
| **다형성 연결** | work_order, sales_order 등 다양한 모델과 연결 |
| **자재 LOT 추적** | 검사 문서에서 투입 자재의 LOT 이력 조회 |
### 1.3 문서 구조
| 문서 | 설명 |
|------|------|
| [README.md](README.md) | API 엔드포인트, 모델 요약, FormRequest |
| **이 문서** | MNG 화면별 상세, 동작원리, 데이터 흐름 |
---
## 2. 메뉴/탭 구조
```
생산 관리
└── 문서관리
├── 문서 목록 /documents ← 문서 검색/필터/관리
├── 새 문서 작성 /documents/create ← 템플릿 선택 → 폼 입력
├── 문서 상세 /documents/{id} ← 읽기 전용 + 결재 현황
├── 문서 수정 /documents/{id}/edit ← DRAFT/REJECTED만
├── 인쇄 /documents/{id}/print ← 성적서 인쇄용
└── 문서양식 관리
├── 양식 목록 /document-templates ← 양식 검색/관리
├── 새 양식 (레거시) /document-templates/create ← 레거시 빌더
├── 양식 수정 /document-templates/{id}/edit ← 자동 빌더 판별
├── 양식 디자이너 /document-templates/block-create ← 블록 빌더
└── 블록 수정 /document-templates/{id}/block-edit ← 블록 빌더 수정
```
---
## 3. 파일 구조
```
mng/
├── app/Http/Controllers/
│ ├── DocumentController.php ← 문서 CRUD 화면
│ └── DocumentTemplateController.php ← 양식 관리 화면
├── app/Models/Documents/
│ ├── Document.php ← 문서 모델
│ ├── DocumentApproval.php ← 결재 단계
│ ├── DocumentData.php ← EAV 데이터
│ ├── DocumentTemplate.php ← 양식 마스터
│ └── ... (기타 템플릿 관련 모델)
└── resources/views/
├── documents/
│ ├── index.blade.php ← 문서 목록
│ ├── edit.blade.php ← 문서 작성/수정
│ ├── show.blade.php ← 문서 상세
│ └── print.blade.php ← 인쇄 전용
└── document-templates/
├── index.blade.php ← 양식 목록
├── edit.blade.php ← 레거시 빌더
├── block-editor.blade.php ← 블록 빌더
└── partials/
├── block-palette.blade.php ← 블록 타입 목록
├── block-canvas.blade.php ← 편집 캔버스
└── block-properties.blade.php ← 속성 패널
```
---
## 4. 데이터베이스 아키텍처
### 4.1 테이블 관계도
```
document_templates (양식 마스터)
├── 1:N → document_template_approval_lines (결재선 정의)
├── 1:N → document_template_basic_fields (기본필드 정의)
├── 1:N → document_template_sections (섹션 정의)
│ └── 1:N → document_template_section_items (검사항목)
├── 1:N → document_template_columns (테이블 컬럼 정의)
├── 1:N → document_template_section_fields (섹션 필드)
├── 1:N → document_template_links (외부 연결 정의)
│ └── 1:N → document_template_link_values (템플릿 레벨 연결값)
└── 1:N → documents (문서 인스턴스)
├── 1:N → document_approvals (결재 진행)
├── 1:N → document_data (EAV 필드값)
├── 1:N → document_attachments (첨부파일)
└── 1:N → document_links (문서 레벨 연결)
```
### 4.2 documents (문서)
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `tenant_id` | BIGINT FK | 테넌트 격리 |
| `template_id` | BIGINT FK | 사용 양식 |
| `document_no` | VARCHAR UNIQUE | 문서번호 (자동 채번) |
| `title` | VARCHAR | 문서 제목 |
| `status` | VARCHAR(20) | 상태 (5가지) |
| `linkable_type` | VARCHAR NULL | 다형성 모델 타입 |
| `linkable_id` | BIGINT NULL | 다형성 모델 ID |
| `submitted_at` | TIMESTAMP NULL | 결재 요청 일시 |
| `completed_at` | TIMESTAMP NULL | 결재 완료 일시 |
| `created_by` | BIGINT FK | 작성자 |
| `deleted_at` | TIMESTAMP NULL | 소프트 삭제 |
**인덱스**: `(tenant_id, status)`, `document_no`, `(linkable_type, linkable_id)`
### 4.3 document_data (EAV 필드값)
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `document_id` | BIGINT FK | 소속 문서 |
| `section_id` | BIGINT FK NULL | 소속 섹션 (NULL=기본필드) |
| `column_id` | BIGINT FK NULL | 소속 컬럼 (테이블 데이터용) |
| `row_index` | INT | 테이블 행 번호 (기본: 0) |
| `field_key` | VARCHAR | 필드 식별자 (`bf_1`, `cf_2`, `col_3`) |
| `field_value` | TEXT NULL | 실제 값 |
**인덱스**: `(document_id, section_id)`, `(document_id, field_key)`
### 4.4 document_approvals (결재)
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `document_id` | BIGINT FK | 소속 문서 |
| `user_id` | BIGINT FK | 결재자 |
| `step` | INT | 결재 순서 (1, 2, 3...) |
| `role` | VARCHAR | 역할 (작성, 검토, 승인) |
| `status` | VARCHAR(20) | PENDING / APPROVED / REJECTED |
| `comment` | TEXT NULL | 결재 의견 |
| `acted_at` | TIMESTAMP NULL | 처리 일시 |
**인덱스**: `(document_id, step)`, `(user_id, status)`
### 4.5 document_attachments (첨부파일)
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `document_id` | BIGINT FK | 소속 문서 |
| `file_id` | BIGINT FK | File 모델 연결 |
| `attachment_type` | VARCHAR | `general`, `signature`, `image`, `reference` |
| `description` | VARCHAR NULL | 설명 |
| `created_by` | BIGINT FK | 업로드자 |
---
## 5. 양식(Template) 시스템
### 5.1 두 가지 빌더 방식
| 방식 | 필드명 | 저장 구조 | UI | 상태 |
|------|--------|----------|-----|------|
| **레거시 빌더** | `builder_type = null` | 정규화 테이블들 | `edit.blade.php` | 기존 양식용 |
| **블록 빌더** | `builder_type = 'block'` | `schema` JSON | `block-editor.blade.php` | 신규 양식용 |
**자동 판별 로직:**
```php
// DocumentTemplateController::edit()
if ($template->isBlockBuilder()) {
return $this->blockEdit($id); // block-editor.blade.php
} else {
return view('document-templates.edit'); // 레거시
}
```
### 5.2 양식 마스터 (document_templates)
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `name` | VARCHAR | 양식명 (예: "제품검사 성적서") |
| `category` | VARCHAR | 분류 (common_codes 기반) |
| `title` | VARCHAR NULL | 문서 제목 템플릿 |
| `company_name` | VARCHAR NULL | 회사명 |
| `company_address` | VARCHAR NULL | 회사 주소 |
| `company_contact` | VARCHAR NULL | 연락처 |
| `footer_remark_label` | VARCHAR NULL | 비고란 라벨 |
| `footer_judgement_label` | VARCHAR NULL | 판정란 라벨 |
| `footer_judgement_options` | JSON NULL | 판정 선택지 (적합/부적합) |
| `builder_type` | VARCHAR NULL | `block` 또는 NULL |
| `schema` | JSON NULL | 블록 빌더 JSON 스키마 |
| `page_config` | JSON NULL | 페이지 설정 (A4, 여백 등) |
| `is_active` | BOOLEAN | 활성 여부 |
### 5.3 레거시 빌더 구성 요소
#### 결재선 (document_template_approval_lines)
```
step 1: 작성 (작성자 본인)
step 2: 검토 (팀장)
step 3: 승인 (부장)
```
| 컬럼 | 설명 |
|------|------|
| `name` | 라벨 (작성, 검토, 승인) |
| `dept` | 부서 |
| `role` | 역할 |
| `sort_order` | 순서 |
#### 기본필드 (document_template_basic_fields)
문서 상단의 고정 필드 영역.
| 컬럼 | 설명 |
|------|------|
| `label` | 필드 라벨 (품명, LOT NO, 납기일 등) |
| `field_key` | 식별자 (EAV 저장 시 사용) |
| `field_type` | 입력 타입 (text, date, number, item_search) |
| `default_value` | 기본값 |
| `sort_order` | 순서 |
**EAV 저장 시 field_key 패턴:**
```
bf_1 → 기본필드 ID 1 (예: 품명)
bf_2 → 기본필드 ID 2 (예: LOT NO)
bf_3 → 기본필드 ID 3 (예: 납기일)
```
#### 섹션 (document_template_sections)
검사 기준서의 섹션 단위.
| 컬럼 | 설명 |
|------|------|
| `title` | 섹션 제목 (예: "겉모양 검사", "치수 검사") |
| `image_path` | 도해 이미지 경로 (검사 부위 도면) |
| `sort_order` | 순서 |
#### 검사항목 (document_template_section_items)
각 섹션 내의 개별 검사항목.
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `category` | VARCHAR | 구분 (겉모양, 치수, 재질) |
| `item` | VARCHAR | 검사항목명 |
| `standard` | VARCHAR | 검사기준 (100mm ±5mm) |
| `tolerance` | JSON NULL | 허용오차 (min/max) |
| `standard_criteria` | VARCHAR NULL | 판정기준 |
| `method` | VARCHAR | 검사방법 (육안, 측정) |
| `measurement_type` | VARCHAR NULL | 측정 유형 |
| `frequency_n` | INT NULL | 검사건수 N |
| `frequency_c` | INT NULL | 합격건수 C |
| `frequency` | VARCHAR NULL | 검사빈도 텍스트 |
| `field_values` | JSON NULL | 확장 필드 (마이그레이션 없이 추가) |
#### 테이블 컬럼 (document_template_columns)
검사 데이터 테이블의 컬럼 정의.
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `label` | VARCHAR | 컬럼 라벨 |
| `width` | INT NULL | 너비 (px) |
| `column_type` | VARCHAR | `text`, `check`, `complex`, `measurement`, `select` |
| `group_name` | VARCHAR NULL | 상단 병합 헤더명 |
| `sub_labels` | JSON NULL | complex 타입 하위 라벨 |
| `sort_order` | INT | 순서 |
**컬럼 타입 상세:**
| 타입 | 설명 | 예시 |
|------|------|------|
| `text` | 단순 텍스트 입력 | 비고, 메모 |
| `check` | 체크박스 (합격/부적합) | 외관 검사 합격 여부 |
| `complex` | 여러 서브필드 조합 | 측정값 + 단위 + 판정 |
| `measurement` | 수치 입력 | 길이: 100.5mm |
| `select` | 드롭다운 선택 | 판정: 합격/불합격/보류 |
#### 외부 연결 (document_template_links)
템플릿에서 외부 테이블 데이터를 참조하기 위한 정의.
| 컬럼 | 설명 |
|------|------|
| `link_key` | 연결 식별자 |
| `label` | 화면 라벨 |
| `link_type` | `single` (1개 선택) / `multiple` (다중 선택) |
| `source_table` | 소스 테이블 (`items`, `processes`, `users`) |
| `search_params` | API 검색 추가 조건 (JSON) |
| `display_fields` | 표시 필드 (title, subtitle) |
| `is_required` | 필수 여부 |
### 5.4 블록 빌더 구조
**페이지 설정 (page_config):**
```json
{
"size": "A4",
"orientation": "portrait",
"margin": {
"top": 20,
"right": 15,
"bottom": 20,
"left": 15
}
}
```
**스키마 (schema):**
블록 배열로 레이아웃 정의. 드래그앤드롭으로 편집.
```json
{
"blocks": [
{ "type": "text", "x": 0, "y": 0, "width": 100, "content": "검사 성적서" },
{ "type": "table", "x": 0, "y": 50, "columns": [...], "rows": [...] },
{ "type": "image", "x": 200, "y": 100, "src": "..." }
]
}
```
**블록 빌더 UI (3패널):**
```
┌──────────┬────────────────────┬──────────┐
│ 블록 │ │ 속성 │
│ 팔레트 │ A4 캔버스 │ 패널 │
│ │ │ │
│ [텍스트] │ ┌──────────────┐ │ 너비: _ │
│ [이미지] │ │ 드래그앤드롭 │ │ 높이: _ │
│ [표] │ │ 블록 배치 │ │ 색상: _ │
│ [선] │ │ │ │ 폰트: _ │
│ [도형] │ └──────────────┘ │ │
└──────────┴────────────────────┴──────────┘
```
---
## 6. EAV 데이터 저장 패턴
### 6.1 핵심 개념
하나의 `document_data` 테이블에 **모든 양식의 모든 필드값**을 저장. 양식이 다르면 field_key가 다르고, 같은 양식이라도 섹션/행이 다르면 section_id/row_index로 구분.
### 6.2 저장 구조
```
document_data 레코드 예시:
기본필드 (상단 고정 영역):
┌─────────────┬────────────┬───────────┬───────────┬───────────┬─────────────┐
│ document_id │ section_id │ column_id │ row_index │ field_key │ field_value │
├─────────────┼────────────┼───────────┼───────────┼───────────┼─────────────┤
│ 42 │ NULL │ NULL │ 0 │ bf_1 │ 블라인드A │ ← 품명
│ 42 │ NULL │ NULL │ 0 │ bf_2 │ LOT-2026-001│ ← LOT NO
│ 42 │ NULL │ NULL │ 0 │ bf_3 │ 2026-03-15 │ ← 납기일
├─────────────┼────────────┼───────────┼───────────┼───────────┼─────────────┤
테이블 데이터 (섹션별 검사 결과):
│ 42 │ 10 │ 20 │ 0 │ col_20 │ 합격 │ ← 섹션10, 컬럼20, 1행
│ 42 │ 10 │ 20 │ 1 │ col_20 │ 부적합 │ ← 섹션10, 컬럼20, 2행
│ 42 │ 10 │ 21 │ 0 │ col_21 │ 100.5 │ ← 섹션10, 컬럼21, 1행
└─────────────┴────────────┴───────────┴───────────┴───────────┴─────────────┘
```
### 6.3 field_key 네이밍 규칙
| 접두사 | 의미 | 예시 |
|--------|------|------|
| `bf_` | 기본필드 (BasicField) | `bf_1`, `bf_2` |
| `cf_` | 섹션필드 (SectionField) | `cf_5`, `cf_6` |
| `col_` | 컬럼 데이터 | `col_20`, `col_21` |
### 6.4 데이터 조회 패턴
```php
// 기본필드 값 조회
$data = DocumentData::where('document_id', $id)
->whereNull('section_id')
->get()
->keyBy('field_key');
$productName = $data['bf_1']->field_value;
// 섹션별 테이블 데이터 조회
$rows = DocumentData::where('document_id', $id)
->where('section_id', $sectionId)
->get()
->groupBy('row_index');
```
---
## 7. 결재 워크플로우
### 7.1 상태 전이
```
DRAFT (작성중)
├── submit() → PENDING (결재중)
│ │
│ ├── approve() [step 1] → 다음 step 대기
│ ├── approve() [step 2] → 다음 step 대기
│ ├── approve() [마지막] → APPROVED (승인)
│ │
│ └── reject() → REJECTED (반려)
│ │
│ └── edit → submit() → PENDING (재요청)
└── cancel() → CANCELLED (취소)
```
### 7.2 상태값 및 라벨
| 코드 | 라벨 | 색상 | 편집 가능 |
|------|------|------|----------|
| `DRAFT` | 작성중 | gray | 예 |
| `PENDING` | 결재중 | yellow | 아니오 |
| `APPROVED` | 승인 | green | 아니오 |
| `REJECTED` | 반려 | red | 예 (수정 후 재요청) |
| `CANCELLED` | 취소 | gray | 아니오 |
### 7.3 결재 단계 (Approval)
```
DocumentTemplateApprovalLine (양식 정의)
↓ (문서 생성 시 복사)
DocumentApproval (문서별 결재 레코드)
step 1: 작성 → PENDING → 결재자 승인 → APPROVED
step 2: 검토 → PENDING → 결재자 승인 → APPROVED
step 3: 승인 → PENDING → 결재자 승인 → APPROVED → 문서 전체 APPROVED
```
### 7.4 결재 판단 메서드
```php
// Document 모델
canEdit() // DRAFT 또는 REJECTED
canSubmit() // DRAFT 또는 REJECTED
canApprove() // PENDING (현재 결재자만)
canCancel() // DRAFT 또는 PENDING (작성자만)
```
---
## 8. 자동 데이터 매핑
### 8.1 개요
문서 작성/수정 시, 연결된 작업지시서(work_order)/수주(order) 데이터에서 기본필드를 **자동으로 채움**. 사용자 입력 부담을 줄이고 데이터 정확성을 보장.
### 8.2 검사 성적서 매핑 (field_key 기반)
| field_key | 라벨 | 소스 |
|-----------|------|------|
| `product_name` | 품명 | `workOrderItem.item_name` |
| `specification` | 규격 | `workOrderItem.specification` |
| `lot_no` | LOT NO | `order.order_no` |
| `lot_size` | LOT 크기 | `"N 개소"` (개소 수 기반) |
| `client` | 발주처 | `order.client_name` |
| `site_name` | 현장명 | `workOrder.project_name` |
| `inspection_date` | 검사일 | `workOrderItem.options.inspection_data.inspected_at` |
| `inspector` | 검사자 | 검사자 이름 |
### 8.3 작업일지 매핑 (label 기반)
| label 포함 문자열 | 소스 |
|------------------|------|
| `발주처` | `order.client_name` |
| `현장명` | `workOrder.project_name` |
| `작업일자` | `now()` |
| `LOT NO`, `LOT` | `order.order_no` |
| `납기일`, `납기` | `order.delivery_date` |
| `작업지시번호` | `workOrder.work_order_no` |
| `수주일` | `order.received_at` 또는 `order.created_at` |
### 8.4 자동 매핑 흐름
```
문서 작성/수정 페이지 로드
DocumentController::edit()
resolveAndBackfillBasicFields($template, $document)
linkable_type 확인 (work_order? order?)
field_key 또는 label 매칭
DB에 값이 없으면 → 소스 데이터에서 resolve
뷰에 자동 채움된 값 전달
```
---
## 9. 자재 LOT 추적
### 9.1 개요
검사 성적서에서 해당 작업지시의 **투입 자재 LOT 이력**을 조회. `stock_transactions` 테이블의 OUT(투입)/IN(취소) 트랜잭션을 상쇄하여 순수 투입량을 계산.
### 9.2 추적 구조
```
work_orders (작업지시)
├── stock_transactions (재고 트랜잭션)
│ ├── OUT (투입): qty < 0
│ └── IN (취소/반납): qty > 0
│ → 순수 투입량 = ABS(SUM(qty)) where qty < 0
└── work_order_material_inputs (개소별 투입자재)
└── stock_lots (LOT 정보) JOIN
```
### 9.3 표시 내용
| 항목 | 설명 |
|------|------|
| 자재명 | 투입된 원자재/부자재 이름 |
| LOT 번호 | 자재의 LOT 식별 번호 |
| 투입 수량 | OUT 트랜잭션 합계 (절대값) |
| 투입일 | 트랜잭션 일시 |
---
## 10. 화면별 상세
### 10.1 문서 목록 (/documents)
**필터 항목:**
| 필터 | 타입 | 설명 |
|------|------|------|
| 검색 | text | 문서번호 또는 제목 |
| 상태 | dropdown | DRAFT, PENDING, APPROVED, REJECTED, CANCELLED, 휴지통(admin) |
| 양식분류 | dropdown | category |
| 템플릿 | dropdown | template_id |
| 날짜 범위 | date | created_at (from ~ to) |
**목록 테이블 컬럼:**
```
문서번호 | 제목 | 양식 | 상태 | 작성자 | 작성일 | 결재현황
```
### 10.2 문서 작성/수정 (/documents/create, /documents/{id}/edit)
**폼 구성:**
```
┌──────────────────────────────────────────────┐
│ 템플릿 선택 (읽기전용) │
│ 제목 (필수) │
├──────────────────────────────────────────────┤
│ 기본 필드 (template.basicFields) │
│ ┌─────────────────┬─────────────────┐ │
│ │ 품명: [자동채움] │ LOT NO: [자동] │ │
│ │ 납기일: [날짜] │ 발주처: [자동] │ │
│ └─────────────────┴─────────────────┘ │
├──────────────────────────────────────────────┤
│ 섹션 1: 겉모양 검사 │
│ ┌──────────────────────────────────────┐ │
│ │ 도해 이미지 (있으면) │ │
│ ├──────┬──────┬──────┬──────┬──────┤ │
│ │ 구분 │ 항목 │ 기준 │ 결과1│ 결과2│ │
│ ├──────┼──────┼──────┼──────┼──────┤ │
│ │ 치수 │ 길이 │±5mm │ [ ] │ [ ] │ │
│ │ 외관 │ 흠집 │ 없음 │ [✓] │ [✓] │ │
│ ├──────┴──────┴──────┴──────┴──────┤ │
│ │ [+ 행 추가] [행 삭제] │ │
│ └──────────────────────────────────────┘ │
├──────────────────────────────────────────────┤
│ 외부 연결 (template.links) │
│ 품목 선택: [검색 드롭다운] │
├──────────────────────────────────────────────┤
│ 첨부파일 │
│ [일반 문서] [서명 이미지] [검사 사진] [참고 자료] │
├──────────────────────────────────────────────┤
│ [임시저장] [결재 요청] │
└──────────────────────────────────────────────┘
```
### 10.3 문서 상세 (/documents/{id})
**읽기 전용 표시:**
```
┌──────────────────────────────────────────────┐
│ 문서번호: DOC-260306-001 상태: [🟢 승인] │
│ 제목: 블라인드A 검사 성적서 │
├──────────────────────────────────────────────┤
│ 기본 필드 (읽기 전용) │
├──────────────────────────────────────────────┤
│ 검사 데이터 테이블 (읽기 전용) │
├──────────────────────────────────────────────┤
│ 결재 현황 │
│ ┌────────┬────────┬────────┐ │
│ │ 작성 │ 검토 │ 승인 │ │
│ │ 홍길동 │ 김과장 │ 박부장 │ │
│ │ ✓승인 │ ✓승인 │ ●대기 │ │
│ └────────┴────────┴────────┘ │
├──────────────────────────────────────────────┤
│ 자재 투입 LOT (작업지시 연결 시) │
│ ┌────────┬──────────┬──────┬──────┐ │
│ │ 자재명 │ LOT 번호 │ 수량 │ 투입일│ │
│ └────────┴──────────┴──────┴──────┘ │
├──────────────────────────────────────────────┤
│ 첨부파일 목록 │
├──────────────────────────────────────────────┤
│ [수정] [인쇄] [결재 승인] [결재 반려] │
└──────────────────────────────────────────────┘
```
### 10.4 인쇄 (/documents/{id}/print)
성적서 형식의 인쇄 전용 화면. `window.print()` 호출. 작업지시 관련 자재(work_order_items) 데이터 포함.
### 10.5 양식 목록 (/document-templates)
**필터:**
- 검색: 양식명, 제목, 분류
- 카테고리: common_codes 기반 + 기존 데이터 폴백
- 활성 상태: 활성 / 비활성 / 휴지통(admin)
**HTMX**: 필터 변경 시 테이블 영역만 부분 로드
---
## 11. 첨부파일 유형
| 유형 | 코드 | 용도 | 예시 |
|------|------|------|------|
| 일반 문서 | `general` | PDF, 엑셀 등 | 규격서, 보고서 |
| 서명 이미지 | `signature` | 검사 완료 서명 | 검사자 서명 사진 |
| 검사 사진 | `image` | 검사 증빙 사진 | 불량 부위 촬영 |
| 참고 자료 | `reference` | 참고용 문서 | KS 규격, 작업 지침 |
---
## 12. API 연동 (MNG → API)
MNG 뷰에서 데이터 저장/삭제는 **API 서버를 호출**하여 처리. GET 요청(뷰 렌더링)은 MNG 컨트롤러가 직접 처리.
| 작업 | MNG (GET 요청) | API (POST/PUT/DELETE) |
|------|---------------|----------------------|
| 목록 조회 | `DocumentController::index()` | `GET /v1/documents` |
| 상세 조회 | `DocumentController::show()` | `GET /v1/documents/{id}` |
| 생성 | 폼 표시만 | `POST /v1/documents` |
| 수정 | 폼 표시만 | `PATCH /v1/documents/{id}` |
| 삭제 | - | `DELETE /v1/documents/{id}` |
| 결재 요청 | - | `POST /v1/documents/{id}/submit` |
| 승인 | - | `POST /v1/documents/{id}/approve` |
| 반려 | - | `POST /v1/documents/{id}/reject` |
---
## 13. 카테고리 해결 로직
양식 카테고리는 **common_codes 테이블**에서 조회하되, 없으면 **기존 데이터에서 추출**하여 폴백.
```php
// DocumentTemplateController::getCategories()
$categories = CommonCode::where('group', 'document_category')
->orderBy('sort_order')
->get();
if ($categories->isEmpty()) {
// 폴백: 기존 템플릿의 category 값에서 중복 제거
$categories = DocumentTemplate::distinct('category')
->pluck('category')
->filter();
}
```
---
## 14. 검사항목 확장 (field_values JSON)
`document_template_section_items.field_values` JSON 컬럼으로 마이그레이션 없이 새 필드를 추가할 수 있다.
```json
{
"custom_field_1": "추가 기준값",
"min_value": 95.0,
"max_value": 105.0,
"unit": "mm"
}
```
> options JSON 컬럼 정책(`docs/standards/options-column-policy.md`) 준용
---
## 15. HTMX 전체 페이지 로드 규칙
문서관리 페이지들은 JavaScript를 사용하므로 HTMX 부분 로드 시 스크립트 미실행 문제가 있다. 컨트롤러에서 HX-Request 감지 시 **HX-Redirect로 전체 페이지 리로드 강제**.
```php
if ($request->header('HX-Request')) {
return response('', 200)->header('HX-Redirect', route('documents.index'));
}
```
---
## 관련 문서
- [README.md](README.md) — API 엔드포인트, 모델 요약, FormRequest
- [DB 스키마 — 문서/전자서명](../../system/database/documents.md) — 테이블 상세
- [게시판 시스템](../boards/README.md) — 유사한 EAV 패턴 참고
- [결재관리 시스템](../approvals/README.md) — 별도 결재 시스템 (문서관리와 독립)
---
**최종 업데이트**: 2026-03-06