Files
sam-docs/dev/dev_plans/document-system-work-log.md

326 lines
16 KiB
Markdown
Raw Normal View History

# Phase 5.3: 작업일지 폼 구현 계획
> **작성일**: 2026-02-10
> **마스터 문서**: [`document-system-master.md`](./document-system-master.md)
> **상태**: 🔄 진행 중 (3/4+α, mng 상세보기 ✅)
> **선행 조건**: Phase 5.0과 독립 (검사기준서 없음). 병렬 진행 가능
---
## 1. 개요
### 1.1 목적
mng에서 작업일지 양식 템플릿을 정의하고, React 작업자 화면(`/production/worker-screen`)의 작업일지 모달에서 해당 양식을 기반으로 작업 내역을 기록/조회할 수 있도록 한다.
### 1.2 하이브리드 방식
- **양식 정의**: mng 템플릿 시스템 (DocumentTemplate) 활용
- **전용 UI/로직**: React에서 작업일지 전용 컴포넌트로 구현 (검사 성적서와 다른 구조)
- **이유**: 작업일지는 검사 항목표가 아닌, 품목 목록 + 작업 통계 + 특이사항 구조
### 1.3 현재 상태
| 항목 | 상태 | 비고 |
|------|:----:|------|
| React WorkLogContent.tsx | ✅ | 정적 문서, DocumentHeader + 기본정보 + 품목테이블 + 작업내역 + 특이사항 |
| mng 양식 템플릿 | ✅ | WorkLogTemplateSeeder 3종 (스크린:62, 슬랫:63, 절곡:64) |
| WorkLogModal 양식 연동 | ✅ | 공정관리 workLogTemplateId 기반 콘텐츠 분기, processType 폴백 |
| ScreenWorkLogContent 자재 LOT | ✅ | materialLots item_name별 동적 그룹핑 (하드코딩 "내화실" 제거) |
| API 자재 투입 LOT 조회 | ✅ | materialInputLots 엔드포인트 (stock_transactions 기반) |
| API 작업일지 전용 | ✅ | getWorkLogTemplate, getWorkLog, createWorkLog (3개 라우트) |
| 작업 통계 계산 | ✅ | calculateWorkStats() 함수 존재 (완료/진행중/대기 수량) |
| **mng 문서 상세보기** | ✅ | **show.blade.php 작업일지 전용 섹션 (템플릿 컬럼 기반 동적 렌더링)** |
| **mng bf_ backfill 분기** | ✅ | **resolveAndBackfillBasicFields: 작업일지=label 기반, 검사=field_key 기반** |
| **mng 재단 알고리즘 (PHP)** | ✅ | **React calculateCutSize 동일 구현. 실리카/와이어/화이바 원단별 설정** |
| **mng 개소별 투입자재 LOT** | ✅ | **work_order_material_inputs → stock_lots JOIN, 개소별 lot_no 매핑** |
| **mng 취소 트랜잭션 상쇄** | ✅ | **work_order_input + work_order_input_cancel 합산 → 순수 투입량** |
### 1.4 성공 기준
1. mng에서 작업일지 양식 정의 가능 (기본필드, 결재라인)
2. React에서 WorkOrder 선택 시 작업일지 자동생성 또는 수동생성
3. 품목 목록(WorkOrderItem[])이 자동으로 테이블에 매핑
4. 작업 통계(지시수량/완료수량/진행률) 자동 계산
5. 특이사항 입력/저장 가능
---
## 2. 데이터 흐름
```
WorkOrder (작업지시)
├─ work_order_no: "KD-WO-260210-01"
├─ process_id → Process (공정: 스크린/슬랫/절곡)
├─ sales_order_id → Order (수주)
│ ├─ client_name: "발주처명"
│ └─ site_name: "현장명"
└─ items: WorkOrderItem[]
├─ [0] item_name="와이어 스크린", quantity=2, status="completed"
├─ [1] item_name="메쉬 스크린", quantity=4, status="in_progress"
└─ [N] ...
작업일지 생성:
Document (작업일지 1건 / 작업지시 1건)
├─ template_id → 작업일지 양식
├─ linkable_type = 'WorkOrder'
├─ linkable_id = work_order.id
├─ status: DRAFT → PENDING → APPROVED
└─ document_data (EAV)
├─ 기본필드: 발주처, 현장명, 작업일자, LOT NO, 납기일, 작업지시번호
├─ 품목데이터: 행(row) = WorkOrderItem별
│ ├─ r{행}_item_name = "와이어 스크린"
│ ├─ r{행}_floor_code = "1F-A"
│ ├─ r{행}_specification = "W7400×H2950"
│ ├─ r{행}_quantity = "2"
│ └─ r{행}_status = "completed"
├─ 작업통계: order_qty, completed_qty, in_progress_qty, waiting_qty, progress
└─ 특이사항: remarks
```
### 2.1 mng 상세보기 데이터 흐름 (구현 완료)
```
DocumentController::show($id)
├─ Document + relations 로드
├─ linkable_type === 'work_order' ?
│ ├─ workOrderItems (work_order_items, options JSON decode)
│ ├─ workOrder (work_orders)
│ ├─ salesOrder (orders, via work_order.sales_order_id)
│ ├─ materialInputLots (stock_transactions: work_order_input + cancel 상쇄)
│ │ └─ 순수 투입량 = SUM(qty) where qty < 0 abs()
│ └─ itemLotMap (work_order_material_inputs → stock_lots JOIN)
│ └─ groupBy(work_order_item_id) → lot_no 문자열
├─ resolveAndBackfillBasicFields($document)
│ ├─ isWorkLog = sections 없음?
│ ├─ 작업일지 → buildWorkLogResolveMap (label 기반: 발주처, 현장명, 수주일 등)
│ └─ 검사 문서 → buildInspectionResolveMap (field_key 기반: product_name 등)
└─ view('documents.show', [...])
show.blade.php (작업일지 전용 섹션)
├─ 템플릿 컬럼 기반 동적 테이블
│ ├─ 헤더: simple 컬럼 = 1행, complex 컬럼 = colspan + sub_labels 2행
│ ├─ 데이터: $getCellValue (label 기반 매핑), $getSubCellValue (sub_label 매핑)
│ └─ PHP $calculateCutSize (재단 알고리즘: FABRIC_CONFIG 원단별)
├─ 작업 통계 (지시수량/완료/진행중/대기/진행률)
├─ 투입 자재 LOT 테이블 (materialInputLots)
└─ 비고 (remarks)
```
### 2.2 중간검사 문서와의 차이
| 항목 | 중간검사 | 작업일지 |
|------|---------|---------|
| 단위 | 작업지시 (내부 개소별 행) | 작업지시 (1:1) |
| 테이블 내용 | 검사항목 + 측정값 + 판정 | 품목 목록 + 상태 |
| 통계 | 적합/부적합 비율 | 완료/진행중/대기 수량 |
| Footer | 부적합 내용 + 종합판정 | 특이사항 |
| 결재 | 작성→검토→승인 (3단계) | 작성→확인 (2단계) |
---
## 3. 작업 항목
| # | 작업 | 상태 | 완료 기준 | 비고 |
|---|------|:----:|----------|------|
| 5.3.1 | mng 작업일지 양식 시더 생성 | ✅ | WorkLogTemplateSeeder 3종. 스크린(62)/슬랫(63)/절곡(64). 공정별 결재+기본필드+컬럼 | 검사 기준서 섹션 없음, 판정 없음 |
| 5.3.2 | mng 양식 편집 검증 | ✅ | 작업일지 양식 edit → 미리보기 정상 동작 확인 (코드 레벨 검증) | 빈 sections/judgement 안전 처리 |
| 5.3.3 | API 작업일지 생성/저장 | ✅ | getWorkLogTemplate, getWorkLog, createWorkLog 구현. 3개 라우트 추가 | EAV 저장, 기본필드 자동매핑, 작업통계 자동계산 |
| 5.3.4 | React WorkLogContent 양식 기반 전환 | ⏳ | 양식의 기본필드/결재라인을 API에서 받아 렌더링. 품목테이블/통계는 전용 로직 유지 | 하이브리드 |
### mng 작업일지 상세보기 (추가 작업, ✅ 완료)
| # | 작업 | 상태 | 설명 |
|---|------|:----:|------|
| α.1 | resolveAndBackfillBasicFields 작업일지/검사 분기 | ✅ | 섹션 유무로 판별. 작업일지=label 기반(발주처, 현장명 등), 검사=field_key 기반(product_name 등) |
| α.2 | show() 메서드 데이터 로딩 확장 | ✅ | workOrder, salesOrder, materialInputLots, itemLotMap 변수 추가 |
| α.3 | 템플릿 컬럼 기반 동적 테이블 렌더링 | ✅ | template.columns 구조대로 헤더/데이터 렌더링. complex 컬럼(제작사이즈, 규격매수) sub_labels 지원 |
| α.4 | PHP 재단 알고리즘 (calculateCutSize) | ✅ | React 동일 구현. FABRIC_CONFIG(실리카1220/와이어1100/화이바1100), 나머지높이+규격(매수) 자동계산 |
| α.5 | 개소별 투입자재 LOT 매핑 | ✅ | work_order_material_inputs → stock_lots JOIN. 입고 LOT NO 컬럼에 개소별 lot_no 표시 |
| α.6 | 투입자재 취소 트랜잭션 상쇄 | ✅ | stock_transactions에서 work_order_input(OUT,음수) + work_order_input_cancel(IN,양수) 합산 |
| α.7 | 작업통계/자재LOT/비고 섹션 | ✅ | 지시수량/완료/진행중/대기 통계, 투입 자재 LOT 테이블, 비고 표시 |
---
## 4. 작업일지 구조 (React 현재 기준)
### 4.1 WorkLogContent.tsx 구조
```
작업일지 문서
├─ DocumentHeader
│ ├─ 로고 (케이디산업)
│ ├─ 제목: "작업일지"
│ └─ 결재라인: 작성 / 확인
├─ 기본 정보 테이블
│ ├─ 발주처 / 현장명
│ ├─ 작업일자 / LOT NO
│ └─ 납기일 / 작업지시번호
├─ 품목 테이블
│ ├─ No | 품목명 | 층-부호 | 규격 | 수량 | 상태
│ ├─ [1] 와이어 스크린 | 1F-A | W7400×H2950 | 2 | 완료
│ ├─ [2] 메쉬 스크린 | 2F-B | W5200×H3100 | 4 | 작업중
│ └─ [N] ...
├─ 작업내역 (공정별)
│ ├─ 지시수량: 50
│ ├─ 완료수량: 30
│ ├─ 진행률: 60%
│ └─ 대기: 10 / 작업중: 10 / 완료: 30
└─ 특이사항
└─ (자유 텍스트 입력)
```
### 4.2 작업 통계 계산 (기존 로직)
```typescript
function calculateWorkStats(items: WorkOrderItem[]): WorkStats {
return {
orderQty: items.length, // 전체 개소 수
completedQty: items.filter(i => i.status === 'completed').length,
inProgressQty: items.filter(i => i.status === 'in_progress').length,
waitingQty: items.filter(i => i.status === 'waiting').length,
progress: (completedQty / orderQty) * 100
}
}
```
---
## 5. 양식 시더 구조 (구현 완료 - 3종)
```php
// WorkLogTemplateSeeder - 공정별 3종
// 스크린(ID:62): 결재 3단계(작성/검토/승인), 규격매수 컬럼(기준폭/900/800/600/400/300)
// 슬랫(ID:63): 결재 4단계(작성/승인×3), 방화유리/조인트바/코일 컬럼
// 절곡(ID:64): 결재 4단계(작성/승인×3), 유형명/세부품명/재질/길이규격 컬럼
//
// 공통: 기본필드 9개(신청업체4+신청내용5), 판정 없음, 비고만
[
'name' => '스크린 작업일지', // or 슬랫/절곡
'category' => '생산/작업일지',
'title' => '작업일지 (스크린)',
'company_name' => '케이디산업',
'footer_remark_label' => '비고',
'footer_judgement_label' => '', // NOT NULL 컬럼 → 빈문자열
'footer_judgement_options' => [], // 작업일지는 종합판정 없음
'approval_lines' => [
['name' => '작성', 'dept' => '생산', 'role' => '담당자', 'sort_order' => 1],
['name' => '확인', 'dept' => '생산', 'role' => '관리자', 'sort_order' => 2],
],
'basic_fields' => [
['label' => '발주처', 'field_type' => 'text'],
['label' => '현장명', 'field_type' => 'text'],
['label' => '작업일자', 'field_type' => 'date'],
['label' => 'LOT NO', 'field_type' => 'text'],
['label' => '납기일', 'field_type' => 'date'],
['label' => '작업지시번호', 'field_type' => 'text'],
],
// 섹션 없음 (작업일지는 검사 기준서가 필요 없음)
'sections' => [],
// 컬럼: 품목 테이블용 (React에서 직접 렌더링하므로 참조용)
'columns' => [
['label' => 'No', 'column_type' => 'text', 'width' => '40px'],
['label' => '품목명', 'column_type' => 'text', 'width' => '150px'],
['label' => '층-부호', 'column_type' => 'text', 'width' => '80px'],
['label' => '규격', 'column_type' => 'text', 'width' => '150px'],
['label' => '수량', 'column_type' => 'text', 'width' => '60px'],
['label' => '상태', 'column_type' => 'select', 'width' => '80px'],
],
]
```
---
## 6. 하이브리드 구현 전략
### mng 템플릿에서 관리하는 것
- 결재라인 (작성/확인 or 커스텀)
- 기본필드 (발주처, 현장명, 작업일자 등)
- 회사명, 문서 제목
### React 전용 로직으로 유지하는 것
- 품목 테이블 (WorkOrderItem[] 기반 동적 생성)
- 작업 통계 계산 (calculateWorkStats)
- 상태 배지 (완료/작업중/대기 → 색상 표시)
- 특이사항 입력 UI
### API 요청 흐름
```
1. 작업일지 생성 요청
POST /api/v1/work-orders/{id}/create-work-log
→ Document 생성 (template_id, linkable → WorkOrder)
→ 기본필드 자동매핑 (발주처, 현장명, LOT NO 등)
2. 작업일지 데이터 저장
PUT /api/v1/documents/{id}
Body: {
basic_data: { ... }, // 기본필드 (양식 기반)
table_data: [ ... ], // 품목 테이블 (전용 로직)
stats: { ... }, // 작업 통계 (자동 계산)
remarks: "특이사항" // 자유 텍스트
}
3. 작업일지 조회
GET /api/v1/documents/{id}
→ 양식 JSON + 저장된 데이터 반환
```
---
## 7. 핵심 파일 경로
### mng
| 파일 | 용도 |
|------|------|
| `mng/database/seeders/WorkLogTemplateSeeder.php` | ✅ 3종 생성 (62/63/64) |
| `mng/app/Http/Controllers/DocumentController.php` | ✅ show() 작업일지 데이터 로딩, resolveAndBackfillBasicFields 분기, buildWorkLogResolveMap |
| `mng/resources/views/documents/show.blade.php` | ✅ 작업일지 전용 섹션 (템플릿 컬럼 동적 렌더링, PHP 재단 알고리즘, 통계, 자재LOT, 비고) |
### react
| 파일 | 용도 |
|------|------|
| `react/src/components/production/WorkerScreen/WorkLogModal.tsx` | ✅ 작업일지 모달 (공정관리 양식 연동) |
| `react/src/components/production/WorkerScreen/WorkLogContent.tsx` | 작업일지 범용 (~280행) |
| `react/src/components/production/WorkOrders/documents/ScreenWorkLogContent.tsx` | ✅ 스크린 작업일지 (자재 LOT 동적화) |
| `react/src/components/production/WorkOrders/documents/SlatWorkLogContent.tsx` | 슬랫 작업일지 |
| `react/src/components/production/WorkOrders/documents/BendingWorkLogContent.tsx` | 절곡 작업일지 |
| `react/src/components/production/WorkerScreen/actions.ts` | API 호출 |
| `react/src/components/document-system/viewer/DocumentViewer.tsx` | 문서 뷰어 |
### api
| 파일 | 용도 |
|------|------|
| `api/app/Services/WorkOrderService.php` | ✅ getWorkLogTemplate, getWorkLog, createWorkLog |
| `api/app/Http/Controllers/Api/V1/WorkOrderController.php` | ✅ 작업일지 3개 엔드포인트 |
| `api/routes/api/v1/production.php` | ✅ work-log-template, work-log 라우트 |
| `api/app/Models/Production/WorkOrder.php` | ✅ documents() MorphMany 관계 |
---
## 8. 변경 이력
| 날짜 | 내용 |
|------|------|
| 2026-02-10 | Phase 5.3 계획 문서 신규 생성 |
| 2026-02-11 | 5.3.1 완료: WorkLogTemplateSeeder 3종 생성 (스크린62/슬랫63/절곡64). 범용(61) 삭제. React 공정별 코드 분석 기반 구조 반영. 판정 없음 확정 |
| 2026-02-11 | WorkLogModal 공정관리 양식 연동: workLogTemplateId/Name prop 추가, resolveProcessTypeFromTemplate() |
| 2026-02-11 | ScreenWorkLogContent 자재 LOT 동적화: "내화실 입고 LOT NO" → materialLots item_name별 그룹핑 |
| 2026-02-11 | 결정: 자재 LOT 역할 분리 — 개소별 품목=작업내역 테이블, 공용 자재=자재 투입 시스템 (예외 필드 없음) |
| 2026-02-12 | 5.3.2 완료: mng 양식 편집/미리보기 코드 레벨 검증 (빈 sections/judgement 안전 처리 확인) |
| 2026-02-12 | 5.3.3 완료: API 작업일지 3개 엔드포인트 구현 (getWorkLogTemplate, getWorkLog, createWorkLog). 기본필드 자동매핑, 작업통계 자동계산, EAV 저장 |
| 2026-02-12 | MNG α.1~7 완료: 작업일지 상세보기 전면 구현 |
| 2026-02-12 | DocumentController: resolveAndBackfillBasicFields 작업일지(label)/검사(field_key) 분기. buildWorkLogResolveMap, buildInspectionResolveMap 추가 |
| 2026-02-12 | show.blade.php: 템플릿 컬럼 기반 동적 테이블 (complex 컬럼 sub_labels 지원), PHP 재단 알고리즘 (React calculateCutSize 동일) |
| 2026-02-12 | show(): workOrder, salesOrder, materialInputLots(취소 상쇄), itemLotMap(개소별 LOT) 변수 추가 |
| 2026-02-12 | 자재 투입 방식 변경 요청 기록 (수량 입력 → LOT 선택 방식, 미착수) |
---
*이 문서는 /plan 스킬로 생성되었습니다.*