From d0de02ad8e1f8ace24a7ded15dcc573dd4689f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Thu, 19 Mar 2026 08:21:35 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20[material]=20=EB=B6=80=EC=A0=81?= =?UTF-8?q?=ED=95=A9=EA=B4=80=EB=A6=AC=20=EA=B8=B0=ED=9A=8D=EC=84=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 수주서 형식 기반 5개 섹션 구성 - 불량내역, 원인분석/처리방안, 자재내역/비용이 핵심 - DB 스키마, API 엔드포인트, React 파일 구조 포함 - Phase 1~3 단계별 구현 범위 정의 --- INDEX.md | 1 + .../nonconforming-management-plan.md | 440 ++++++++++++++++++ 2 files changed, 441 insertions(+) create mode 100644 dev/dev_plans/nonconforming-management-plan.md diff --git a/INDEX.md b/INDEX.md index 5e28eb2..981b96c 100644 --- a/INDEX.md +++ b/INDEX.md @@ -39,6 +39,7 @@ | 입고×수입검사 연동 | `dev/dev_plans/receiving-inspection-integration-plan.md` | 입고등록 수입검사 27종 매핑, Phase 1~3 계획 | | 운영 배포 | `dev/dev_plans/production-deployment-plan.md` | 배포 계획 | | QMS 점검표 | `dev/dev_plans/qms-checklist-template-plan.md` | 점검표 템플릿 관리 기능 | +| 부적합관리 | `dev/dev_plans/nonconforming-management-plan.md` | 자재관리 부적합관리 기획 (불량내역/처리방법/자재비용) | | 서버 운영 | `dev/deploys/ops-manual/README.md` | 서버 운영 매뉴얼 | | 서버 접근/백업 | `system/server-access-management.md` | 계정, 권한, 백업, 리플리케이션 | | 이관 작업 | `system/migration-status.md` | MNG→API+React 이관 현황, 우선순위, 로드맵 | diff --git a/dev/dev_plans/nonconforming-management-plan.md b/dev/dev_plans/nonconforming-management-plan.md new file mode 100644 index 0000000..7a58ab3 --- /dev/null +++ b/dev/dev_plans/nonconforming-management-plan.md @@ -0,0 +1,440 @@ +# 자재관리 - 부적합관리 기획서 + +> **작성일**: 2026-03-19 +> **상태**: 기획 중 +> **담당**: R&D실 +> **라우트**: `/material/nonconforming-management` + +--- + +## 1. 개요 + +### 1.1 목적 + +제조 공정 및 자재 입고 과정에서 발생하는 품질 부적합(불량)을 체계적으로 기록하고, 원인 분석 → 시정 조치 → 비용 산정까지 일괄 관리하는 기능을 제공한다. + +### 1.2 핵심 컨셉 + +SAM 수주서의 화면 구조(FormSection 기반 섹션 레이아웃 + 하단 품목 테이블)를 그대로 따르되, **3가지 핵심 데이터**에 집중한다: + +``` +┌─────────────────────────────────────────────┐ +│ 1. 불량 내역 — 무엇이, 언제, 어디서 발생 │ +│ 2. 처리 방법 — 원인 분석 + 시정/개선 조치 │ +│ 3. 자재 내역과 비용 — 손실 비용 산정 │ +└─────────────────────────────────────────────┘ +``` + +### 1.3 참고 + +- 타사 양식: "품질불량(부적합) 원인분석 및 개선 대책 보고서" +- SAM 내부: 수주 등록(`OrderRegistration.tsx`) 구조 준용 + +--- + +## 2. 화면 구성 + +> SAM 수주서처럼 `IntegratedDetailTemplate` + `FormSection` 패턴을 따른다. +> 목록 → 등록/상세 → 수정 모드를 `?mode=new|edit` 쿼리파라미터로 분기한다. + +### 2.1 목록 페이지 + +`/material/nonconforming-management` + +| 필터 | 타입 | 설명 | +|------|------|------| +| 기간 | DateRangePicker | 발생일 기준 | +| 상태 | Select | 전체/접수/분석중/조치완료/종결 | +| 부적합 유형 | Select | 자재불량/공정불량/시공불량/기타 | +| 검색 | Input | 부적합번호, 현장명, 품목명 | + +| 순서 | 컬럼 | 설명 | +|------|------|------| +| 1 | 체크박스 | 행 선택 | +| 2 | No. | 자동 번호 | +| 3 | 부적합번호 | `NC-YYYYMMDD-NNN` | +| 4 | 부적합 유형 | 자재불량/공정불량/시공불량/기타 | +| 5 | 현장명 | 발생 현장 | +| 6 | 품목명 | 관련 품목 | +| 7 | 발생일 | 불량 발생 날짜 | +| 8 | 비용 합계 | 자재+운송+시공+기타 | +| 9 | 상태 | Badge (접수/분석중/조치완료/종결) | +| 10 | 등록자 | 작성자명 | + +### 2.2 등록/상세 페이지 — 섹션 구성 + +`/material/nonconforming-management?mode=new` +`/material/nonconforming-management/{id}` +`/material/nonconforming-management/{id}?mode=edit` + +수주서와 동일한 패턴으로 5개 FormSection을 배치한다: + +``` +┌─ IntegratedDetailTemplate ──────────────────────────────────┐ +│ │ +│ [섹션 1] 기본 정보 │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 부적합번호 │ 상태(Badge) │ │ +│ │ 발생일 │ 확인일 │ 부적합 유형 │ │ +│ │ 현장명 (🔍 수주 연결) │ 부서 │ │ +│ │ 등록자 │ 관련 직원 │ 관련 수주번호 │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ [섹션 2] 불량 내역 ★핵심 │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 관련 품목 (🔍 품목 검색) │ 불량 수량 │ 단위 │ │ +│ │ 불량 상세 설명 (Textarea) │ │ +│ │ 첨부파일 (이미지/문서 다중 업로드) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ [섹션 3] 원인 분석 및 처리 방안 ★핵심 │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 불량 발생 원인 및 분석 (Textarea) │ │ +│ │ 처리 방안 및 개선 사항 (Textarea) │ │ +│ │ 시정 조치 완료일 │ 조치 담당자 │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ [섹션 4] 자재 내역 및 비용 ★핵심 │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 원자재 및 자재 소요량 설명 (Textarea) │ │ +│ │ ┌─ 자재 상세 테이블 ───────────────────────────┐ │ │ +│ │ │ No │ 품목명 │ 규격 │ 수량│단가│금액│비고 │ │ │ +│ │ │ 1 │ ... │ ... │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ │ [+추가] │ │ │ +│ │ └──────────────────────────────────────────────┘ │ │ +│ │ ┌─ 비용 요약 ─────────────────────────────────┐ │ │ +│ │ │ 자재비용 │ 운송비용 │ 시공비용 │ 기타 │ 합계 │ │ │ +│ │ └──────────────────────────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ [섹션 5] 비고 │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 비고 (Textarea) │ │ +│ │ 도면 저장 위치 (Input) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ── sticky bottom bar ────────────────────────────────── │ +│ │ [취소] [저장] / [결재상신] │ │ +│ ──────────────────────────────────────────────────────── │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 2.3 상태 워크플로우 + +``` +접수(RECEIVED) → 분석중(ANALYZING) → 조치완료(RESOLVED) → 종결(CLOSED) + ↗ + → 결재상신 → 승인 시 종결 +``` + +| 상태 | 설명 | 액션 | +|------|------|------| +| `RECEIVED` | 부적합 접수 (초기 상태) | 등록 시 자동 | +| `ANALYZING` | 원인 분석 중 | 분석 시작 클릭 | +| `RESOLVED` | 시정 조치 완료 | 조치 완료 클릭 | +| `CLOSED` | 종결 (결재 승인 후) | 결재 승인 시 자동 | + +--- + +## 3. 데이터 모델 + +### 3.1 nonconforming_reports (부적합 보고서) + +> `api` 프로젝트에서 마이그레이션 관리 (공용 테이블) + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| `id` | bigint PK | | +| `tenant_id` | bigint FK | 테넌트 | +| `nc_number` | varchar(30) | 부적합번호 `NC-YYYYMMDD-NNN` | +| `status` | varchar(20) | RECEIVED/ANALYZING/RESOLVED/CLOSED | +| `nc_type` | varchar(20) | 부적합 유형 (material/process/construction/other) | +| `occurred_at` | date | 발생일 | +| `confirmed_at` | date | 불량 확인일 | +| `site_name` | varchar(100) | 현장명 | +| `department_id` | bigint FK nullable | 부서 | +| `order_id` | bigint FK nullable | 관련 수주 | +| `item_id` | bigint FK nullable | 관련 품목 | +| `defect_quantity` | decimal(10,2) nullable | 불량 수량 | +| `unit` | varchar(20) nullable | 단위 | +| `defect_description` | text nullable | 불량 상세 설명 | +| `cause_analysis` | text nullable | 원인 분석 | +| `corrective_action` | text nullable | 처리 방안 및 개선 사항 | +| `action_completed_at` | date nullable | 조치 완료일 | +| `action_manager_id` | bigint FK nullable | 조치 담당자 | +| `related_employee_id` | bigint FK nullable | 관련 직원 | +| `material_cost` | decimal(12,0) default 0 | 자재 비용 | +| `shipping_cost` | decimal(12,0) default 0 | 운송 비용 | +| `construction_cost` | decimal(12,0) default 0 | 시공 비용 | +| `other_cost` | decimal(12,0) default 0 | 기타 비용 | +| `total_cost` | decimal(12,0) default 0 | 비용 합계 (자동 계산) | +| `remarks` | text nullable | 비고 | +| `drawing_location` | varchar(255) nullable | 도면 저장 위치 | +| `options` | json nullable | 확장 속성 | +| `created_by` | bigint FK | 등록자 | +| `updated_by` | bigint FK nullable | | +| `deleted_by` | bigint FK nullable | | +| `created_at` | timestamp | | +| `updated_at` | timestamp | | +| `deleted_at` | timestamp nullable | 소프트 삭제 | + +### 3.2 nonconforming_report_items (부적합 자재 상세) + +> 수주의 `order_items`처럼 부적합 보고서에 연결된 자재 내역 + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| `id` | bigint PK | | +| `tenant_id` | bigint FK | 테넌트 | +| `nonconforming_report_id` | bigint FK | 부적합 보고서 | +| `item_id` | bigint FK nullable | 품목 마스터 연결 | +| `item_name` | varchar(100) | 품목명 (수동 입력 허용) | +| `specification` | varchar(100) nullable | 규격/사양 | +| `quantity` | decimal(10,2) default 0 | 수량 | +| `unit_price` | decimal(12,0) default 0 | 단가 | +| `amount` | decimal(12,0) default 0 | 금액 (수량 x 단가) | +| `sort_order` | int default 0 | 정렬 순서 | +| `remarks` | varchar(255) nullable | 비고 | +| `options` | json nullable | 확장 속성 | +| `created_at` | timestamp | | +| `updated_at` | timestamp | | + +### 3.3 첨부파일 + +기존 SAM 파일 시스템(`files` 테이블 polymorphic) 활용: + +```php +// NonconformingReport 모델 +public function files(): MorphMany +{ + return $this->morphMany(File::class, 'fileable'); +} +``` + +--- + +## 4. API 엔드포인트 + +> prefix: `/api/v1/material/nonconforming-reports` + +| Method | Path | 설명 | +|--------|------|------| +| GET | `/` | 목록 조회 (필터/검색/페이지네이션) | +| GET | `/stats` | 상태별 통계 | +| GET | `/{id}` | 단건 조회 (items, files 포함) | +| POST | `/` | 등록 (items 포함 일괄 저장) | +| PUT | `/{id}` | 수정 (items sync) | +| DELETE | `/{id}` | 소프트 삭제 | +| PATCH | `/{id}/status` | 상태 변경 | +| POST | `/{id}/files` | 첨부파일 업로드 | +| DELETE | `/{id}/files/{fileId}` | 첨부파일 삭제 | + +### 4.1 등록 요청 예시 + +```json +{ + "nc_type": "material", + "occurred_at": "2026-03-19", + "confirmed_at": "2026-03-19", + "site_name": "강남 현장", + "department_id": 3, + "order_id": 152, + "item_id": 45, + "defect_quantity": 10, + "unit": "EA", + "defect_description": "V-CUT 불량 - 절단면 틀어짐", + "cause_analysis": "절단기 날 마모로 인한 정밀도 저하", + "corrective_action": "절단기 날 교체 및 정밀도 재검증", + "material_cost": 150000, + "shipping_cost": 30000, + "construction_cost": 0, + "other_cost": 0, + "drawing_location": "nas2dual/도면/2026/03", + "related_employee_id": 12, + "remarks": "", + "items": [ + { + "item_id": 45, + "item_name": "알루미늄 프레임 50T", + "specification": "50 x 3000mm", + "quantity": 10, + "unit_price": 15000, + "remarks": "전량 폐기" + } + ] +} +``` + +### 4.2 목록 응답 예시 + +```json +{ + "data": [ + { + "id": 1, + "nc_number": "NC-20260319-001", + "nc_type": "material", + "nc_type_label": "자재불량", + "site_name": "강남 현장", + "item_name": "알루미늄 프레임 50T", + "occurred_at": "2026-03-19", + "total_cost": 180000, + "status": "RECEIVED", + "status_label": "접수", + "creator_name": "김보곤" + } + ], + "meta": { "current_page": 1, "total": 15 } +} +``` + +--- + +## 5. 비즈니스 규칙 + +### 5.1 채번 규칙 + +| 항목 | 규칙 | +|------|------| +| 형식 | `NC-YYYYMMDD-NNN` | +| 예시 | `NC-20260319-001` | +| 리셋 | 일자별 순번 리셋 | +| 테넌트 | 테넌트별 독립 채번 | + +### 5.2 비용 자동 계산 + +``` +total_cost = material_cost + shipping_cost + construction_cost + other_cost +material_cost = SUM(items[].amount) (자재 상세 테이블 합계로 자동 산정) +``` + +- 자재 상세 테이블의 금액 합계가 변경되면 `material_cost` 자동 갱신 +- `total_cost`는 4개 비용 항목의 합계로 자동 계산 (프론트/백엔드 양쪽) + +### 5.3 상태 전이 규칙 + +| 현재 상태 | 가능한 전이 | 조건 | +|----------|-----------|------| +| RECEIVED | → ANALYZING | — | +| ANALYZING | → RESOLVED | `cause_analysis` + `corrective_action` 필수 | +| RESOLVED | → CLOSED | 결재 승인 시 | +| CLOSED | (변경 불가) | 종결 후 수정 불가 (열람만) | + +### 5.4 수주 연결 + +- 현장명 입력 시 기존 수주를 검색하여 연결 가능 (선택적) +- 수주 연결 시 현장명, 거래처 등 기본 정보 자동 채움 +- 연결 없이도 수동 입력 허용 + +### 5.5 결재 연동 + +- SAM 기존 결재 시스템(`approval_requests` 테이블)과 연동 +- "결재상신" 버튼으로 결재 요청 생성 +- 결재 승인 시 상태가 `CLOSED`로 자동 전환 + +--- + +## 6. 구현 범위 (Phase) + +### Phase 1 — 기본 CRUD + 비용 산정 + +- [ ] DB 마이그레이션 (`nonconforming_reports`, `nonconforming_report_items`) +- [ ] API: Controller, Service, Model, FormRequest +- [ ] React: 목록 페이지 (필터, 검색, 페이지네이션) +- [ ] React: 등록/수정 페이지 (5개 섹션) +- [ ] React: 자재 상세 테이블 (추가/삭제/금액 자동 계산) +- [ ] React: 비용 요약 (4항목 + 합계) +- [ ] 첨부파일 업로드 (기존 File 시스템 활용) + +### Phase 2 — 워크플로우 + 결재 + +- [ ] 상태 워크플로우 (RECEIVED → ANALYZING → RESOLVED → CLOSED) +- [ ] 결재 시스템 연동 (결재상신 → 승인 시 종결) +- [ ] 상태별 수정 제한 (CLOSED 시 읽기 전용) + +### Phase 3 — 통계 + 연동 + +- [ ] 대시보드 통계 카드 (월별 부적합 건수, 비용 추이) +- [ ] 수주 연결 강화 (수주 상세에서 부적합 이력 조회) +- [ ] 엑셀 Export +- [ ] 인쇄용 보고서 (DomPDF) + +--- + +## 7. 기술 설계 요약 + +### 7.1 API 파일 구조 + +``` +api/app/ +├── Http/Controllers/Api/V1/Material/ +│ └── NonconformingReportController.php +├── Services/Material/ +│ └── NonconformingReportService.php +├── Models/Material/ +│ ├── NonconformingReport.php +│ └── NonconformingReportItem.php +├── Http/Requests/Material/ +│ ├── StoreNonconformingReportRequest.php +│ └── UpdateNonconformingReportRequest.php +└── database/migrations/ + ├── xxxx_create_nonconforming_reports_table.php + └── xxxx_create_nonconforming_report_items_table.php +``` + +### 7.2 React 파일 구조 + +``` +react/src/ +├── app/[locale]/(protected)/material/ +│ └── nonconforming-management/ +│ ├── page.tsx ← 목록 + mode 분기 +│ └── [id]/page.tsx ← 상세/수정 +├── components/material/nonconforming/ +│ ├── NonconformingList.tsx ← 목록 +│ ├── NonconformingForm.tsx ← 등록/수정 폼 (5개 FormSection) +│ ├── NonconformingDetail.tsx ← 상세 뷰 +│ ├── NonconformingItemTable.tsx ← 자재 상세 테이블 +│ ├── CostSummary.tsx ← 비용 요약 (4항목+합계) +│ └── actions.ts ← Server Actions +``` + +### 7.3 모델 주요 설정 + +```php +// NonconformingReport.php +class NonconformingReport extends Model +{ + use Auditable, BelongsToTenant, ModelTrait, SoftDeletes; + + protected $casts = [ + 'options' => 'array', + 'occurred_at' => 'date', + 'confirmed_at' => 'date', + 'action_completed_at' => 'date', + 'material_cost' => 'integer', + 'shipping_cost' => 'integer', + 'construction_cost' => 'integer', + 'other_cost' => 'integer', + 'total_cost' => 'integer', + ]; + + public function items(): HasMany { ... } + public function order(): BelongsTo { ... } + public function item(): BelongsTo { ... } + public function files(): MorphMany { ... } + public function department(): BelongsTo { ... } +} +``` + +--- + +## 관련 문서 + +- `rules/item-policy.md` — 품목 정책 +- `features/quotes/README.md` — 견적/수주 시스템 +- `system/database/production.md` — 생산/자재/품질 DB + +--- + +**최종 업데이트**: 2026-03-19