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

29 KiB

MNG 문서관리 시스템 상세 기술 명세

작성일: 2026-03-06 상태: 운영 중 프로젝트: SAM MNG (관리자 웹) 관련: 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 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 신규 양식용

자동 판별 로직:

// 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 드롭다운 선택 판정: 합격/불합격/보류

템플릿에서 외부 테이블 데이터를 참조하기 위한 정의.

컬럼 설명
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):

{
    "size": "A4",
    "orientation": "portrait",
    "margin": {
        "top": 20,
        "right": 15,
        "bottom": 20,
        "left": 15
    }
}

스키마 (schema):

블록 배열로 레이아웃 정의. 드래그앤드롭으로 편집.

{
    "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 데이터 조회 패턴

// 기본필드 값 조회
$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 결재 판단 메서드

// 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 테이블에서 조회하되, 없으면 기존 데이터에서 추출하여 폴백.

// 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 컬럼으로 마이그레이션 없이 새 필드를 추가할 수 있다.

{
    "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로 전체 페이지 리로드 강제.

if ($request->header('HX-Request')) {
    return response('', 200)->header('HX-Redirect', route('documents.index'));
}

관련 문서


최종 업데이트: 2026-03-06