docs: [MES] 데이터 정합성 보고서 v2 → v2.1 업데이트
- 이슈 #2 품질검사: 출하 연동 불필요 (품질검사는 출하 후 시공 완료 후) - 이슈 #3 can_ship: ✅ 해결 완료 - 이슈 #4 재고차감: ✅ 비활성화 완료 - 이슈 #6 ShipmentItem FK: ✅ 해결 완료 - P0/P1/P2 로드맵 상태 전체 갱신
This commit is contained in:
@@ -0,0 +1,506 @@
|
||||
# MES 데이터 정합성 심층 분석 보고서 v2
|
||||
|
||||
**분석일**: 2026-03-13 (v2.1 - 정밀 코드 검증 반영)
|
||||
**범위**: 수주(Order) → 생산(Production) → 품질(Quality) → 출고(Shipment) 전체 파이프라인
|
||||
**방법**: 프론트엔드(sam-react-prod) + 백엔드(sam-api) 코드 레벨 재분석 + v2.1 정밀 검증
|
||||
|
||||
---
|
||||
|
||||
## v1 대비 변경사항 요약
|
||||
|
||||
| 항목 | v1 (초기 분석) | v2 (코드 업데이트 반영) |
|
||||
|------|---------------|----------------------|
|
||||
| StockLot.work_order_id FK | 확인 안됨 | ✅ 2026-02-21 추가 확인 (생산→재고 연결 기반 마련) |
|
||||
| QualityDocument 시스템 | 존재 인지 | ✅ 2026-03-05~10 활발히 개선 중 (inspection_data, options JSON 추가) |
|
||||
| 출하 자동생성 | 언급 | ✅ 상세 분석 완료: createShipmentFromOrder() 중복방지 + ensureShipmentExists() |
|
||||
| 3월 MES FK 추가 | 미확인 | ❌ 3월 마이그레이션에 MES FK 추가 없음 확인 |
|
||||
| LOT 백엔드 자동 채번 | 미구현 | ✅ v2.1 확인: generateLotNo() + saveItemResults() 구현됨 |
|
||||
| 나머지 3개 이슈 | 발견 | 🔴 여전히 미해결 (can_ship, 수주생산 재고입고, ShipmentItem FK) |
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
| # | 이슈 | 심각도 | v1 판정 | v2 판정 | 변경 |
|
||||
|---|------|--------|---------|---------|------|
|
||||
| 1 | 생산완료→수주 PRODUCED 자동전환 | 🟡→🟢 | 조건부 동작 | **정상 동작 + 자동출하 생성** | ⬆ 개선 |
|
||||
| 2 | 품질검사 이중 시스템 | 🔴 | 구조적 문제 | 🔴 **구조적 문제 지속** (QualityDocument 활발 개발 중이나 출고 연동 미완) | 유지 |
|
||||
| 3 | 출고 시 can_ship 검증 | 🔴 | 누락 | 🔴 **여전히 누락** (canProceedToShip 호출 0회) | 유지 |
|
||||
| 4 | 출고 시 재고 차감 | ✅ | 구현됨 | ✅ **구현됨**, ⚠️ soft fail 리스크 유지 | 유지 |
|
||||
| 5 | LOT 추적 체계 | 🔴 | 단절 | 🟢 **선생산 정상** / 🟡 **수주생산 재고입고 미경유** (v2.1: generateLotNo() 구현 확인) | ⬆⬆ 상향 |
|
||||
| 6 | 출고품목↔수주품목 FK | 🔴 | 없음 | 🔴 **여전히 없음** (3월 마이그레이션에도 미추가) | 유지 |
|
||||
|
||||
---
|
||||
|
||||
## 이슈 1: 생산완료 → 수주 상태 자동전환 + 출하 자동생성
|
||||
|
||||
### v2 판정: 🟢 정상 동작 (v1 대비 상향)
|
||||
|
||||
v2 분석에서 자동 출하 생성 로직까지 상세 확인 완료. **정상 동작 확인**.
|
||||
|
||||
### 전체 흐름
|
||||
|
||||
```
|
||||
WorkOrder 상태 변경 (updateStatus)
|
||||
↓
|
||||
syncOrderStatus() 자동 호출 (L971-1059)
|
||||
↓
|
||||
메인 WO 필터링: is_auxiliary=false AND process_id≠null
|
||||
↓
|
||||
전체 완료 시 → Order.status = PRODUCED
|
||||
↓
|
||||
createShipmentFromOrder() 자동 호출 (L719-809)
|
||||
↓
|
||||
Shipment 생성: status='scheduled', can_ship=true(자동)
|
||||
↓
|
||||
기존 Shipment 있으면 → 중복 생성 방지 (L721-728)
|
||||
```
|
||||
|
||||
### 코드 근거
|
||||
|
||||
**syncOrderStatus**: `WorkOrderService.php:971-1059`
|
||||
|
||||
```php
|
||||
// L989-995: 메인 WO 필터 (보조공정 + process_id=null 제외)
|
||||
$mainWorkOrders = $allWorkOrders->filter(fn ($wo) =>
|
||||
!$this->isAuxiliaryWorkOrder($wo) && $wo->process_id !== null
|
||||
);
|
||||
|
||||
// L1001-1019: 상태 결정
|
||||
if ($shippedCount === $totalCount) {
|
||||
$newOrderStatus = Order::STATUS_SHIPPED;
|
||||
} elseif (($completedCount + $shippedCount) === $totalCount) {
|
||||
$newOrderStatus = Order::STATUS_PRODUCED;
|
||||
}
|
||||
```
|
||||
|
||||
**createShipmentFromOrder**: `WorkOrderService.php:719-809`
|
||||
|
||||
```php
|
||||
// L721-728: 중복 방지
|
||||
$existingShipment = Shipment::where('order_id', $order->id)->first();
|
||||
if ($existingShipment) return $existingShipment;
|
||||
|
||||
// L732-744: 출하 자동 생성
|
||||
$shipment = Shipment::create([
|
||||
'order_id' => $order->id,
|
||||
'work_order_id' => null, // 수주 레벨 (WO 레벨 아님)
|
||||
'status' => 'scheduled',
|
||||
'can_ship' => true, // ← 자동으로 true 설정
|
||||
]);
|
||||
|
||||
// L746-790: WO 아이템 → ShipmentItem 복사
|
||||
```
|
||||
|
||||
**ensureShipmentExists**: 이미 PRODUCED인데 출하가 없는 경우 보완 (L1027-1033)
|
||||
|
||||
### 잔존 리스크 (낮음)
|
||||
|
||||
| 조건 | 원인 | 발생 가능성 |
|
||||
|------|------|------------|
|
||||
| `process_id = NULL`인 WO | 공정 매핑 실패 | 낮음 (생성 시 검증됨) |
|
||||
| `is_auxiliary` 오설정 | options JSON 수동 수정 | 매우 낮음 |
|
||||
|
||||
### 회의 논의 포인트
|
||||
- ✅ 이 부분은 정상 동작 확인됨. 추가 조치 불필요
|
||||
- (선택) process_id=null WO가 실데이터에 존재하는지 한번 쿼리 확인
|
||||
|
||||
---
|
||||
|
||||
## 이슈 2: 품질검사 이중 시스템
|
||||
|
||||
### v2.1 판정: 🟡 이중 시스템은 존재하나, 출하 연동은 불필요 (비즈니스 흐름상 품질검사는 출하 후)
|
||||
|
||||
### v1 대비 변화
|
||||
|
||||
| 변경 사항 | 시기 | 내용 |
|
||||
|-----------|------|------|
|
||||
| `quality_document_locations.inspection_data` JSON 추가 | 2026-03-06 | 개소별 검사 데이터 저장 |
|
||||
| `quality_document_locations.options` JSON 추가 | 2026-03-10 | 검사 옵션 확장 |
|
||||
| QualityDocumentService 개선 | 2026-03 | inspectLocation() 등 기능 확장 |
|
||||
|
||||
### v2.1 비즈니스 흐름 정정
|
||||
|
||||
```
|
||||
실제 흐름 (확인됨):
|
||||
생산 완료 → 출하 → 현장 시공 → 시공 완료 → 제품검사(FQC)
|
||||
|
||||
∴ 품질검사는 출하 이후에 발생 → can_ship ↔ 품질검사 연동 불필요
|
||||
∴ v2에서 "출고 연동 미완"으로 판정한 것은 오판 → 연동 자체가 해당 없음
|
||||
```
|
||||
|
||||
**두 시스템 현재 상태**:
|
||||
|
||||
| 항목 | 경로A: Inspection | 경로B: QualityDocument |
|
||||
|------|-------------------|----------------------|
|
||||
| **테이블** | `inspections` | `quality_documents` + `quality_document_locations` |
|
||||
| **FK 연결** | `work_order_id` (작업지시) | `order_id` (수주) + `order_item_id` (개소) |
|
||||
| **3월 업데이트** | 변경 없음 | ✅ 활발히 개선 중 |
|
||||
| **용도** | IQC/PQC (공정 중 검사) | FQC (출하 후 제품 검사) |
|
||||
|
||||
### 회의 논의 포인트
|
||||
- QualityDocument가 활발히 개발 중 → **경로B를 FQC 표준으로 확정하는 것이 합리적**
|
||||
- 경로A(Inspection)는 IQC/PQC 전용으로 역할 한정, FQC는 경로B로 통일
|
||||
- ~~품질 완료 시 can_ship 연동~~ → **해당 없음** (품질검사는 출하 후 시공 완료 후 수행)
|
||||
|
||||
---
|
||||
|
||||
## 이슈 3: 출고 시 can_ship 검증 누락
|
||||
|
||||
### v2.1 판정: ✅ 해결 완료 — 백엔드 can_ship 검증 + 프론트 버튼 비활성화 추가
|
||||
|
||||
### 코드 현황 (변경 없음)
|
||||
|
||||
**canProceedToShip()**: `Shipment.php:220-223` — 정의만 존재
|
||||
|
||||
```php
|
||||
public function canProceedToShip(): bool {
|
||||
return $this->can_ship && $this->deposit_confirmed;
|
||||
}
|
||||
// grep 결과: 모델 정의 외 호출 0회
|
||||
```
|
||||
|
||||
**updateStatus()**: `ShipmentService.php:305-356` — can_ship 검증 없이 바로 업데이트
|
||||
|
||||
```php
|
||||
public function updateStatus(int $id, string $status, ?array $additionalData = null): Shipment
|
||||
{
|
||||
$shipment = Shipment::findOrFail($id);
|
||||
// 🔴 can_ship 검증 없음
|
||||
$shipment->update(['status' => $status, ...]);
|
||||
}
|
||||
```
|
||||
|
||||
**프론트엔드**: `ShipmentDetail.tsx:304-314` — can_ship 참조 자체가 없음
|
||||
|
||||
```typescript
|
||||
const STATUS_TRANSITIONS: Record<ShipmentStatus, ShipmentStatus | null> = {
|
||||
scheduled: 'ready', ready: 'shipping', shipping: 'completed', completed: null,
|
||||
};
|
||||
// v2.1 확인: canShip 변수 자체가 컴포넌트에 없음 (grep 0건)
|
||||
// can_ship=false여도 상태 변경 버튼이 무조건 표시됨
|
||||
```
|
||||
|
||||
### v2 신규 발견: 자동 출하에서 can_ship=true 자동 설정
|
||||
|
||||
```php
|
||||
// createShipmentFromOrder (L732-744)
|
||||
'can_ship' => true, // 자동 생성 시 무조건 true
|
||||
```
|
||||
|
||||
→ 자동 생성된 출하는 can_ship=true이므로 문제 경감
|
||||
→ **그러나** 수동 생성 출하에서는 여전히 검증 없음
|
||||
|
||||
### 위험 시나리오
|
||||
|
||||
```
|
||||
수동 출하 생성 (can_ship=false)
|
||||
→ 사용자가 "출하대기" 클릭 → 검증 없이 ready
|
||||
→ "배송중" → "배송완료" → 재고 차감 시도
|
||||
→ 재고 부족 시 soft fail (로그만, 상태는 completed) ❌
|
||||
```
|
||||
|
||||
### ~~수정안~~ → ✅ v2.1에서 적용 완료
|
||||
|
||||
**백엔드** (ShipmentService::updateStatus() L312-317):
|
||||
```php
|
||||
if (in_array($status, ['ready', 'shipping', 'completed']) && !$shipment->can_ship) {
|
||||
throw new BadRequestHttpException(__('error.shipment.cannot_ship'));
|
||||
}
|
||||
```
|
||||
|
||||
**프론트엔드** (ShipmentDetail.tsx):
|
||||
```typescript
|
||||
{STATUS_TRANSITIONS[detail.status] && detail.canShip && (
|
||||
<Button onClick={handleOpenStatusDialog}>상태 변경</Button>
|
||||
)}
|
||||
{STATUS_TRANSITIONS[detail.status] && !detail.canShip && (
|
||||
<Button variant="outline" size="sm" disabled>출하 불가 (품질 검수 필요)</Button>
|
||||
)}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 이슈 4: 출고 시 재고 차감
|
||||
|
||||
### v2.1 판정: ✅ 비활성화 완료 — 현재 시점에서 불필요한 로직
|
||||
|
||||
**비즈니스 흐름 정정** (v2.1):
|
||||
```
|
||||
- 수주생산: 재고를 거치지 않음 (make-to-order) → 출하 시 재고 차감 불필요
|
||||
- 선생산 완성품: 재고에 입고 → 다른 WO의 자재 투입 시 차감 → 출하 시 차감 불필요
|
||||
∴ decreaseStockForShipment() 자체가 현재 시점에서 쓸 일이 없는 로직
|
||||
```
|
||||
|
||||
**조치**: `updateStatus()`에서 `decreaseStockForShipment()` 호출 비활성화 (TODO 주석으로 선생산 재검토 예약)
|
||||
|
||||
### ~~회의 논의 포인트~~ → 해결됨
|
||||
- ~~Hard Fail vs Soft Fail~~ → 호출 자체 비활성화로 해소
|
||||
- 선생산 로직이 본격 가동되면 재검토 필요
|
||||
|
||||
---
|
||||
|
||||
## 이슈 5: LOT 추적 체계
|
||||
|
||||
### v2.1 판정: 🟢 선생산 정상 / 🟡 수주생산 재고입고 미경유 (v1 🔴 → v2 🟡 → v2.1 ⬆ 상향)
|
||||
|
||||
### v2.1 정밀 검증 결과: LOT 자동 채번 **구현 확인**
|
||||
|
||||
v2에서 "백엔드 LOT 저장 로직 없음"으로 판정했으나, **현재 코드에 구현되어 있음**.
|
||||
|
||||
| 단계 | 메서드 | 위치 | 상태 |
|
||||
|------|--------|------|------|
|
||||
| LOT 자동 채번 | `generateLotNo()` | L1155-1176 | ✅ `KD-SA-YYMMDD-NN` 형식 |
|
||||
| LOT options 저장 | `saveItemResults()` | L1105-1140 | ✅ `options.result.lot_no`에 저장 |
|
||||
| 선생산 재고 입고 | `stockInFromProduction()` | L603-604 | ✅ `sales_order_id=null`일 때 호출 |
|
||||
| StockLot 생성 | `increaseFromProduction()` | L349-398 | ✅ `work_order_id` FK 활용 |
|
||||
|
||||
### 핵심 코드 근거
|
||||
|
||||
**1. LOT 자동 채번 (구현됨)**
|
||||
|
||||
```php
|
||||
// WorkOrderService.php:1155-1176
|
||||
private function generateLotNo(WorkOrder $workOrder): string
|
||||
{
|
||||
$date = now()->format('ymd');
|
||||
$prefix = 'KD-SA';
|
||||
// 오늘 날짜의 마지막 LOT 번호 조회 → 시퀀스 증가
|
||||
return sprintf('%s-%s-%02d', $prefix, $date, $seq);
|
||||
}
|
||||
```
|
||||
|
||||
**2. saveItemResults()에서 LOT 저장 (구현됨)**
|
||||
|
||||
```php
|
||||
// WorkOrderService.php:1105-1140
|
||||
private function saveItemResults(WorkOrder $workOrder, ?array $resultData, int $userId): void
|
||||
{
|
||||
$lotNo = $this->generateLotNo($workOrder); // ✅ LOT 자동 채번
|
||||
foreach ($items as $item) {
|
||||
$itemResult = [
|
||||
'lot_no' => $lotNo, // ✅ 모든 아이템에 LOT 저장
|
||||
'good_qty' => $item->quantity,
|
||||
// ... 기타 결과 데이터
|
||||
];
|
||||
$options['result'] = $itemResult;
|
||||
$item->save();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**3. 선생산 vs 수주생산 분기 (핵심 발견)**
|
||||
|
||||
```php
|
||||
// WorkOrderService.php:602-605
|
||||
// 작업완료 시: 선생산(수주 없음) → 재고 입고
|
||||
if ($status === WorkOrder::STATUS_COMPLETED && !$workOrder->sales_order_id) {
|
||||
$this->stockInFromProduction($workOrder); // ✅ 선생산만 호출
|
||||
}
|
||||
```
|
||||
|
||||
### 두 경로의 LOT 추적 현황
|
||||
|
||||
**경로A: 선생산 (sales_order_id = null) — ✅ 정상 동작**
|
||||
|
||||
```
|
||||
WO 완료 → saveItemResults() (LOT 채번 + 저장)
|
||||
→ stockInFromProduction() 호출
|
||||
→ increaseFromProduction() → StockLot 생성 (work_order_id FK ✅)
|
||||
→ Stock 합계 갱신 + 거래 이력 기록
|
||||
```
|
||||
|
||||
**경로B: 수주 생산 (sales_order_id 존재) — 🟡 재고 미경유 (의도된 설계 가능성)**
|
||||
|
||||
```
|
||||
WO 완료 → saveItemResults() (LOT 채번 + 저장 ✅)
|
||||
→ stockInFromProduction() 미호출 (수주 연결이므로 skip)
|
||||
→ syncOrderStatus() → Order PRODUCED
|
||||
→ createShipmentFromOrder() → ShipmentItem에 lot_no 복사 ✅
|
||||
→ 출하 완료 시 decreaseStockForShipment() → ⚠️ 입고 없이 차감 시도
|
||||
```
|
||||
|
||||
→ 수주 생산은 make-to-order 방식으로 재고를 거치지 않는 **의도된 설계일 수 있음**.
|
||||
→ 그러나 StockLot이 생성되지 않으므로 **재고 기반 LOT 추적은 불가**.
|
||||
|
||||
### v1 대비 개선 사항
|
||||
|
||||
| 개선 | 시기 | 코드 근거 |
|
||||
|------|------|-----------|
|
||||
| `stock_lots.work_order_id` FK 추가 | 2026-02-21 | 마이그레이션 확인 |
|
||||
| `inspections.work_order_id` FK 추가 | 2026-02-27 | 마이그레이션 확인 |
|
||||
| `generateLotNo()` 백엔드 자동 채번 | v2.1 확인 | WorkOrderService:1155-1176 |
|
||||
| `saveItemResults()` LOT 저장 | v2.1 확인 | WorkOrderService:1105-1140 |
|
||||
|
||||
### 잔존 이슈 (v2.1 업데이트)
|
||||
|
||||
1. **수주생산 재고입고 경로**: StockLot 미생성 → 의도된 설계 확인됨 (make-to-order). 선생산 로직 추후 검증
|
||||
2. **프론트 LOT 생성 코드**: WorkerScreen/actions.ts에서 별도 LOT 생성 → 백엔드와 이중 생성 가능성 (P1 #5-1)
|
||||
3. ~~**출하 재고 차감**~~: ✅ decreaseStockForShipment() 비활성화로 해소
|
||||
|
||||
### ~~논의 필요 사항~~ → v2.1 확인됨
|
||||
|
||||
- **수주생산의 재고 미경유**: ✅ 의도된 설계 확인 (make-to-order, 주인이 있는 제품은 재고 미경유)
|
||||
- 선생산 완성품은 재고 입고 → 다른 WO의 자재 투입 시 차감되는 흐름 (부품화)
|
||||
|
||||
---
|
||||
|
||||
## 이슈 6: 출고품목 ↔ 수주품목 FK 부재
|
||||
|
||||
### v2.1 판정: ✅ 해결 완료 — order_item_id + work_order_item_id 컬럼/인덱스 추가
|
||||
|
||||
### ShipmentItem 컬럼 (v2.1 수정됨)
|
||||
|
||||
```
|
||||
id, tenant_id, shipment_id(FK), seq,
|
||||
item_code, item_name, floor_unit, specification,
|
||||
quantity, unit, lot_no, stock_lot_id(index only),
|
||||
order_item_id(index) 🆕, work_order_item_id(index) 🆕, remarks
|
||||
```
|
||||
|
||||
- ✅ `order_item_id` → 추가됨 (nullable, 인덱스)
|
||||
- ✅ `work_order_item_id` → 추가됨 (nullable, 인덱스)
|
||||
- 마이그레이션: `2026_03_13_100000_add_order_item_id_to_shipment_items`
|
||||
- 모델: ShipmentItem에 orderItem(), workOrderItem() BelongsTo 관계 추가
|
||||
- 데이터 입력: createShipmentFromOrder()에서 자동 매핑
|
||||
|
||||
### 추적 가능 여부 (v2.1 이후)
|
||||
|
||||
| 질문 | 답변 가능 여부 |
|
||||
|------|--------------|
|
||||
| "출고 #1234에서 어떤 수주 품목이 출고됐나?" | ✅ order_item_id로 조회 |
|
||||
| "수주 품목 #999는 어느 출고에서 출고됐나?" | ✅ 역추적 가능 |
|
||||
| "수주 10개 품목 중 미출고 품목은?" | ✅ 집계 가능 |
|
||||
| "부분 출고 진행률은?" | ✅ 계산 가능 |
|
||||
|
||||
> ⚠️ 기존 데이터는 order_item_id=null. 소급 매칭은 추후 필요시 진행
|
||||
|
||||
### ~~자동 출하 생성 시 연결 기회 놓침~~ → ✅ v2.1에서 해결
|
||||
|
||||
```php
|
||||
// createShipmentFromOrder (L756-768) — v2.1 수정 완료
|
||||
ShipmentItem::create([
|
||||
'shipment_id' => $shipment->id,
|
||||
'item_code' => $woItem->item_id ? "ITEM-{$woItem->item_id}" : null,
|
||||
'quantity' => $result['good_qty'] ?? $woItem->quantity,
|
||||
'order_item_id' => $woItem->source_order_item_id, // ✅ 추가됨
|
||||
'work_order_item_id' => $woItem->id, // ✅ 추가됨
|
||||
]);
|
||||
```
|
||||
|
||||
### ~~수정안~~ → ✅ 적용 완료
|
||||
|
||||
- **마이그레이션**: `2026_03_13_100000_add_order_item_id_to_shipment_items` (인덱스만, FK 제약 없음)
|
||||
- **모델**: ShipmentItem에 $fillable, $casts, orderItem()/workOrderItem() 관계 추가
|
||||
- **데이터 입력**: createShipmentFromOrder()의 WO 경로 + Order fallback 경로 모두 적용
|
||||
|
||||
---
|
||||
|
||||
## 전체 FK 연결 현황도 (v2 업데이트)
|
||||
|
||||
```
|
||||
orders ──────────────────── order_items ──────── order_nodes
|
||||
│ (order_id FK) │ (order_node_id FK) │
|
||||
│ │ │
|
||||
├─── work_orders │ │
|
||||
│ │ (sales_order_id FK) │ │
|
||||
│ │ │ │
|
||||
│ └─── work_order_items │ │
|
||||
│ │ │ │
|
||||
│ │ source_order_item_id ──→ ❌ FK 없음 (인덱스만)
|
||||
│ │ │
|
||||
│ inspections │
|
||||
│ │ (work_order_id FK ✅) [2026-02-27 추가] │
|
||||
│ │ (lot_no ← 연결 안됨 ❌) │
|
||||
│ │
|
||||
│ stock_lots │
|
||||
│ │ (work_order_id FK ✅) [2026-02-21 추가] ← 🆕 v1에서 미확인
|
||||
│ │
|
||||
├─── quality_document_orders ──→ quality_documents │
|
||||
│ │ (order_id FK ✅) │
|
||||
│ │ │
|
||||
│ └─── quality_document_locations │
|
||||
│ │ (order_item_id FK ✅) │
|
||||
│ │ (inspection_data JSON 🆕 2026-03-06) │
|
||||
│ │ (options JSON 🆕 2026-03-10) │
|
||||
│ │
|
||||
└─── shipments │
|
||||
│ (order_id FK ✅, work_order_id FK ✅) │
|
||||
│ │
|
||||
└─── shipment_items │
|
||||
│ (shipment_id FK ✅) │
|
||||
│ (stock_lot_id → 인덱스만, FK 없음) │
|
||||
│ (order_item_id ✅ 인덱스) 🆕 v2.1 │
|
||||
│ (work_order_item_id ✅ 인덱스) 🆕 v2.1 │
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 개선 우선순위 로드맵 (v2.1 업데이트)
|
||||
|
||||
### P0 (즉시 - 운영 리스크) — v2.1 반영
|
||||
|
||||
| # | 작업 | 수정 범위 | 난이도 | 상태 |
|
||||
|---|------|---------|--------|------|
|
||||
| 1 | **can_ship 검증 추가** | ShipmentService::updateStatus() + ShipmentDetail.tsx | 하 | ✅ 완료 |
|
||||
| 2 | ~~**재고 차감 실패 알림**~~ | decreaseStockForShipment() 호출 비활성화 — 수주생산은 재고 미경유, 선생산 완성품은 자재 투입 시 차감 | - | ✅ 비활성화 |
|
||||
|
||||
### P1 (단기 - 데이터 정합성) — v2.1 반영
|
||||
|
||||
| # | 작업 | 수정 범위 | 난이도 | 상태 |
|
||||
|---|------|---------|--------|------|
|
||||
| 3 | ~~**생산 LOT 백엔드 자동 채번**~~ | generateLotNo() + saveItemResults() 이미 구현됨 | - | ✅ 확인 완료 |
|
||||
| 4 | **수주생산 재고입고 경로 검토** | 수주생산은 재고 미경유 (make-to-order) — 의도된 설계. 선생산 로직은 추후 검증 | 중 | 🟡 설계 확인됨 |
|
||||
| 5 | **shipment_items에 order_item_id/work_order_item_id 추가** | 마이그레이션 + ShipmentItem 모델 + createShipmentFromOrder() | 중 | ✅ 완료 |
|
||||
| 5-1 | **프론트 LOT 이중 생성 정리** | WorkerScreen/actions.ts 프론트 LOT 생성 코드 제거 (백엔드 채번으로 통일) | 하 | 미착수 |
|
||||
|
||||
### P2 (중기 - 구조 개선) — v2.1 품질검사 흐름 정정 반영
|
||||
|
||||
| # | 작업 | 수정 범위 | 난이도 |
|
||||
|---|------|---------|--------|
|
||||
| 6 | **품질검사 정본 = QualityDocument** | Inspection은 IQC/PQC 전용, FQC는 QualityDocument로 통일 | 상 |
|
||||
| 7 | ~~**품질완료 → can_ship 자동 연동**~~ | ❌ 해당 없음 — 품질검사는 출하 후 시공 완료 후 수행 | - |
|
||||
| 8 | **work_order_items.source_order_item_id FK** | 마이그레이션 1줄 | 하 |
|
||||
| 9 | **stock_lot_id FK constraint 추가** | shipment_items 마이그레이션 | 하 |
|
||||
|
||||
---
|
||||
|
||||
## 정상 동작 확인 항목 (v2)
|
||||
|
||||
- ✅ 수주 → 생산지시 생성 (공정별 자동 분류)
|
||||
- ✅ 작업지시 상태 관리 (유효 상태 전환 + auxiliary 필터링)
|
||||
- ✅ **syncOrderStatus()**: 메인 WO 완료 → Order PRODUCED 자동 전환
|
||||
- ✅ **createShipmentFromOrder()**: PRODUCED 전환 시 출하 자동 생성 (중복 방지 포함)
|
||||
- ✅ **ensureShipmentExists()**: 이미 PRODUCED인데 출하 없는 경우 보완
|
||||
- ✅ 자재 투입 재고 차감 (WorkOrderMaterialInput + StockService)
|
||||
- ✅ 출고 완료 시 재고 차감 (FIFO + lockForUpdate + stock_transactions)
|
||||
- ✅ 출고 완료 → 수주 상태 SHIPPED 자동 전환
|
||||
- ✅ 매출 자동 생성 (sales_recognition 조건부)
|
||||
- ✅ 수주 상태별 수정/삭제 제한
|
||||
- ✅ 생산지시 되돌리기 (WorkOrder/Item/Result 삭제)
|
||||
- 🆕 ✅ StockLot.work_order_id FK (생산→재고 연결 기반)
|
||||
- 🆕 ✅ Inspection.work_order_id FK (검사→생산 연결)
|
||||
- 🆕 ✅ generateLotNo() 백엔드 LOT 자동 채번 (v2.1 확인)
|
||||
- 🆕 ✅ saveItemResults() LOT options 저장 (v2.1 확인)
|
||||
- 🆕 ✅ 선생산 stockInFromProduction() → StockLot 생성 + work_order_id 연결 (v2.1 확인)
|
||||
|
||||
---
|
||||
|
||||
## 회의 토론 안건 정리
|
||||
|
||||
### 즉시 결정 필요 (P0) — ✅ 해결됨
|
||||
|
||||
1. ~~**can_ship 검증**~~: ✅ 백엔드 + 프론트 수정 완료
|
||||
2. ~~**재고 차감 실패 처리**~~: ✅ decreaseStockForShipment() 비활성화 (수주생산=재고 미경유, 선생산=자재 투입 시 차감)
|
||||
|
||||
### 설계 방향 결정 필요 (P1) — v2.1 업데이트
|
||||
|
||||
3. ~~**LOT 채번 규칙**~~: ✅ 이미 구현됨 (`KD-SA-YYMMDD-NN`, 백엔드 generateLotNo())
|
||||
4. **수주생산 재고입고 경로**: ✅ 수주생산은 재고 미경유 (make-to-order) — 의도된 설계 확인됨. 선생산 로직은 추후 검증
|
||||
5. ~~**ShipmentItem FK**~~: ✅ order_item_id + work_order_item_id 컬럼/인덱스 추가 완료 (FK 제약 없이 인덱스만)
|
||||
5-1. **프론트 LOT 이중 생성**: WorkerScreen/actions.ts의 프론트 LOT 생성 코드 → 백엔드 통일 후 제거
|
||||
|
||||
### 방향성 논의 (P2)
|
||||
|
||||
6. **품질 시스템 정본**: QualityDocument를 FQC 표준으로 확정하는 것에 이견 있는지?
|
||||
7. ~~**품질→출하 자동 연동**~~: ❌ 해당 없음 — 비즈니스 흐름상 품질검사는 출하 후 시공 완료 후 수행되므로 can_ship 연동 불필요
|
||||
Reference in New Issue
Block a user