docs: [입고×수입검사] 검사완료→재고 자동 생성 설계 반영 (2026-03-20 구현 완료)

This commit is contained in:
김보곤
2026-03-20 16:09:31 +09:00
parent 96f7ab9054
commit 8d30539ad8

View File

@@ -1,7 +1,7 @@
# 입고등록 × 수입검사 연동 계획
> **작성일**: 2026-03-17
> **상태**: 설계 중
> **상태**: 구현 완료 (Phase 1 데이터 매핑 제외)
> **담당**: R&D실 (API) + 프론트엔드 개발자 (React)
---
@@ -19,12 +19,13 @@
| `DocumentService::resolve()` | ✅ | 품목 ID → 수입검사 템플릿 자동 매칭 |
| `ReceivingService::getItemsWithInspectionTemplate()` | ✅ | 입고 목록에서 `has_inspection_template` 플래그 반환 |
| React `checkInspectionTemplate()` | ✅ | 입고 상세 로드 시 API 호출 → `hasInspectionTemplate` 상태 설정 |
| React 수입검사 버튼 렌더링 | ✅ | `hasInspectionTemplate === true` → "수입검사하기" + "수입검사성적서 보기" 버튼 표시 |
| React 수입검사 버튼 렌더링 | ✅ | 템플릿 존재 또는 검사결과 있으면 버튼 표시 |
| `ImportInspectionInputModal` | ✅ | 수입검사 입력 모달 (검사항목 동적 로드) |
| `InspectionModal` | ✅ | 수입검사 성적서 보기 모달 |
| MNG 중복 검증 | ✅ | 동일 category 내 같은 품목 중복 연결 방지 |
| 검사완료 → `inspection_completed` 상태 전이 | ✅ | 검사완료 시 자동 상태 변경 |
| 검사완료 → 재고 자동 생성 | ✅ | `inspection_completed` 상태에서 Stock/StockLot 자동 생성 |
| **품목 ↔ 템플릿 매핑 데이터** | ❌ | `linked_item_ids`에 품목 미연결 (27종 전부) |
| 검사결과 → 입고 반영 API | ⚠️ | options에 저장은 되나 상태 전이 보강 필요 |
### 1.3 핵심 포인트
@@ -49,25 +50,61 @@ API DocumentService::resolve()
└─ 출력: { template: {...}, is_new: true/false }
```
### 2.2 입고 → 수입검사 흐름 (구현)
### 2.2 입고 → 수입검사 → 재고 흐름 (구현 완료)
```
입고 상세 로드 (ReceivingDetail.tsx)
↓ loadData() → getReceivingById(id)
↓ result.data.itemId 확인
↓ checkInspectionTemplate(itemId) 호출
↓ API: GET /api/v1/documents/resolve?category=incoming_inspection&item_id={itemId}
입고 등록 (receiving_pending)
├─ hasTemplate: true
│ → hasInspectionTemplate = true
│ → customHeaderActions 렌더링:
│ [수입검사하기] → ImportInspectionInputModal 오픈
[수입검사성적서 보기] → InspectionModal 오픈
입고 상세 로드 (ReceivingDetail.tsx)
↓ checkInspectionTemplate(itemId)
├─ 템플릿 존재 또는 검사결과 있음
→ [수입검사하기] + [수입검사성적서 보기] 버튼 표시
└─ hasTemplate: false
→ 버튼 미표시 (수입검사 불필요)
└─ 템플릿 없고 검사결과 없음
→ 버튼 미표시
[수입검사하기] → ImportInspectionInputModal
↓ 검사항목 입력 → 검사완료 버튼
saveInspectionData()
↓ Step 1: POST /v1/documents/upsert (검사 데이터 저장)
↓ Step 2: PUT /v1/receivings/{id} (status → inspection_completed)
ReceivingService::update()
↓ inspection_completed 감지 → 재고 반영 대상
↓ StockService::increaseFromReceiving()
Stock + StockLot 자동 생성 → 재고현황에 표시
```
### 2.3 상태 흐름 (확정)
```
receiving_pending ──수입검사완료──→ inspection_completed ──(재고 자동 생성)
(입고대기) (검사완료)
receiving_pending ──입고처리──→ completed ──(재고 자동 생성)
(입고대기) (입고완료)
```
> **핵심**: `inspection_completed`와 `completed` 두 상태 모두 재고 생성을 트리거한다.
### 2.4 재고 연동 조건 (`ReceivingService::update()`)
```php
$stockStatuses = ['completed', 'inspection_completed'];
$wasCompleted = in_array($oldStatus, $stockStatuses);
$isCompletingReceiving = in_array($newStatus, $stockStatuses) && !$wasCompleted;
```
| 상태 변경 | 재고 동작 |
|----------|----------|
| `receiving_pending``inspection_completed` | Stock/StockLot **생성** |
| `receiving_pending``completed` | Stock/StockLot **생성** |
| `inspection_completed``receiving_pending` | 재고 **차감** (전량) |
| `inspection_completed``inspection_completed` (수량 변경) | 재고 **조정** (차이분) |
> **view/edit 모드에서만 버튼 표시.** new 모드에서는 아직 품목이 저장되지 않았으므로 미표시.
---
@@ -134,55 +171,52 @@ MNG 문서양식관리에서 27종 수입검사 템플릿의 `linked_item_ids`
| MNG에서 linked_item_ids 설정 | R&D실 | 각 템플릿 수정 |
| 매핑 검증 (누락/중복 체크) | R&D실 | API로 검증 |
### Phase 2: API 보강 (백엔드, 필요 시)
> Phase 1 완료 후 실제 검사 플로우를 테스트하며 부족한 부분을 보강한다.
### Phase 2: API 보강 (백엔드) — ✅ 완료
| 작업 | 상태 | 설명 |
|------|:----:|------|
| `DocumentService::resolve()` | ✅ 완료 | 품목 → 템플릿 매칭 |
| `DocumentService::formatTemplateForReact()` | ✅ 완료 | 클로저 스코프 수정 (`$methodCodes`) |
| `ReceivingService::getItemsWithInspectionTemplate()` | ✅ 완료 | 입고 목록 `has_inspection_template` 플래그 |
| `checkInspectionTemplate()` React 호출 | ✅ 완료 | 입고 상세에서 버튼 표시 제어 |
| 수입검사 결과 → Receiving options 반영 | ⚠️ 보강 필요 | 검사완료 시 inspectionDate/Result 자동 설정 |
| 입고 상태 전이 (inspection_pending) | ⚠️ 보강 필요 | 검사 시작 → 검사대기, 검사완료 → 입고대기 |
| `Receiving::STATUSES` | ✅ 완료 | `inspection_completed` 상태 추가 |
| `ReceivingService::update()` | ✅ 완료 | `inspection_completed` 상태에서 재고 자동 생성 |
| `StoreReceivingRequest` | ✅ 완료 | `order_qty` required 검증, `inspection_completed` 허용 |
| 수입검사 결과 → Receiving options 반영 | ✅ 완료 | `saveInspectionData()`에서 자동 설정 |
### Phase 3: React UI 보강 (프론트엔드, 필요 시)
> 버튼 표시 및 모달 연동은 이미 구현됨. 검사 결과 저장 후 입고 데이터 자동 반영 부분만 보강.
### Phase 3: React UI 보강 (프론트엔드) — ✅ 완료
| 작업 | 상태 | 설명 |
|------|:----:|------|
| 수입검사하기 버튼 | ✅ 완료 | `hasInspectionTemplate` 기반 조건부 렌더링 |
| 수입검사하기 버튼 | ✅ 완료 | 템플릿 존재 또는 검사결과 있으면 표시 |
| ImportInspectionInputModal | ✅ 완료 | 검사항목 동적 로드 + 입력 |
| InspectionModal (성적서 보기) | ✅ 완료 | 저장된 검사 성적서 조회 |
| 검사 결과 저장 후 입고 자동 갱신 | ⚠️ 보강 필요 | `handleImportInspectionSave()``loadData()` 이미 호출, API 보강 후 자동 반영 |
| 검사완료 → `inspection_completed` 상태 전이 | ✅ 완료 | `saveInspectionData()` → status 자동 변경 |
| 입고 목록 삭제 기능 | ✅ 완료 | 체크박스 선택 → 삭제 버튼 표시 |
| 단위(unit) → API 전달 | ✅ 완료 | `transformFrontendToApi()`에서 `unit` 매핑 |
---
## 5. 관련 API 엔드포인트
### 5.1 기존 (구현 완료)
### 5.1 구현 완료
```
GET /api/v1/documents/resolve
params: { category: 'incoming_inspection', item_id: 101 }
→ 해당 품목의 수입검사 템플릿 + 기존 문서 반환
POST /api/v1/documents
→ 검사 결과 문서 저장
POST /api/v1/documents/upsert
→ 검사 데이터 저장 (sections/items/field_values)
PUT /api/v1/receivings/{id}
body: { status: 'inspection_completed', inspection_status, inspection_date, inspection_result }
→ 상태 변경 + 재고 자동 생성 (inspection_completed 시)
GET /api/v1/items/{id}
→ 응답에 has_inspection_template 포함
```
### 5.2 보강 필요
```
PATCH /api/v1/receivings/{id}/inspection-result (신규)
body: { inspection_date, inspection_result, document_id? }
→ Receiving.options의 검사일/검사결과 업데이트
→ 상태 전이 (inspection_pending → receiving_pending)
```
> 별도 `PATCH /inspection-result` 엔드포인트는 불필요. `saveInspectionData()`가 `documents/upsert` + `receivings/{id}` PUT 2단계로 처리한다.
---
@@ -205,11 +239,15 @@ PATCH /api/v1/receivings/{id}/inspection-result (신규)
└──────────────────────────────────────────────────────────────────┘
```
### 버튼 렌더링 조건 (ReceivingDetail.tsx:882)
### 버튼 렌더링 조건 (ReceivingDetail.tsx)
```typescript
const showInspectionActions = hasInspectionTemplate
|| !!detail?.inspectionResult
|| !!detail?.inspectionDate;
const customHeaderActions =
(isViewMode || isEditMode) && detail && hasInspectionTemplate ? (
(isViewMode || isEditMode) && detail && showInspectionActions ? (
<div className="flex gap-2">
<Button onClick={handleInspection}>수입검사하기</Button>
<Button onClick={handleViewInspectionReport}>수입검사성적서 보기</Button>
@@ -220,10 +258,11 @@ const customHeaderActions =
| 조건 | 결과 |
|------|------|
| new 모드 | 버튼 미표시 (품목 미저장) |
| view/edit + `hasInspectionTemplate=false` | 버튼 미표시 |
| view/edit + 템플릿 없고 검사결과 없음 | 버튼 미표시 |
| view/edit + `hasInspectionTemplate=true` | **두 버튼 모두 표시** |
| view/edit + 검사결과 또는 검사일 있음 | **두 버튼 모두 표시** (합격 후에도 유지) |
> **핵심**: MNG에서 해당 품목의 수입검사 템플릿에 `linked_item_ids`를 설정하면 버튼이 자동 표시된다.
> **핵심**: 템플릿이 있거나, 이미 검사가 수행된 경우 버튼이 표시된다.
---
@@ -236,4 +275,4 @@ const customHeaderActions =
---
**최종 업데이트**: 2026-03-17
**최종 업데이트**: 2026-03-20