2026-03-19 08:21:35 +09:00
|
|
|
# 자재관리 - 부적합관리 기획서
|
|
|
|
|
|
|
|
|
|
> **작성일**: 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`로 자동 전환
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2026-03-19 08:29:51 +09:00
|
|
|
## 6. 단계별 구현 계획
|
|
|
|
|
|
|
|
|
|
> **원칙**: 각 Phase에서 API를 먼저 완성한 후 프론트엔드 개발을 시작한다.
|
|
|
|
|
> API 완료 시점에 프론트엔드 개발자에게 API 명세를 전달한다.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Phase 1: API 완성 → FE 개발 (병행 가능)
|
|
|
|
|
Phase 2: API 완성 → FE 개발 (병행 가능)
|
|
|
|
|
Phase 3: API 완성 → FE 개발 (병행 가능)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
2026-03-19 08:21:35 +09:00
|
|
|
|
|
|
|
|
### Phase 1 — 기본 CRUD + 비용 산정
|
|
|
|
|
|
2026-03-19 08:29:51 +09:00
|
|
|
> 목표: 부적합 보고서 등록/조회/수정/삭제 + 자재 내역 + 비용 계산
|
|
|
|
|
|
|
|
|
|
#### Phase 1-A: API (sam/api)
|
|
|
|
|
|
|
|
|
|
| 순서 | 작업 | 산출물 |
|
|
|
|
|
|------|------|--------|
|
|
|
|
|
| 1 | DB 마이그레이션 | `nonconforming_reports`, `nonconforming_report_items` 테이블 |
|
|
|
|
|
| 2 | Model 생성 | `NonconformingReport`, `NonconformingReportItem` (관계, cast, scope) |
|
|
|
|
|
| 3 | FormRequest 생성 | `StoreNonconformingReportRequest`, `UpdateNonconformingReportRequest` |
|
|
|
|
|
| 4 | Service 생성 | `NonconformingReportService` — CRUD + 채번 + 비용 자동 계산 |
|
|
|
|
|
| 5 | Controller 생성 | `NonconformingReportController` — REST 9개 엔드포인트 |
|
|
|
|
|
| 6 | Route 등록 | `routes/api/v1/material.php` |
|
|
|
|
|
| 7 | 첨부파일 연동 | 기존 `File` polymorphic 연결 |
|
|
|
|
|
| 8 | 채번 로직 | `NC-YYYYMMDD-NNN` (테넌트별, 일자별 리셋) |
|
|
|
|
|
|
|
|
|
|
**완료 기준**: Postman/Swagger로 전체 CRUD 동작 확인
|
|
|
|
|
|
|
|
|
|
**API 엔드포인트 (Phase 1 범위)**:
|
|
|
|
|
|
|
|
|
|
| Method | Path | 설명 |
|
|
|
|
|
|--------|------|------|
|
|
|
|
|
| GET | `/api/v1/material/nonconforming-reports` | 목록 (필터/검색/페이지네이션) |
|
|
|
|
|
| GET | `/api/v1/material/nonconforming-reports/{id}` | 단건 (items, files 포함) |
|
|
|
|
|
| POST | `/api/v1/material/nonconforming-reports` | 등록 (items 일괄 저장) |
|
|
|
|
|
| PUT | `/api/v1/material/nonconforming-reports/{id}` | 수정 (items sync) |
|
|
|
|
|
| DELETE | `/api/v1/material/nonconforming-reports/{id}` | 소프트 삭제 |
|
|
|
|
|
| POST | `/api/v1/material/nonconforming-reports/{id}/files` | 첨부파일 업로드 |
|
|
|
|
|
| DELETE | `/api/v1/material/nonconforming-reports/{id}/files/{fileId}` | 첨부파일 삭제 |
|
|
|
|
|
|
|
|
|
|
#### Phase 1-B: 프론트엔드 (sam/react)
|
|
|
|
|
|
|
|
|
|
> **선행 조건**: Phase 1-A API 완료 + API 명세 전달
|
|
|
|
|
|
|
|
|
|
| 순서 | 작업 | 산출물 |
|
|
|
|
|
|------|------|--------|
|
|
|
|
|
| 1 | Server Actions 작성 | `actions.ts` — API 호출 함수 (목록/상세/등록/수정/삭제) |
|
|
|
|
|
| 2 | 목록 페이지 | `NonconformingList.tsx` — 필터, 검색, 페이지네이션, 상태 Badge |
|
|
|
|
|
| 3 | 등록/수정 폼 | `NonconformingForm.tsx` — 5개 FormSection (수주서 구조 준용) |
|
|
|
|
|
| 4 | 자재 상세 테이블 | `NonconformingItemTable.tsx` — 행 추가/삭제, 금액 자동 계산 |
|
|
|
|
|
| 5 | 비용 요약 | `CostSummary.tsx` — 자재/운송/시공/기타/합계, 자재비 자동 산정 |
|
|
|
|
|
| 6 | 상세 뷰 | `NonconformingDetail.tsx` — 읽기 전용 상세 화면 |
|
|
|
|
|
| 7 | 첨부파일 | 기존 파일 업로드 컴포넌트 재사용 |
|
|
|
|
|
| 8 | 라우트 등록 | `page.tsx` + `[id]/page.tsx` (mode=new/edit/view 분기) |
|
|
|
|
|
|
|
|
|
|
**전달 자료 (API → FE)**:
|
|
|
|
|
- API 엔드포인트 목록 (이 문서 4장)
|
|
|
|
|
- 요청/응답 JSON 예시 (이 문서 4.1, 4.2)
|
|
|
|
|
- 부적합 유형 코드 목록: `material`, `process`, `construction`, `other`
|
|
|
|
|
- 상태 코드 목록: `RECEIVED`, `ANALYZING`, `RESOLVED`, `CLOSED`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Phase 2 — 상태 워크플로우 + 결재
|
|
|
|
|
|
|
|
|
|
> 목표: 상태 전이 로직 + 결재 시스템 연동 + 수정 제한
|
|
|
|
|
|
|
|
|
|
#### Phase 2-A: API (sam/api)
|
|
|
|
|
|
|
|
|
|
| 순서 | 작업 | 산출물 |
|
|
|
|
|
|------|------|--------|
|
|
|
|
|
| 1 | 상태 전이 로직 | Service에 `changeStatus()` — 전이 규칙 검증 포함 |
|
|
|
|
|
| 2 | 상태 변경 API | `PATCH /{id}/status` — body: `{ "status": "ANALYZING" }` |
|
|
|
|
|
| 3 | 통계 API | `GET /stats` — 상태별 건수, 월별 비용 합계 |
|
|
|
|
|
| 4 | 수정 제한 | CLOSED 상태에서 update/delete 요청 시 `403` 반환 |
|
|
|
|
|
| 5 | 결재 연동 | 기존 `approval_requests` 테이블과 연결, 승인 시 자동 CLOSED |
|
|
|
|
|
|
|
|
|
|
**추가 API 엔드포인트 (Phase 2)**:
|
|
|
|
|
|
|
|
|
|
| Method | Path | 설명 |
|
|
|
|
|
|--------|------|------|
|
|
|
|
|
| PATCH | `/{id}/status` | 상태 변경 (전이 규칙 검증) |
|
|
|
|
|
| GET | `/stats` | 상태별 통계 |
|
|
|
|
|
|
|
|
|
|
**상태 전이 검증 규칙**:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
RECEIVED → ANALYZING : 조건 없음
|
|
|
|
|
ANALYZING → RESOLVED : cause_analysis + corrective_action 필수
|
|
|
|
|
RESOLVED → CLOSED : 결재 승인 시 자동 (수동 전이 불가)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Phase 2-B: 프론트엔드 (sam/react)
|
|
|
|
|
|
|
|
|
|
> **선행 조건**: Phase 2-A API 완료
|
|
|
|
|
|
|
|
|
|
| 순서 | 작업 | 산출물 |
|
|
|
|
|
|------|------|--------|
|
|
|
|
|
| 1 | 상태 변경 버튼 | 상세 화면 상단에 상태별 액션 버튼 표시 |
|
|
|
|
|
| 2 | 수정 제한 UI | CLOSED 상태 시 폼 전체 disabled + 수정 버튼 숨김 |
|
|
|
|
|
| 3 | 결재상신 버튼 | RESOLVED 상태에서 "결재상신" 버튼 → 결재 다이얼로그 |
|
|
|
|
|
| 4 | 목록 통계 카드 | 상단에 상태별 건수 카드 (접수/분석중/조치완료/종결) |
|
|
|
|
|
|
|
|
|
|
**전달 자료 (API → FE)**:
|
|
|
|
|
- 상태 전이 규칙 (이 문서 5.3)
|
|
|
|
|
- 상태별 허용 액션 매핑
|
|
|
|
|
- 결재 API 연동 방식 (기존 결재 시스템 패턴 참고)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Phase 3 — 통계 + 연동 + 출력
|
|
|
|
|
|
|
|
|
|
> 목표: 대시보드, 수주 연결 강화, 엑셀/PDF 출력
|
2026-03-19 08:21:35 +09:00
|
|
|
|
2026-03-19 08:29:51 +09:00
|
|
|
#### Phase 3-A: API (sam/api)
|
2026-03-19 08:21:35 +09:00
|
|
|
|
2026-03-19 08:29:51 +09:00
|
|
|
| 순서 | 작업 | 산출물 |
|
|
|
|
|
|------|------|--------|
|
|
|
|
|
| 1 | 대시보드 통계 API | 월별 부적합 건수/비용 추이, 유형별 분포 |
|
|
|
|
|
| 2 | 수주 연동 API | `GET /api/v1/orders/{id}/nonconforming-reports` — 수주별 부적합 이력 |
|
|
|
|
|
| 3 | 엑셀 Export | `GET /{id}/export/excel` — 부적합 보고서 엑셀 다운로드 |
|
|
|
|
|
| 4 | PDF 출력 | `GET /{id}/export/pdf` — 인쇄용 보고서 (DomPDF, Pretendard 폰트) |
|
|
|
|
|
| 5 | 일괄 삭제 | `PATCH /bulk-delete` — 체크박스 선택 일괄 삭제 |
|
2026-03-19 08:21:35 +09:00
|
|
|
|
2026-03-19 08:29:51 +09:00
|
|
|
**추가 API 엔드포인트 (Phase 3)**:
|
|
|
|
|
|
|
|
|
|
| Method | Path | 설명 |
|
|
|
|
|
|--------|------|------|
|
|
|
|
|
| GET | `/dashboard` | 대시보드 통계 (월별 건수/비용, 유형별 분포) |
|
|
|
|
|
| GET | `/{id}/export/excel` | 엑셀 다운로드 |
|
|
|
|
|
| GET | `/{id}/export/pdf` | PDF 다운로드 |
|
|
|
|
|
| PATCH | `/bulk-delete` | 일괄 삭제 |
|
|
|
|
|
|
|
|
|
|
#### Phase 3-B: 프론트엔드 (sam/react)
|
|
|
|
|
|
|
|
|
|
> **선행 조건**: Phase 3-A API 완료
|
|
|
|
|
|
|
|
|
|
| 순서 | 작업 | 산출물 |
|
|
|
|
|
|------|------|--------|
|
|
|
|
|
| 1 | 대시보드 차트 | 월별 부적합 건수/비용 추이 차트, 유형별 도넛 차트 |
|
|
|
|
|
| 2 | 수주 상세 연동 | 수주 상세 화면에 "부적합 이력" 탭 추가 |
|
|
|
|
|
| 3 | 엑셀/PDF 버튼 | 상세 화면 + 목록 화면에 다운로드 버튼 |
|
|
|
|
|
| 4 | 일괄 삭제 | 목록 체크박스 → "선택 삭제" 버튼 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 전체 일정 요약
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Phase 1-A (API) ■■■■□□□□□□□□ → Phase 1-B (FE) □□□□■■■■□□□□
|
|
|
|
|
Phase 2-A (API) □□□□□□■■□□□□ → Phase 2-B (FE) □□□□□□□□■■□□
|
|
|
|
|
Phase 3-A (API) □□□□□□□□□■■□ → Phase 3-B (FE) □□□□□□□□□□■■
|
|
|
|
|
```
|
2026-03-19 08:21:35 +09:00
|
|
|
|
2026-03-19 08:29:51 +09:00
|
|
|
> Phase 1-A 완료 후 FE 개발이 시작되면, API 개발자는 Phase 2-A를 병행할 수 있다.
|
2026-03-19 08:21:35 +09:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 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
|