From 314fd4eb6fa0d97cbc62eda331e26871965838a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Sat, 31 Jan 2026 04:32:41 +0900 Subject: [PATCH] =?UTF-8?q?docs:=EB=AC=B8=EC=84=9C=EA=B4=80=EB=A6=AC=20?= =?UTF-8?q?=EA=B3=84=ED=9A=8D=20Phase=202.1=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=20(=EC=A7=84=ED=96=89=EB=A5=A0=2030%)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Phase 2.1 완료 표시 (문서번호 prefix, 결재라인 초기화, 기본필드 뷰 수정) - changelog 파일에 Phase 1.5, 2.1 이력 추가 - 진행률: 5/20 → 6/20 (30%) Co-Authored-By: Claude Opus 4.5 --- plans/document-management-system-changelog.md | 18 + plans/document-management-system-plan.md | 1471 +++++++---------- 2 files changed, 644 insertions(+), 845 deletions(-) create mode 100644 plans/document-management-system-changelog.md diff --git a/plans/document-management-system-changelog.md b/plans/document-management-system-changelog.md new file mode 100644 index 0000000..7730821 --- /dev/null +++ b/plans/document-management-system-changelog.md @@ -0,0 +1,18 @@ +# 문서관리 시스템 - 변경 이력 + +> **본 문서**: `docs/plans/document-management-system-plan.md`의 변경 이력 +> **최종 업데이트**: 2026-01-31 + +--- + +## 변경 이력 + +| 날짜 | 항목 | 변경 내용 | 관련 섹션 | 승인 | +|------|------|----------|----------|------| +| 2026-01-31 | 초안 | 기존 시스템 분석 기반 계획 문서 전면 재작성 | 본 문서 | - | +| 2026-01-31 | Phase 1.1 완료 | 양식 편집 UI 5개 탭 전체 CRUD 확인 (사실상 완료) | 섹션 3.1, 11.1 | - | +| 2026-01-31 | Phase 1.2 완료 | viewJS.php 라우팅 분석 + EGI/SUS 대표 2종 상세 분석 + 공통패턴 추출 | 섹션 3.1, 11.2 | - | +| 2026-01-31 | Phase 1.3 완료 | IncomingInspectionTemplateSeeder 생성. EGI(ID:7), SUS(ID:8) 2종 시드 완료. 결재2+기본필드10+섹션+항목+컬럼 전체 | 섹션 3.1 | - | +| 2026-01-31 | Phase 1.4 완료 | 미리보기 기능 기존 구현 확인. 모달로 결재란+기본정보+검사이미지+검사테이블(complex)+Footer 모두 렌더링 | 섹션 3.1 | - | +| 2026-01-31 | Phase 1.5 완료 | 양식 복제 기능. duplicate() 메서드 + 라우트 + 테이블 버튼 + JS 함수 추가 | 섹션 3.1 | - | +| 2026-01-31 | Phase 2.1 완료 | 문서 생성 기능 보완. ①문서번호 카테고리별 prefix(IQC/PRD/SLS/PUR, YYMMDD-순번) ②결재라인 초기화(template.approvalLines→document_approvals) ③기본필드 뷰 속성 불일치 수정(field_type/label/default_value 매핑, Str::slug로 field_key 생성) ④섹션 title 참조 수정 | 섹션 3.2 | - | \ No newline at end of file diff --git a/plans/document-management-system-plan.md b/plans/document-management-system-plan.md index 6c5512e..a69c95a 100644 --- a/plans/document-management-system-plan.md +++ b/plans/document-management-system-plan.md @@ -1,8 +1,103 @@ -# 문서 관리 시스템 개발 계획 +# 문서관리 시스템 개발 계획 -> **작성일**: 2025-01-28 -> **목적**: 문서 템플릿 기반 실제 문서 작성/결재/관리 시스템 -> **상태**: 📋 계획 수립 +> **작성일**: 2026-01-31 +> **목적**: mng에서 문서양식(템플릿)을 관리하고 문서를 생성하여, SAM(react)에서 JSON으로 소비하는 문서관리 시스템을 구축한다 +> **기준 문서**: `docs/specs/database-schema.md`, `mng/CLAUDE.md` +> **상태**: 📋 계획 확정 → Phase 1 시작 대기 + +--- + +## 🚀 새 세션 시작 가이드 + +> **이 섹션은 새 세션에서 이 문서만 보고 작업을 시작할 수 있도록 작성되었습니다.** + +### 프로젝트 정보 + +| 항목 | 내용 | +|------|------| +| **작업 프로젝트** | `mng` (관리자 패널) | +| **절대 경로** | `/Users/kent/Works/@KD_SAM/SAM/mng/` | +| **기술 스택** | Laravel 12 + Plain Blade + DaisyUI + HTMX + Alpine.js | +| **로컬 URL** | `https://mng.sam.kr` (Docker 로컬, `admin.sam.kr`도 동일) | +| **관련 API** | `/Users/kent/Works/@KD_SAM/SAM/api/` (Laravel 12 REST API) | +| **프론트** | `/Users/kent/Works/@KD_SAM/SAM/react/` (Next.js 15, 이 작업에서는 미수정) | +| **5130 레거시** | `/Users/kent/Works/@KD_SAM/SAM/5130/` (참조 전용) | +| **문서 경로** | `/Users/kent/Works/@KD_SAM/SAM/docs/` | + +### mng Git 저장소 + +```bash +cd /Users/kent/Works/@KD_SAM/SAM/mng +git status && git branch +``` + +> **주의**: SAM/ 루트는 Git 저장소가 아님. api/, mng/, react/ 각각 독립 Git 저장소. + +### 세션 시작 체크리스트 + +``` +1. 이 문서를 읽는다 (📍 현재 진행 상태 섹션 확인) +2. mng/CLAUDE.md 를 읽는다 (mng 프로젝트 규칙 확인) +3. 마지막 완료 작업 확인 → 다음 작업 결정 +4. 해당 Phase의 상세 절차(섹션 11)를 읽는다 +5. 작업 시작 전 사용자에게 "Phase X.X 시작할까요?" 확인 +``` + +### 핵심 파일 (작업 빈도순) + +| 파일 | 설명 | 크기 | +|------|------|------| +| `mng/resources/views/document-templates/edit.blade.php` | 양식 편집 UI (메인 작업 대상) | 44.5KB | +| `mng/app/Http/Controllers/DocumentTemplateController.php` | 양식 CRUD 컨트롤러 | | +| `mng/app/Http/Controllers/DocumentController.php` | 문서 CRUD 컨트롤러 | | +| `mng/app/Models/DocumentTemplate.php` | 양식 모델 (관계 정의) | | +| `mng/app/Models/Documents/Document.php` | 문서 모델 (상태 워크플로우) | | +| `mng/routes/web.php` (340-353줄) | 양식/문서 라우트 | | + +### 모델 관계 구조 (코드 참조) + +```php +// DocumentTemplate.php 주요 관계 +class DocumentTemplate extends Model { + use BelongsToTenant, SoftDeletes; + + // 결재라인: template->approval_lines (작성/검토/승인) + public function approvalLines() { return $this->hasMany(DocumentTemplateApprovalLine::class, 'template_id')->orderBy('sort_order'); } + + // 기본필드: template->basic_fields (품명, LOT NO 등) + public function basicFields() { return $this->hasMany(DocumentTemplateBasicField::class, 'template_id')->orderBy('sort_order'); } + + // 섹션: template->sections->items (검사기준서 섹션 + 검사항목) + public function sections() { return $this->hasMany(DocumentTemplateSection::class, 'template_id')->orderBy('sort_order'); } + + // 컬럼: template->columns (데이터 테이블 컬럼 정의) + public function columns() { return $this->hasMany(DocumentTemplateColumn::class, 'template_id')->orderBy('sort_order'); } +} + +// Document.php 주요 관계 +class Document extends Model { + use BelongsToTenant, SoftDeletes; + + // 상태: DRAFT -> PENDING -> APPROVED/REJECTED/CANCELLED + protected $casts = ['status' => DocumentStatus::class]; + + public function template() { return $this->belongsTo(DocumentTemplate::class); } + public function approvals() { return $this->hasMany(DocumentApproval::class); } + public function data() { return $this->hasMany(DocumentData::class); } // EAV 패턴 + public function attachments() { return $this->hasMany(DocumentAttachment::class); } + public function linkable() { return $this->morphTo(); } // 다형성 연결 (수주, 작업지시 등) +} +``` + +### mng 라우트 구조 + +```php +// mng/routes/web.php (340-353줄) +Route::resource('document-templates', DocumentTemplateController::class); // /document-templates +Route::resource('documents', DocumentController::class); // /documents +``` + +> **URL 확인**: `https://mng.sam.kr/document-templates` (양식 관리), `https://mng.sam.kr/documents` (문서 관리) --- @@ -10,74 +105,10 @@ | 항목 | 내용 | |------|------| -| **마지막 완료 작업** | Phase 2 - MNG 관리자 패널 구현 ✅ | -| **다음 작업** | - (Phase 3 보류) | -| **진행률** | 8/12 (67%) | -| **보류 항목** | 결재 워크플로우 (submit/approve/reject/cancel) - 기존 시스템 연동 필요
Phase 3 React 연동 - 사용자 직접 구현 또는 추후 진행 | -| **마지막 업데이트** | 2026-01-28 | - ---- - -## 0. 빠른 시작 가이드 - -### 0.1 전제 조건 - -```bash -# Docker 서비스 실행 확인 -docker ps | grep sam - -# 예상 결과: sam-api-1, sam-mng-1, sam-mysql-1, sam-nginx-1 실행 중 -``` - -### 0.2 프로젝트 경로 - -| 프로젝트 | 경로 | 설명 | -|----------|------|------| -| API | `/Users/kent/Works/@KD_SAM/SAM/api` | Laravel 12 REST API | -| MNG | `/Users/kent/Works/@KD_SAM/SAM/mng` | Laravel 12 + Blade 관리자 | -| React | `/Users/kent/Works/@KD_SAM/SAM/react` | Next.js 15 프론트엔드 | - -### 0.3 작업 시작 명령어 - -```bash -# 1. API 마이그레이션 상태 확인 -docker exec sam-api-1 php artisan migrate:status - -# 2. 새 마이그레이션 생성 -docker exec sam-api-1 php artisan make:migration create_documents_table - -# 3. 마이그레이션 실행 -docker exec sam-api-1 php artisan migrate - -# 4. 모델 생성 -docker exec sam-api-1 php artisan make:model Document - -# 5. 코드 포맷팅 -docker exec sam-api-1 ./vendor/bin/pint -``` - -### 0.4 작업 순서 요약 - -``` -Phase 1 (API) -├── 1.1 마이그레이션 파일 생성 → 컨펌 필요 -├── 1.2 마이그레이션 실행 -├── 1.3 모델 생성 (Document, DocumentApproval, DocumentData) -├── 1.4 Service 생성 (DocumentService) -├── 1.5 Controller 생성 (DocumentController) -└── 1.6 Swagger 문서 - -Phase 2 (MNG) -├── 2.1 모델 복사/수정 -├── 2.2 문서 목록 화면 -├── 2.3 문서 상세/편집 화면 -└── 2.4 문서 생성 화면 - -Phase 3 (React) -├── 3.1 문서 작성 컴포넌트 -├── 3.2 결재선 지정 UI -└── 3.3 수입검사 연동 -``` +| **마지막 완료 작업** | Phase 2.1 - 문서 생성 (양식 선택 → 빈 문서 생성, 문서번호 자동 채번, 결재라인 초기화) | +| **다음 작업** | Phase 2.2 - 문서 데이터 입력 UI (양식 columns/sections 기반 동적 폼) | +| **진행률** | 6/20 (30%) - Phase 1 ✅, Phase 2.1 ✅ | +| **마지막 업데이트** | 2026-01-31 | --- @@ -85,39 +116,30 @@ Phase 3 (React) ### 1.1 배경 -현재 SAM 시스템에는 문서 템플릿 관리 기능이 존재하나, 실제 문서를 작성하고 관리하는 기능이 없음. +현재 SAM(react)에는 검사 성적서(수입검사, 중간검사), 작업일지 등이 하드코딩된 모달 컴포넌트로 존재한다. 5130 레거시 시스템에도 동일 문서들이 PHP 파일 단위로 구현되어 있다. 이들을 **mng에서 동적으로 양식을 관리**하고, **API를 통해 JSON으로 제공**하여 SAM에서 렌더링하는 구조로 전환한다. -**현재 상태:** -- ✅ MNG: 문서 템플릿 관리 (`/document-templates`) -- ❌ 실제 문서 작성/관리 기능 없음 -- ❌ 결재 시스템과 연동 없음 +**핵심 문제:** +- 현재 검사 문서가 React 컴포넌트에 하드코딩되어, 새 양식 추가 시 코드 수정이 필요 +- 5130의 수입검사만 약 40종의 자재별 페이지가 개별 PHP 파일로 존재 +- 검사 기준, 항목, 판정 로직이 코드와 혼재되어 비개발자가 관리 불가 +- 중간검사(절곡/스크린/슬랫/조인트바)도 각각 별도 컴포넌트로 분산 -**목표:** -- 템플릿 기반 동적 문서 생성 -- 결재 시스템 연동 -- 수입검사/입고등록에서 실사용 +**해결 방향:** +- mng에서 문서양식(템플릿)을 동적으로 정의 → 검사 항목/기준/판정 로직 포함 +- 양식 기반으로 실제 문서 인스턴스를 생성 → 데이터 입력/결재/출력 +- SAM에서 API로 양식+데이터를 JSON 수신 → 범용 렌더러로 표시 -### 1.2 시스템 구조 +### 1.2 기준 원칙 ``` ┌─────────────────────────────────────────────────────────────────┐ -│ 문서 관리 시스템 │ +│ 핵심 원칙 │ ├─────────────────────────────────────────────────────────────────┤ -│ │ -│ [MNG 관리자] [React 사용자] │ -│ ┌──────────────┐ ┌──────────────┐ │ -│ │ 템플릿 관리 │ │ 문서 작성 │ │ -│ │ 문서 관리 │ │ 결재 처리 │ │ -│ └──────────────┘ └──────────────┘ │ -│ │ │ │ -│ └──────────────┬───────────────┘ │ -│ ▼ │ -│ [API Server] │ -│ │ │ -│ ▼ │ -│ [Database] │ -│ documents, document_approvals, document_data │ -│ │ +│ 1. 양식 정의는 mng에서만 관리 (비개발자도 양식 수정 가능하도록) │ +│ 2. SAM(react)은 JSON을 받아 렌더링만 담당 (문서 로직 없음) │ +│ 3. 기존 DB 구조(document_templates 계열) 최대한 활용 │ +│ 4. 5130 레거시의 검사 기준/항목을 데이터로 이관 │ +│ 5. 결재 워크플로우(DRAFT->PENDING->APPROVED) 유지 │ └─────────────────────────────────────────────────────────────────┘ ``` @@ -125,841 +147,600 @@ Phase 3 (React) | 분류 | 예시 | 승인 | |------|------|------| -| ✅ 즉시 가능 | 필드 추가, 문서 수정 | 불필요 | -| ⚠️ 컨펌 필요 | 마이그레이션, 새 API | **필수** | -| 🔴 금지 | 기존 테이블 변경 | 별도 협의 | +| 즉시 가능 | 양식 필드 추가/변경, 검사항목 추가, 기준값 수정, 뷰(Blade) 수정 | 불필요 | +| 컨펌 필요 | 새 DB 테이블 추가, 기존 테이블 컬럼 변경, API 엔드포인트 추가, 마이그레이션 | **필수** | +| 금지 | 기존 document_templates 테이블 구조 파괴적 변경, 기존 API 삭제 | 별도 협의 | + +### 1.4 준수 규칙 +- `docs/specs/database-schema.md` - DB 스키마 참조 +- `docs/standards/quality-checklist.md` - 품질 체크리스트 +- `mng/CLAUDE.md` - MNG 프로젝트 규칙 --- -## 2. 대상 범위 +## 2. 현황 분석 -### 2.1 Phase 1: Database & API +### 2.1 기존 DB 구조 (이미 생성됨) -| # | 작업 항목 | 상태 | 파일 경로 | -|---|----------|:----:|----------| -| 1.1 | 마이그레이션 생성 | ✅ | `api/database/migrations/2026_01_28_200000_create_documents_table.php` | -| 1.2 | Document 모델 | ✅ | `api/app/Models/Documents/Document.php` | -| 1.3 | DocumentApproval 모델 | ✅ | `api/app/Models/Documents/DocumentApproval.php` | -| 1.4 | DocumentData 모델 | ✅ | `api/app/Models/Documents/DocumentData.php` | -| 1.5 | DocumentService | ⏳ | `api/app/Services/DocumentService.php` | -| 1.6 | DocumentController | ⏳ | `api/app/Http/Controllers/Api/V1/DocumentController.php` | -| 1.7 | FormRequest | ⏳ | `api/app/Http/Requests/Document/` | -| 1.8 | Swagger 문서 | ⏳ | `api/app/Swagger/v1/DocumentApi.php` | +``` +document_templates # 양식 마스터 +├── document_template_approval_lines # 결재라인 (작성/검토/승인) +├── document_template_basic_fields # 기본필드 (품명, LOT NO 등) +├── document_template_sections # 섹션 (검사기준서 섹션) +│ └── document_template_section_items # 섹션 항목 (검사항목) +└── document_template_columns # 데이터 테이블 컬럼 -### 2.2 Phase 2: MNG 관리 화면 +documents # 문서 인스턴스 +├── document_approvals # 결재 이력 +├── document_data # 필드 데이터 (EAV 패턴) +└── document_attachments # 첨부 파일 +``` -| # | 작업 항목 | 상태 | 파일 경로 | -|---|----------|:----:|----------| -| 2.1 | Document 모델 | ✅ | `mng/app/Models/Documents/Document.php` | -| 2.2 | DocumentController | ✅ | `mng/app/Http/Controllers/DocumentController.php` | -| 2.3 | 문서 목록 뷰 | ✅ | `mng/resources/views/documents/index.blade.php` | -| 2.4 | 문서 상세 뷰 | ✅ | `mng/resources/views/documents/show.blade.php` | -| 2.5 | 문서 생성/수정 뷰 | ✅ | `mng/resources/views/documents/edit.blade.php` | -| 2.6 | API Controller | ✅ | `mng/app/Http/Controllers/Api/Admin/DocumentApiController.php` | +**주요 테이블 컬럼:** -### 2.3 Phase 3: React 연동 (⏸️ 보류) +| 테이블 | 핵심 컬럼 | +|--------|----------| +| `document_templates` | tenant_id, name, category, title, company_name, footer_remark_label, footer_judgement_label, footer_judgement_options(json) | +| `document_template_approval_lines` | template_id, name, dept, role, sort_order | +| `document_template_basic_fields` | template_id, label, field_type(text/date), default_value, sort_order | +| `document_template_sections` | template_id, title, image_path, sort_order | +| `document_template_section_items` | section_id, category, item, standard, method, frequency, regulation, sort_order | +| `document_template_columns` | template_id, label, column_type(text/check/measurement/select/complex), group_name, sub_labels(json), width, sort_order | +| `documents` | tenant_id, template_id, document_no, title, status(DRAFT/PENDING/APPROVED/REJECTED/CANCELLED), linkable_type, linkable_id | +| `document_data` | document_id, section_id, column_id, row_index, field_key, field_value | +| `document_approvals` | document_id, user_id, step, role, status(PENDING/APPROVED/REJECTED), comment, acted_at | -| # | 작업 항목 | 상태 | 파일 경로 | -|---|----------|:----:|----------| -| 3.1 | 문서 작성 컴포넌트 | ⏸️ | `react/src/components/document-system/DocumentForm/` | -| 3.2 | API actions | ⏸️ | `react/src/components/document-system/actions.ts` | -| 3.3 | 수입검사 연동 | ⏸️ | `react/src/components/material/ReceivingManagement/` | +### 2.2 기존 MNG 코드 현황 -> **보류 사유**: 사용자 직접 구현 또는 추후 진행 예정 +| 항목 | 경로 | 상태 | +|------|------|------| +| DocumentTemplate 모델 | `mng/app/Models/DocumentTemplate.php` | 존재 | +| Document 모델 | `mng/app/Models/Documents/Document.php` | 존재 | +| 관련 하위 모델 6개 | `mng/app/Models/Documents/`, `mng/app/Models/DocumentTemplate*.php` | 존재 | +| DocumentTemplateController | `mng/app/Http/Controllers/DocumentTemplateController.php` | 존재 | +| DocumentController | `mng/app/Http/Controllers/DocumentController.php` | 존재 | +| 라우트 (templates, documents) | `mng/routes/web.php` 340-353줄 | 존재 | +| 양식 편집 Blade | `mng/resources/views/document-templates/edit.blade.php` (44.5KB) | 존재 | +| 문서 Blade (index/edit/show) | `mng/resources/views/documents/` | 존재 | + +### 2.3 5130 레거시 검사 문서 현황 + +#### 수입검사 (instock) + +| 항목 | 내용 | +|------|------| +| 위치 | `5130/instock/` | +| 자재별 검사 페이지 | 40+ PHP 파일 (`i_EGI155.php`, `i_SUSplate.php`, `i_wire.php`, `i_motor.php` 등) | +| 메인 로더 | `fetch_inspection.php` (21.8KB) - 자재코드별 동적 로딩 | +| 검사 필드 | 로트번호, 검사일, 납품업체, 품명, 규격, 단위, 품목코드, 입고량, 자재번호, 제조사 | +| 판정 방식 | 항목별 합격/불합격 -> 종합판정 자동계산 | +| LOT 관리 | `lotnum.txt` 파일 기반, YYMMDD-## 형식 | +| PDF 출력 | html2pdf.js 사용 | + +#### 중간검사 (output) + +| 검사 종류 | 파일 | DB 필드 | +|----------|------|---------| +| 절곡품 중간검사 | `viewMidInspectBending.php` (60.7KB) | `recordbendingMid` (JSON) | +| 스크린 중간검사 | `viewMidInspectScreen.php` (33.6KB) | `recordscreenMid` (JSON) | +| 슬랫 중간검사 | `viewMidInspectSlat.php` | `recordslatMid` (JSON) | +| 조인트바 검사 | `viewinspectionJointbar.php` (34.1KB) | `recordjointbar` (JSON) | + +#### 검사 공통 구조 +- 결재: 작성(판매/Order) -> 검토(생산) -> 승인(품질/QC) +- 검사 기준 이미지: `5130/img/inspection/` (20+ 이미지) +- 데이터: JSON으로 DB 저장 (approval chain + measurements) +- QC 관리자 권한 제어 (이세희, 함신옥, 이경호, 노완호) + +### 2.4 SAM(react) 현재 검사 컴포넌트 + +| 컴포넌트 | 경로 | 용도 | +|---------|------|------| +| ImportInspectionDocument | `react/src/.../quality/qms/components/documents/` | 수입검사 성적서 | +| ScreenInspectionDocument | 동일 경로 | 스크린 중간검사 성적서 | +| SlatInspectionDocument | 동일 경로 | 슬랫 중간검사 성적서 | +| BendingInspectionDocument | 동일 경로 | 절곡품 중간검사 성적서 | +| JointbarInspectionDocument | 동일 경로 | 조인트바 중간검사 성적서 | +| ProductInspectionDocument | 동일 경로 | 제품검사 성적서 | +| WorkLogContent | `react/src/components/production/WorkerScreen/` | 작업일지 | +| InspectionReportModal | `react/src/components/production/WorkOrders/documents/` | 중간검사 모달 | +| DocumentViewer | `react/src/components/document-system/viewer/` | 범용 문서 뷰어 | + +**공통 컴포넌트 (document-system):** +- `DocumentHeader.tsx` - 문서 헤더 (로고, 결재라인) +- `QualityApprovalTable.tsx` - 품질 결재표 +- `InfoTable.tsx` - 정보 테이블 +- `DocumentViewer.tsx` - 문서 뷰어 (zoom, drag, print, download) --- -## 3. 상세 설계 +## 3. 대상 범위 -### 3.1 Database Schema (마이그레이션 파일) +### 3.1 Phase 1: mng 양식 관리 기능 완성 (수입검사) -```php -id(); - $table->foreignId('tenant_id')->constrained()->cascadeOnDelete(); - $table->foreignId('template_id')->constrained('document_templates'); +### 3.2 Phase 2: mng 문서 생성/관리 기능 - // 문서 정보 - $table->string('document_no', 50)->comment('문서번호'); - $table->string('title', 255)->comment('문서 제목'); - $table->enum('status', ['DRAFT', 'PENDING', 'APPROVED', 'REJECTED', 'CANCELLED']) - ->default('DRAFT')->comment('상태'); +양식을 기반으로 실제 검사 문서를 생성하고 데이터를 입력/결재하는 기능. - // 연결 정보 (다형성) - $table->string('linkable_type', 100)->nullable()->comment('연결 타입'); - $table->unsignedBigInteger('linkable_id')->nullable()->comment('연결 ID'); +| # | 작업 항목 | 상태 | 완료 기준 | 비고 | +|---|----------|:----:|----------|------| +| 2.1 | 문서 생성 (양식 선택 -> 빈 문서 생성) | ✅ | 양식 선택 후 빈 문서(DRAFT)가 documents 테이블에 생성됨. 문서번호 자동 채번 | 카테고리별 prefix (IQC/PRD/SLS/PUR), 결재라인 초기화, 기본필드 뷰 수정 완료 | +| 2.2 | 문서 데이터 입력 UI | ⏳ | 양식의 columns/sections 기반 동적 폼에서 검사값 입력 가능. 저장 시 document_data에 EAV로 저장 | Blade + HTMX | +| 2.3 | 결재 워크플로우 (제출/승인/반려) | ⏳ | DRAFT→PENDING(제출)→APPROVED/REJECTED. document_approvals에 결재 이력 기록 | 기존 상태 ENUM 활용 | +| 2.4 | 문서 목록/검색/필터 | ⏳ | 상태별(DRAFT/PENDING/APPROVED), 양식별, 날짜별 필터 동작. 페이징 포함 | | +| 2.5 | 문서 PDF 출력 | ⏭️ | **추후 고려** - react에 이미 html2pdf.js 구현됨 (6.2 결정사항 #1 참고) | | - // 메타 정보 - $table->foreignId('created_by')->constrained('users'); - $table->timestamp('submitted_at')->nullable()->comment('결재 요청일'); - $table->timestamp('completed_at')->nullable()->comment('결재 완료일'); +### 3.3 Phase 3: 중간검사 양식 추가 - $table->timestamps(); - $table->softDeletes(); +| # | 작업 항목 | 상태 | 완료 기준 | 비고 | +|---|----------|:----:|----------|------| +| 3.1 | 중간검사 양식 구조 설계 | ⏳ | 절곡/스크린/슬랫/조인트바 4종의 검사항목/기준/판정방식 문서화 완료 | 5130 JSON 구조 분석 | +| 3.2 | 5130 중간검사 데이터 이관 설계 | ⏳ | recordbendingMid 등 JSON→양식 매핑 테이블 완성 | | +| 3.3 | 중간검사 양식 시드 데이터 | ⏳ | 4종 양식 seeder 생성, `mng.sam.kr/document-templates`에서 확인 가능 | | +| 3.4 | 검사 기준 이미지 관리 | ⏳ | `5130/img/inspection/` 이미지 → `mng/storage/app/public/inspection/`로 이관. 양식에서 참조 가능 | 20+ 이미지 | - $table->index(['tenant_id', 'status']); - $table->index('document_no'); - $table->index(['linkable_type', 'linkable_id']); - }); +### 3.4 Phase 4: API 연동 및 mng JSON 화면 구현 - // 문서 결재 - Schema::create('document_approvals', function (Blueprint $table) { - $table->id(); - $table->foreignId('document_id')->constrained()->cascadeOnDelete(); - $table->foreignId('user_id')->constrained(); +| # | 작업 항목 | 상태 | 완료 기준 | 비고 | +|---|----------|:----:|----------|------| +| 4.1 | API 엔드포인트 설계 (양식 조회, 문서 CRUD) | ⏳ | api 저장소에 REST 엔드포인트 구현. Swagger 문서화 완료. 섹션 4.1의 엔드포인트 전체 동작 | ⚠️ 컨펌 필요 | +| 4.2 | mng에서 JSON 기반 문서 화면 구현 | ⏳ | API에서 받은 양식 JSON으로 검사 성적서 형태 화면 렌더링. 기본정보+검사테이블+판정란 표시 | Blade + HTMX | +| 4.3 | mng에서 문서 데이터 입력/저장 연동 | ⏳ | JSON 화면에서 검사값 입력→API 호출→DB 저장. 판정(합격/불합격) 결과 저장 확인 | 6.2 결정사항 #2 적용 | +| 4.4 | 프론트엔드 담당자 협의 후 react 전환 결정 | ⏳ | mng 완성 후 프론트 담당자와 미팅. react 기존 컴포넌트는 미수정 (6.2 결정사항 #4) | 협의 결과 문서화 | - $table->unsignedTinyInteger('step')->default(1)->comment('결재 순서'); - $table->string('role', 50)->comment('역할 (작성/검토/승인)'); - $table->enum('status', ['PENDING', 'APPROVED', 'REJECTED']) - ->default('PENDING')->comment('상태'); +### 3.5 Phase 5 (추후): 기타 문서 확장 - $table->text('comment')->nullable()->comment('결재 의견'); - $table->timestamp('acted_at')->nullable()->comment('결재 처리일'); +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 5.1 | 제품검사 양식 | ⏳ | Phase 3 이후 | +| 5.2 | 작업일지 양식 | ⏳ | WorkLogContent 기반 | +| 5.3 | 견적서/거래명세서/발주서 양식 | ⏳ | 기존 문서 모달 분석 필요 | - $table->timestamps(); +--- - $table->index(['document_id', 'step']); - }); +## 4. 아키텍처 설계 - // 문서 데이터 - Schema::create('document_data', function (Blueprint $table) { - $table->id(); - $table->foreignId('document_id')->constrained()->cascadeOnDelete(); +### 4.1 시스템 흐름 - $table->unsignedBigInteger('section_id')->nullable()->comment('섹션 ID'); - $table->unsignedBigInteger('column_id')->nullable()->comment('컬럼 ID'); - $table->unsignedSmallInteger('row_index')->default(0)->comment('행 인덱스'); +``` +[mng - 양식 관리] [api - REST API] [SAM - react 프론트] - $table->string('field_key', 100)->comment('필드 키'); - $table->text('field_value')->nullable()->comment('값'); - - $table->timestamps(); - - $table->index(['document_id', 'section_id']); - }); - - // 문서 첨부파일 - Schema::create('document_attachments', function (Blueprint $table) { - $table->id(); - $table->foreignId('document_id')->constrained()->cascadeOnDelete(); - $table->foreignId('file_id')->constrained('files'); - - $table->string('attachment_type', 50)->default('general')->comment('유형'); - $table->string('description', 255)->nullable(); - - $table->timestamps(); - }); - } - - public function down(): void - { - Schema::dropIfExists('document_attachments'); - Schema::dropIfExists('document_data'); - Schema::dropIfExists('document_approvals'); - Schema::dropIfExists('documents'); - } -}; +DocumentTemplate CRUD ----------> GET /document-templates 양식 목록/상세 + - 결재라인 설정 GET /document-templates/{id} + - 기본필드 설정 + - 섹션/항목 설정 POST /documents 문서 생성 + - 컬럼 설정 PUT /documents/{id} 데이터 입력 + GET /documents/{id} 문서 조회 +Document 생성/관리 ------------> POST /documents/{id}/submit 결재 제출 + - 데이터 입력 POST /documents/{id}/approve 결재 승인 + - 결재 처리 POST /documents/{id}/reject 결재 반려 + - PDF 출력 GET /documents/{id}/pdf PDF 다운로드 ``` -### 3.2 Model 코드 템플릿 +### 4.2 JSON 응답 구조 (양식 상세) -```php - 'datetime', - 'completed_at' => 'datetime', - ]; - - // === 상태 상수 === - public const STATUS_DRAFT = 'DRAFT'; - public const STATUS_PENDING = 'PENDING'; - public const STATUS_APPROVED = 'APPROVED'; - public const STATUS_REJECTED = 'REJECTED'; - public const STATUS_CANCELLED = 'CANCELLED'; - - // === 관계 === - public function template(): BelongsTo - { - return $this->belongsTo(\App\Models\DocumentTemplate::class, 'template_id'); - } - - public function approvals(): HasMany - { - return $this->hasMany(DocumentApproval::class)->orderBy('step'); - } - - public function data(): HasMany - { - return $this->hasMany(DocumentData::class); - } - - public function attachments(): HasMany - { - return $this->hasMany(DocumentAttachment::class); - } - - public function linkable(): MorphTo - { - return $this->morphTo(); - } - - public function creator(): BelongsTo - { - return $this->belongsTo(\App\Models\User::class, 'created_by'); - } - - // === 스코프 === - public function scopeStatus($query, string $status) - { - return $query->where('status', $status); - } - - // === 헬퍼 === - public function canEdit(): bool - { - return $this->status === self::STATUS_DRAFT; - } - - public function canSubmit(): bool - { - return $this->status === self::STATUS_DRAFT; - } + "template": { + "id": 1, + "name": "EGI 1.55T 수입검사", + "category": "incoming_inspection", + "title": "수 입 검 사 성 적 서", + "companyName": "케이디산업", + "approvalLines": [ + { "name": "작성", "dept": "판매/Order", "role": "담당자", "sortOrder": 1 }, + { "name": "검토", "dept": "생산", "role": "담당자", "sortOrder": 2 }, + { "name": "승인", "dept": "품질", "role": "QC", "sortOrder": 3 } + ], + "basicFields": [ + { "label": "품명", "fieldType": "text", "sortOrder": 1 }, + { "label": "규격", "fieldType": "text", "sortOrder": 2 }, + { "label": "LOT NO", "fieldType": "text", "sortOrder": 3 }, + { "label": "검사일자", "fieldType": "date", "sortOrder": 4 }, + { "label": "납품업체", "fieldType": "text", "sortOrder": 5 }, + { "label": "검사자", "fieldType": "text", "sortOrder": 6 } + ], + "sections": [ + { + "title": "가이드레일", + "imagePath": "/storage/inspection/guiderail.jpg", + "items": [ + { + "category": "겉모양", + "item": "사용상 결함이 될 흠이 없을 것", + "standard": "KS D 3506", + "method": "육안검사", + "frequency": "체크검사", + "regulation": "KS D 3506" + }, + { + "category": "치수", + "item": "두께", + "standard": "1.55 +/- 0.15", + "method": "계측", + "frequency": "입고시", + "regulation": "KS D 3506" + } + ] + } + ], + "columns": [ + { "label": "NO", "columnType": "text", "width": "50px", "sortOrder": 1 }, + { "label": "검사항목", "columnType": "text", "width": "120px", "sortOrder": 2 }, + { "label": "검사기준", "columnType": "text", "width": "150px", "sortOrder": 3 }, + { + "label": "검사 DATA", + "columnType": "complex", + "groupName": "검사 DATA", + "subLabels": ["1", "2", "3", "4", "5"], + "width": "300px", + "sortOrder": 4 + }, + { "label": "판정", "columnType": "select", "width": "80px", "sortOrder": 5 } + ], + "footerRemarkLabel": "부적합 내용", + "footerJudgementLabel": "종합판정", + "footerJudgementOptions": ["합격", "불합격"] + } } ``` -```php - 'integer', - 'acted_at' => 'datetime', - ]; - - public const STATUS_PENDING = 'PENDING'; - public const STATUS_APPROVED = 'APPROVED'; - public const STATUS_REJECTED = 'REJECTED'; - - public function document(): BelongsTo - { - return $this->belongsTo(Document::class); - } - - public function user(): BelongsTo - { - return $this->belongsTo(\App\Models\User::class); - } -} -``` - -```php - 'integer', - ]; - - public function document(): BelongsTo - { - return $this->belongsTo(Document::class); - } -} -``` - -### 3.3 Service 코드 템플릿 - -```php -where('tenant_id', $this->tenantId()) - ->with(['template:id,name,category', 'creator:id,name']); - - // 필터 - if (!empty($params['status'])) { - $query->where('status', $params['status']); - } - if (!empty($params['template_id'])) { - $query->where('template_id', $params['template_id']); - } - if (!empty($params['search'])) { - $search = $params['search']; - $query->where(function ($q) use ($search) { - $q->where('document_no', 'like', "%{$search}%") - ->orWhere('title', 'like', "%{$search}%"); - }); - } - - return $query->orderByDesc('id') - ->paginate($params['per_page'] ?? 20); - } - - /** - * 문서 상세 조회 - */ - public function show(int $id): Document - { - $document = Document::with([ - 'template.approvalLines', - 'template.sections.items', - 'template.columns', - 'approvals.user:id,name', - 'data', - 'creator:id,name', - ])->find($id); - - if (!$document || $document->tenant_id !== $this->tenantId()) { - throw new NotFoundHttpException(__('error.not_found')); - } - - return $document; - } - - /** - * 문서 생성 - */ - public function create(array $data): Document - { - $document = Document::create([ - 'tenant_id' => $this->tenantId(), - 'template_id' => $data['template_id'], - 'document_no' => $this->generateDocumentNo($data['template_id']), - 'title' => $data['title'], - 'status' => Document::STATUS_DRAFT, - 'linkable_type' => $data['linkable_type'] ?? null, - 'linkable_id' => $data['linkable_id'] ?? null, - 'created_by' => $this->apiUserId(), - ]); - - // 결재선 생성 - if (!empty($data['approvers'])) { - foreach ($data['approvers'] as $step => $approver) { - DocumentApproval::create([ - 'document_id' => $document->id, - 'user_id' => $approver['user_id'], - 'step' => $step + 1, - 'role' => $approver['role'], - 'status' => DocumentApproval::STATUS_PENDING, - ]); + "document": { + "id": 1, + "templateId": 1, + "documentNo": "IQC-260131-01", + "title": "EGI 1.55T 수입검사 성적서", + "status": "APPROVED", + "template": { "...위 구조와 동일..." }, + "basicData": { + "품명": "전기 아연도금 강판", + "규격": "EGI 1.55T", + "LOT NO": "260131-01", + "검사일자": "2026-01-31", + "납품업체": "포스코", + "검사자": "이세희" + }, + "tableData": [ + { + "sectionId": 1, + "rows": [ + { + "rowIndex": 0, + "values": { + "NO": "1", + "검사항목": "겉모양", + "검사기준": "사용상 결함 없을 것", + "검사 DATA": { "1": "양호", "2": "양호", "3": "양호", "4": "-", "5": "-" }, + "판정": "적합" } - } - - // 데이터 저장 - if (!empty($data['data'])) { - foreach ($data['data'] as $item) { - DocumentData::create([ - 'document_id' => $document->id, - 'section_id' => $item['section_id'] ?? null, - 'column_id' => $item['column_id'] ?? null, - 'row_index' => $item['row_index'] ?? 0, - 'field_key' => $item['field_key'], - 'field_value' => $item['field_value'], - ]); - } - } - - return $document->fresh(['approvals', 'data']); - } - - /** - * 결재 요청 (DRAFT → PENDING) - */ - public function submit(int $id): Document - { - $document = $this->show($id); - - if (!$document->canSubmit()) { - throw new BadRequestHttpException(__('error.invalid_status')); - } - - $document->update([ - 'status' => Document::STATUS_PENDING, - 'submitted_at' => now(), - ]); - - return $document->fresh(); - } - - /** - * 결재 승인 - */ - public function approve(int $id, ?string $comment = null): Document - { - $document = $this->show($id); - $userId = $this->apiUserId(); - - // 현재 사용자의 결재 단계 찾기 - $approval = $document->approvals - ->where('user_id', $userId) - ->where('status', DocumentApproval::STATUS_PENDING) - ->first(); - - if (!$approval) { - throw new BadRequestHttpException(__('error.not_your_turn')); - } - - $approval->update([ - 'status' => DocumentApproval::STATUS_APPROVED, - 'comment' => $comment, - 'acted_at' => now(), - ]); - - // 모든 결재 완료 확인 - $allApproved = $document->approvals() - ->where('status', '!=', DocumentApproval::STATUS_APPROVED) - ->doesntExist(); - - if ($allApproved) { - $document->update([ - 'status' => Document::STATUS_APPROVED, - 'completed_at' => now(), - ]); - } - - return $document->fresh(['approvals']); - } - - /** - * 결재 반려 - */ - public function reject(int $id, string $comment): Document - { - $document = $this->show($id); - $userId = $this->apiUserId(); - - $approval = $document->approvals - ->where('user_id', $userId) - ->where('status', DocumentApproval::STATUS_PENDING) - ->first(); - - if (!$approval) { - throw new BadRequestHttpException(__('error.not_your_turn')); - } - - $approval->update([ - 'status' => DocumentApproval::STATUS_REJECTED, - 'comment' => $comment, - 'acted_at' => now(), - ]); - - $document->update([ - 'status' => Document::STATUS_REJECTED, - 'completed_at' => now(), - ]); - - return $document->fresh(['approvals']); - } - - /** - * 문서번호 생성 - */ - private function generateDocumentNo(int $templateId): string - { - $prefix = 'DOC'; - $date = now()->format('Ymd'); - $count = Document::where('tenant_id', $this->tenantId()) - ->whereDate('created_at', today()) - ->count() + 1; - - return sprintf('%s-%s-%04d', $prefix, $date, $count); - } + } + ] + } + ], + "footerData": { + "remark": "", + "judgement": "합격" + }, + "approvals": [ + { "step": 1, "role": "작성", "userName": "홍길동", "status": "APPROVED", "actedAt": "2026-01-31" }, + { "step": 2, "role": "검토", "userName": "김철수", "status": "APPROVED", "actedAt": "2026-01-31" }, + { "step": 3, "role": "승인", "userName": "이세희", "status": "APPROVED", "actedAt": "2026-01-31" } + ] + } } ``` -### 3.4 Controller 코드 템플릿 - -```php - $this->service->list($request->all()), - __('message.fetched') - ); - } - - public function show(int $id): JsonResponse - { - return ApiResponse::handle( - fn () => $this->service->show($id), - __('message.fetched') - ); - } - - public function store(CreateDocumentRequest $request): JsonResponse - { - return ApiResponse::handle( - fn () => $this->service->create($request->validated()), - __('message.created') - ); - } - - public function submit(int $id): JsonResponse - { - return ApiResponse::handle( - fn () => $this->service->submit($id), - __('message.document.submitted') - ); - } - - public function approve(int $id, ApproveDocumentRequest $request): JsonResponse - { - return ApiResponse::handle( - fn () => $this->service->approve($id, $request->comment), - __('message.document.approved') - ); - } - - public function reject(int $id, RejectDocumentRequest $request): JsonResponse - { - return ApiResponse::handle( - fn () => $this->service->reject($id, $request->comment), - __('message.document.rejected') - ); - } -} -``` - -### 3.5 API Routes - -```php -// api/routes/api.php 에 추가 - -Route::prefix('v1')->middleware(['auth.apikey'])->group(function () { - // ... 기존 라우트 ... - - // 문서 관리 - Route::prefix('documents')->middleware(['auth:sanctum'])->group(function () { - Route::get('/', [DocumentController::class, 'index']); - Route::post('/', [DocumentController::class, 'store']); - Route::get('/{id}', [DocumentController::class, 'show']); - Route::put('/{id}', [DocumentController::class, 'update']); - Route::delete('/{id}', [DocumentController::class, 'destroy']); - - // 결재 - Route::post('/{id}/submit', [DocumentController::class, 'submit']); - Route::post('/{id}/approve', [DocumentController::class, 'approve']); - Route::post('/{id}/reject', [DocumentController::class, 'reject']); - Route::post('/{id}/cancel', [DocumentController::class, 'cancel']); - }); -}); -``` - -### 3.6 문서 상태 흐름 - -``` -┌──────────────────────────────────────────────────────────────┐ -│ │ -│ DRAFT ──submit──> PENDING ──approve──> APPROVED │ -│ │ │ │ -│ │ │──reject──> REJECTED │ -│ │ │ │ │ -│ │ │──cancel──> CANCELLED │ -│ │ │ │ -│ └──────────────────<──edit─────┘ (반려 시 수정 후 재요청) │ -│ │ -└──────────────────────────────────────────────────────────────┘ -``` - --- -## 4. 기존 코드 참조 (인라인) +## 5. 5130 데이터 이관 계획 -### 4.1 기존 템플릿 테이블 구조 +### 5.1 수입검사 자재 목록 (주요) -``` -document_templates (기존) -├── id, tenant_id, name, category, title -├── company_name, company_address, company_contact -├── footer_remark_label, footer_judgement_label -├── footer_judgement_options (JSON) -└── is_active, timestamps, soft_deletes +5130의 `instock/fetch_inspection.php`에서 자재코드별로 로딩하는 개별 페이지를 분석하여, 각 자재별 검사항목을 양식 시드 데이터로 변환한다. -document_template_approval_lines (기존) -├── id, template_id, name, dept, role, sort_order -└── timestamps +| # | 자재 | 5130 파일 | 검사 항목 수 | 우선순위 | +|---|------|----------|:----------:|:-------:| +| 1 | EGI 1.55T (전기아연도금강판) | `i_EGI155.php` | ~8 | 높음 | +| 2 | SUS Plate (스테인리스강판) | `i_SUSplate.php` | ~6 | 높음 | +| 3 | GI Plate (아연도금강판) | `i_GIplate.php` | ~6 | 높음 | +| 4 | Wire (와이어) | `i_wire.php` | ~4 | 중간 | +| 5 | Motor (모터) | `i_motor.php` | ~5 | 중간 | +| 6 | Angle (앵글) | `i_angle.php` | ~4 | 중간 | +| 7-20+ | 기타 자재 | 개별 PHP 파일 | 다양 | 낮음 | -document_template_sections (기존) -├── id, template_id, title, image_path, sort_order -└── timestamps +### 5.2 중간검사 이관 항목 -document_template_section_items (기존) -├── id, section_id, category, item, standard -├── method, frequency, regulation, sort_order -└── timestamps +| 검사 종류 | 5130 DB 필드 | 검사 항목 | 판정 방식 | +|----------|-------------|----------|----------| +| 절곡품 | `recordbendingMid` (JSON) | 겉모양, 치수(높이/길이/간격), 가공상태 | 양호/불량 + 측정값 허용범위 | +| 스크린 | `recordscreenMid` (JSON) | 가공상태, 재봉상태, 조립상태, 길이, 나비, 간격 | 양호/불량 + OK/NG | +| 슬랫 | `recordslatMid` (JSON) | 절곡품과 유사 구조 | 양호/불량 + 측정값 | +| 조인트바 | `recordjointbar` (JSON) | 가공상태, 조립상태, 치수(높이1/높이2/길이/간격) | 양호/불량 + 측정값 허용치 | -document_template_columns (기존) -├── id, template_id, label, width, column_type -├── group_name, sub_labels (JSON), sort_order -└── timestamps -``` +### 5.3 검사 기준 이미지 이관 -### 4.2 API Service 기본 클래스 - -```php -// api/app/Services/Service.php (기존) -abstract class Service -{ - protected function tenantIdOrNull(): ?int; // 테넌트 ID (없으면 null) - protected function tenantId(): int; // 테넌트 ID (없으면 400 예외) - protected function apiUserId(): int; // 사용자 ID (없으면 401 예외) -} -``` - -### 4.3 API Response 헬퍼 - -```php -// api/app/Helpers/ApiResponse.php (기존) -use App\Helpers\ApiResponse; - -// 성공 응답 -ApiResponse::success($data, $message, $debug, $statusCode); - -// 에러 응답 -ApiResponse::error($message, $code, $error); - -// 컨트롤러에서 사용 (권장) -ApiResponse::handle(fn () => $this->service->method(), __('message.xxx')); -``` - -### 4.4 React 결재선 컴포넌트 위치 - -``` -react/src/components/approval/DocumentCreate/ApprovalLineSection.tsx -- 직원 목록에서 결재자 선택 -- getEmployees() 호출로 직원 목록 조회 -- ApprovalPerson[] 형태로 결재선 관리 -``` +`5130/img/inspection/` 디렉토리의 이미지를 mng storage로 이관: +- `jointbar_inspection.jpg` +- `screen_inspection.jpg` +- `bending_inspection1.jpg`, `bending_inspection2.jpg` +- `slat_inspection.jpg` +- `guiderail_*` (다수 변형) +- 기타 17+ 이미지 --- -## 5. 작업 절차 +## 6. 기술 결정사항 -### Step 1: 마이그레이션 생성 (⚠️ 컨펌 필요) +### 6.1 확정된 사항 -```bash -# 1. 마이그레이션 파일 생성 -docker exec sam-api-1 php artisan make:migration create_documents_table +| 항목 | 결정 | 이유 | +|------|------|------| +| 양식 관리 위치 | mng (Laravel + Blade) | 관리자 전용, HTMX 기반 UI 이미 존재 | +| 데이터 저장 패턴 | EAV (document_data 테이블) | 이미 설계됨, 동적 필드 지원 | +| 문서 상태 | DRAFT -> PENDING -> APPROVED/REJECTED/CANCELLED | 이미 구현됨 | +| API 제공 | api 저장소 (Laravel REST API) | SAM 표준 아키텍처 | +| 프론트엔드 소비 | react (Next.js) JSON 렌더링 | 기존 document-system 컴포넌트 확장 | -# 2. 위 3.1 스키마 코드 붙여넣기 +### 6.2 검토 완료 사항 (2026-01-31 확정) -# 3. 마이그레이션 실행 -docker exec sam-api-1 php artisan migrate -``` - -### Step 2: 모델 생성 - -```bash -# Documents 폴더 생성 후 모델 파일 생성 -mkdir -p api/app/Models/Documents - -# 위 3.2 모델 코드 각각 생성 -``` - -### Step 3: Service & Controller - -```bash -# Service 생성 -# api/app/Services/DocumentService.php - -# Controller 생성 -# api/app/Http/Controllers/Api/V1/DocumentController.php - -# Routes 추가 -# api/routes/api.php -``` - -### Step 4: MNG 화면 - -```bash -# mng/app/Models/Document.php -# mng/app/Http/Controllers/DocumentController.php -# mng/resources/views/documents/*.blade.php -``` - -### Step 5: React 연동 - -```bash -# react/src/components/document-system/DocumentForm/ -# react/src/components/document-system/actions.ts -``` +| # | 항목 | 결정 | 근거 | +|---|------|------|------| +| 1 | PDF 생성 | **추후 고려** | react에 이미 구현됨 (html2pdf.js + DocumentViewer). mng 단계에서는 PDF 불필요 | +| 2 | 검사 판정 로직 | **프론트에서 입력, 결과만 저장** | 양식이 검사항목/기준을 정의하고, 프론트에서 사용자가 입력. 저장 시 입력값+판정 결과를 그대로 저장. 별도 판정 엔진 불필요 | +| 3 | 양식 버전 관리 | **수정 시 새 버전 생성** | 요청마다 검사 기준이 다를 수 있으므로 버전 관리 필수. document_templates에 version 컬럼 추가 필요 | +| 4 | 기존 react 컴포넌트 전환 | **기존 react 미수정** | mng에서 JSON 기반 화면 구현까지만 개발. 이후 프론트엔드 담당자와 협의하여 react 전환 여부 결정 | --- -## 6. 컨펌 대기 목록 +## 7. 컨펌 대기 목록 | # | 항목 | 변경 내용 | 영향 범위 | 상태 | |---|------|----------|----------|------| -| 1 | DB 스키마 | 4개 테이블 신규 생성 | api/database | ⏳ 대기 | +| 1 | API 엔드포인트 추가 | `/api/v1/document-templates`, `/api/v1/documents` | api 저장소 | ⏳ Phase 4 전 | +| 2 | DB 마이그레이션 변경 여부 | 기존 테이블로 충분한지 vs version 컬럼 추가 필요 (6.2 #3 확정) | api 저장소 | ⏳ Phase 1 중 | +| 3 | ~~검사 판정 로직 위치~~ | ~~프론트 vs 백엔드~~ → **프론트 입력, 결과만 저장** | - | ✅ 해결됨 (6.2 #2) | +| 4 | ~~PDF 생성 방식~~ | ~~클라이언트 vs 서버~~ → **추후 고려** (react 기 구현) | - | ✅ 해결됨 (6.2 #1) | --- -## 7. 변경 이력 +## 8. 변경 이력 -| 날짜 | 항목 | 변경 내용 | 파일 | 승인 | -|------|------|----------|------|------| -| 2025-01-28 | - | 계획 문서 작성 | - | - | -| 2025-01-28 | - | 자기완결성 보완 | - | - | -| 2026-01-28 | Phase 1.1 | 마이그레이션 파일 생성 및 실행 | `2026_01_28_200000_create_documents_table.php` | ✅ | -| 2026-01-28 | Phase 1.2 | 모델 생성 (Document, DocumentApproval, DocumentData, DocumentAttachment) | `api/app/Models/Documents/` | ✅ | -| 2026-01-28 | Phase 2 | MNG 관리자 패널 구현 (모델, 컨트롤러, 뷰, API) | `mng/app/Models/Documents/`, `mng/app/Http/Controllers/`, `mng/resources/views/documents/` | ✅ | +> 📎 별도 파일로 관리: [`document-management-system-changelog.md`](./document-management-system-changelog.md) --- -## 8. 검증 결과 +## 9. 참고 문서 및 파일 -> 작업 완료 후 이 섹션에 검증 결과 추가 +### 프로젝트 문서 +- `docs/specs/database-schema.md` - DB 스키마 +- `docs/standards/quality-checklist.md` - 품질 체크리스트 +- `mng/CLAUDE.md` - MNG 프로젝트 규칙 -### 8.1 테스트 케이스 +### 기존 코드 (mng) +- `mng/app/Models/DocumentTemplate.php` - 양식 모델 +- `mng/app/Models/Documents/Document.php` - 문서 모델 +- `mng/app/Http/Controllers/DocumentTemplateController.php` - 양식 컨트롤러 +- `mng/app/Http/Controllers/DocumentController.php` - 문서 컨트롤러 +- `mng/resources/views/document-templates/edit.blade.php` - 양식 편집 UI (44.5KB) +- `mng/routes/web.php` 340-353줄 - 라우트 -| 시나리오 | 예상 결과 | 실제 결과 | 상태 | -|----------|----------|----------|------| -| 마이그레이션 실행 | 4개 테이블 생성 | 4개 테이블 생성 (524ms) | ✅ | -| 문서 생성 API | 201 Created | - | ⏳ | -| 결재 요청 | DRAFT → PENDING | - | ⏳ | -| 결재 승인 | PENDING → APPROVED | - | ⏳ | -| 결재 반려 | PENDING → REJECTED | - | ⏳ | +### 기존 코드 (react) +- `react/src/components/document-system/` - 문서 공통 시스템 +- `react/src/app/[locale]/(protected)/quality/qms/components/documents/` - QMS 검사 문서 +- `react/src/components/production/WorkerScreen/WorkLogContent.tsx` - 작업일지 +- `react/src/components/production/WorkOrders/documents/` - 중간검사 + +### 5130 레거시 +- `5130/instock/fetch_inspection.php` - 수입검사 메인 로더 (21.8KB) +- `5130/instock/i_*.php` - 자재별 수입검사 페이지 (40+) +- `5130/output/viewMidInspect*.php` - 중간검사 성적서 +- `5130/output/viewinspectionJointbar.php` - 조인트바 검사 +- `5130/img/inspection/` - 검사 기준 이미지 (20+) + +### DB 마이그레이션 +- `api/database/migrations/2026_01_26_200000_create_document_templates_table.php` +- `api/database/migrations/2026_01_28_200000_create_documents_table.php` --- -## 9. 자기완결성 점검 결과 +## 11. Phase별 상세 실행 절차 -### 9.1 체크리스트 검증 +> 각 Phase 작업 시 이 섹션을 먼저 읽고 진행한다. + +### 11.1 Phase 1.1 - 기존 document-templates 편집 UI 점검 및 보완 + +**목표**: `mng.sam.kr/document-templates/{id}/edit`에서 수입검사 양식에 필요한 모든 구성요소를 관리할 수 있는지 확인하고 부족한 부분을 보완한다. + +**사전 조건**: 없음 (첫 번째 작업) + +**실행 절차**: + +``` +Step 1: 현재 UI 분석 +├── mng/resources/views/document-templates/edit.blade.php (44.5KB) 읽기 +├── 기존 기능 목록 정리: +│ - 양식 기본정보 (이름, 카테고리, 제목, 회사명) 편집 가능? +│ - 결재라인 (approval_lines) CRUD 가능? +│ - 기본필드 (basic_fields) CRUD 가능? +│ - 섹션 (sections) CRUD 가능? +│ - 섹션 항목 (section_items) CRUD 가능? +│ - 컬럼 (columns) CRUD 가능? +│ - footer_remark_label, footer_judgement_label, footer_judgement_options 편집 가능? +└── 누락된 기능 목록화 + +Step 2: 브라우저에서 실제 동작 확인 +├── https://mng.sam.kr/document-templates 접속 +├── 기존 양식 편집 시도 (or 새 양식 생성 후 편집) +├── 각 탭/섹션별 CRUD 동작 확인 +└── JS 에러, 저장 실패 등 이슈 기록 + +Step 3: 보완 작업 +├── 누락된 CRUD 기능 구현 (Blade + HTMX + Alpine.js) +├── DocumentTemplateController 메서드 보강 +├── 유효성 검증 추가 (FormRequest 패턴) +└── 섹션 항목(section_items)의 drag-drop 정렬 (있는 경우 확인, 없으면 sort_order 수동 관리) + +Step 4: 검증 +├── 새 양식 생성 → 모든 하위 요소 추가 → 저장 → DB 확인 +├── 기존 양식 수정 → 저장 → 정상 반영 확인 +└── 양식 삭제 → 하위 요소 cascade 삭제 확인 +``` + +### 11.2 Phase 1.2 - 5130 수입검사 데이터 분석 + +**목표**: 5130의 자재별 수입검사 파일을 분석하여, 양식 시드 데이터로 변환할 수 있는 구조화된 데이터를 생성한다. + +**상태**: ✅ 완료 (2026-01-31, 경량 분석) + +**분석 결과**: + +#### 라우팅 구조 + +`5130/instock/common/viewJS.php`의 `viewBoardInstock()` 함수가 **item_name(품명) 기준 switch-case**로 개별 검사 페이지(`i_*.php`)를 팝업 호출한다. + +- `fetch_inspection.php` = 데이터 입력 폼 (목록에서 호출) +- `i_*.php` = 검사 성적서 뷰 (viewinspection 버튼에서 호출) +- 총 23개 파일, 품명별 1:1 또는 N:1 매핑 + +#### 자재 → 검사파일 매핑 (23개) + +| 품명 | 파일 | 비고 | +|---|---|---| +| EGI1.55T, EGI1.15T, EGI1.6T | `i_EGI155.php` | 전기아연도금강판 | +| SUS1.55T, SUS1.5T, SUS1.2T | `i_SUSplate.php` | 스테인리스강판 | +| GI0.5T, GI0.45T | `i_GIplate.php` | 아연도금강판 | +| 앵글 | `i_angle.php` | | +| 받침용앵글 | `i_anglebottom.php` | | +| 방화유리 | `i_antifireglass.php` | | +| 절곡코일(EGI) | `i_bendingcoil.php` | spec 앞3자=EGI | +| 베어링부 | `i_bracket.php` | | +| 바이오세라크울96K | `i_cerakwool.php` | | +| 연동제어기 | `i_controller.php` | | +| 화이바원단 | `i_fiber.php` | | +| 내화충진재 | `i_Fireproof_sealings.php` | | +| 내화실 | `i_fireproofWire.php` | | +| 전동개폐기 | `i_motor.php` | | +| 평철 | `i_platesteel.php` | | +| 마환봉 | `i_pole.php` | | +| 각파이프 | `i_recpipe.php` | | +| 감기샤프트 | `i_shaft.php` | | +| 실리카원단 | `i_sillica.php` | | +| 슬랫코일 | `i_slatcoil.php` | | +| 절곡코일(SUS) | `i_SUScoil.php` | spec 앞3자=SUS | +| 와이어원단 | `i_wire.php` | 기본 | +| 와이어원단(대한) | `i_wireDaehan.php` | remarks에 '대한' 포함 시 | + +#### 대표 자재 분석: EGI 1.55T (`i_EGI155.php`) + +| NO | 검사항목 | 검사기준 | 검사방식 | 검사주기 | 데이터 타입 | +|---|---|---|---|---|---| +| 1 | 겉모양 | 사용상 해로울 결함이 없을 것 | 육안검사 | n=3, c=0 | OK/NG 체크 ×3 | +| 2 | 치수-두께 | 두께별 허용범위 (±0.07~±0.12, 4구간) | 체크검사 | n=3, c=0 | 측정값 ×3 | +| 2 | 치수-너비 | 1250 미만: +7/-0 | 체크검사 | n=3, c=0 | 측정값 ×3 | +| 2 | 치수-길이 | 길이별 허용범위 (+10~+20/-0, 3구간) | 체크검사 | n=3, c=0 | 측정값 ×3 | +| 3 | 인장강도 (N/mm²) | 270 이상 | 밀시트 | 입고시 | 단일값 | +| 4 | 연신율 (%) | 두께별 36~38 이상 (3구간) | 밀시트 | 입고시 | 단일값 | +| 5 | 아연 최소 부착량 (g/m²) | 한면 17 이상 | 밀시트 | 입고시 | 단일값 | + +#### 대표 자재 분석: SUS Plate (`i_SUSplate.php`) + +| NO | 검사항목 | 검사기준 | 검사방식 | 검사주기 | 데이터 타입 | +|---|---|---|---|---|---| +| 1 | 겉모양 | 사용상 해로울 결함이 없을 것 | 육안검사 | n=3, c=0 | OK/NG 체크 ×3 | +| 2 | 치수-두께 | 두께별 허용범위 (±0.10~±0.12, 2구간) | 체크검사 | n=3, c=0 | 측정값 ×3 | +| 2 | 치수-너비 | 1250 미만: +7/-0 | 체크검사 | n=3, c=0 | 측정값 ×3 | +| 2 | 치수-길이 | 길이별 허용범위 (+10~+20/-0, 2구간) | 체크검사 | n=3, c=0 | 측정값 ×3 | +| 3 | 항복강도 (N/mm²) | 205 이상 | 밀시트 | 입고시 | 단일값 | +| 4 | 인장강도 (N/mm²) | 520 이상 | 밀시트 | 입고시 | 단일값 | +| 5 | 연신율 (%) | 40 이상 | 밀시트 | 입고시 | 단일값 | +| 6 | 경도 (HV) | 200 이하 | 밀시트 | 입고시 | 단일값 | + +#### 공통 패턴 요약 + +**공통 구조 (모든 자재 동일):** +- **결재**: 담당 / 부서장 (2단계) +- **기본정보**: 품명, 규격(두께×너비×길이), 납품업체/제조업체, 로트번호, 자재번호, 검사일자, 로트크기, 검사자 +- **검사 테이블 컬럼**: NO / 검사항목 / 검사기준 / 검사방식 / 검사주기 / 측정치(n1,n2,n3) / 판정(적/부) +- **Footer**: 부적합 내용 + 종합판정(합격/불합격) +- **판정 로직**: JS 자동 계산 (모든 항목 적→합격, 하나라도 부→불합격) +- **저장**: JSON(`iList` hidden field) → AJAX POST → `insert_iList.php` + +**자재별 차이점:** +- 검사항목 수/종류 (EGI: 5항목 7행, SUS: 6항목 8행) +- 기준값 범위 (두께별 허용 오차, 강도/경도 기준 등) +- 두께 범위 구간 수 (EGI: 4구간, SUS: 2구간) +- 밀시트 항목 차이 (EGI: 인장+연신+아연, SUS: 항복+인장+연신+경도) + +> **결론**: 나머지 21개 자재는 Phase 1.3 시드 데이터 생성 시 개별 분석하면서 병행 진행 + +### 11.3 Phase 1.3 - 수입검사 양식 시드 데이터 생성 + +**실행 절차**: + +``` +Step 1: Seeder 파일 생성 +├── mng/database/seeders/IncomingInspectionTemplateSeeder.php 생성 +├── 1.2에서 정리한 데이터 기반 +└── 주요 자재 10종 양식 생성 (EGI, SUS, GI, Wire, Motor, Angle 등) + +Step 2: 실행 및 검증 +├── php artisan db:seed --class=IncomingInspectionTemplateSeeder +├── mng.sam.kr/document-templates 에서 목록 확인 +└── 각 양식 편집 화면에서 데이터 정합성 확인 +``` + +--- + +## 12. 자기완결성 점검 결과 + +### 12.1 체크리스트 검증 | # | 검증 항목 | 상태 | 비고 | |---|----------|:----:|------| -| 1 | 작업 목적이 명확한가? | ✅ | 1.1 배경 섹션 | -| 2 | 성공 기준이 정의되어 있는가? | ✅ | 8.1 테스트 케이스 | -| 3 | 작업 범위가 구체적인가? | ✅ | 2. 대상 범위 (파일 경로 포함) | -| 4 | 의존성이 명시되어 있는가? | ✅ | 0.1 전제 조건 | -| 5 | 참고 파일 경로가 정확한가? | ✅ | 4. 기존 코드 참조 | -| 6 | 단계별 절차가 실행 가능한가? | ✅ | 5. 작업 절차 | -| 7 | 검증 방법이 명시되어 있는가? | ✅ | 8. 검증 결과 | -| 8 | 모호한 표현이 없는가? | ✅ | 코드 템플릿 제공 | +| 1 | 작업 목적이 명확한가? | ✅ | 섹션 1.1 배경 | +| 2 | 성공 기준이 정의되어 있는가? | ✅ | 각 Phase 작업 항목에 "완료 기준" 컬럼 추가됨 | +| 3 | 작업 범위가 구체적인가? | ✅ | 섹션 3 Phase 1-5 + 섹션 11 상세 절차 | +| 4 | 의존성이 명시되어 있는가? | ✅ | 기존 DB/모델/컨트롤러 현황 + 새 세션 가이드 | +| 5 | 참고 파일 경로가 정확한가? | ✅ | 절대 경로 + 상대 경로 모두 명시 | +| 6 | 단계별 절차가 실행 가능한가? | ✅ | 섹션 11 Phase별 Step-by-step 절차 | +| 7 | 검증 방법이 명시되어 있는가? | ✅ | 각 Phase 완료 기준에 검증 방법 포함 | +| 8 | 모호한 표현이 없는가? | ✅ | 구체적 파일/테이블/컬럼/URL 명시 | -### 9.2 새 세션 시뮬레이션 테스트 +### 12.2 새 세션 시뮬레이션 테스트 | 질문 | 답변 가능 | 참조 섹션 | |------|:--------:|----------| | Q1. 이 작업의 목적은 무엇인가? | ✅ | 1.1 배경 | -| Q2. 어디서부터 시작해야 하는가? | ✅ | 0.4 작업 순서, 5. 작업 절차 | -| Q3. 어떤 파일을 수정해야 하는가? | ✅ | 2. 대상 범위 | -| Q4. 작업 완료 확인 방법은? | ✅ | 8. 검증 결과 | -| Q5. 막혔을 때 참고 문서는? | ✅ | 4. 기존 코드 참조 | +| Q2. 어디서부터 시작해야 하는가? | ✅ | 🚀 새 세션 시작 가이드 + 📍 현재 진행 상태 | +| Q3. 어떤 파일을 수정해야 하는가? | ✅ | 새 세션 가이드 "핵심 파일" + 2.2 + 9. 참고 파일 | +| Q4. 작업 완료 확인 방법은? | ✅ | 각 Phase "완료 기준" 컬럼 | +| Q5. 막혔을 때 참고 문서는? | ✅ | 9. 참고 문서 + 새 세션 가이드 | +| Q6. mng 기술 스택과 로컬 환경은? | ✅ | 새 세션 가이드 "프로젝트 정보" | +| Q7. 모델 관계와 DB 구조는? | ✅ | 새 세션 가이드 "모델 관계 구조" + 2.1 | +| Q8. Phase 1.1의 구체적 첫 단계는? | ✅ | 11.1 상세 실행 절차 | -**결과**: 5/5 통과 → ✅ 자기완결성 확보 +**결과**: 8/8 통과 - 자기완결성 확보 + +### 12.3 보완 이력 + +| 날짜 | 항목 | 원본 | 보완 내용 | +|------|------|------|----------| +| 2026-01-31 | 초기 검증 | - | 5/5 통과 | +| 2026-01-31 | 자기완결성 강화 | 새 세션에서 시작 불가 | 🚀 새 세션 시작 가이드 추가, 절대 경로/기술 스택/모델 코드 인라인, Phase 완료 기준 추가, 섹션 11 상세 실행 절차 추가, 컨펌 대기 목록 해결 항목 반영 | ---