# 재고 통합 시스템 개발 계획 > **작성일**: 2025-01-26 > **목적**: 입고/생산/견적 시스템과 재고(Stock)의 실시간 연동 구현 > **기준 문서**: `docs/specs/database-schema.md`, `docs/standards/api-rules.md` > **상태**: 🔄 계획 수립 중 --- ## 📍 현재 진행 상태 | 항목 | 내용 | |------|------| | **마지막 완료 작업** | Phase 3 - 견적/출하 → 재고 연동 완료 | | **다음 작업** | ✅ 모든 Phase 완료 | | **진행률** | 12/12 (100%) | | **마지막 업데이트** | 2025-01-26 | --- ## 1. 개요 ### 1.1 배경 현재 SAM 시스템의 재고 관리는 **조회 전용**으로만 작동합니다: - 입고(Receiving)가 완료되어도 Stock이 증가하지 않음 - 생산(WorkOrder)에서 자재를 투입해도 Stock이 감소하지 않음 - 견적(Order)이 확정되어도 재고 예약이 되지 않음 - 출하(Shipment)가 완료되어도 Stock이 감소하지 않음 **결과**: 재고현황 페이지가 실제 재고를 반영하지 못함 ### 1.2 목표 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 🎯 핵심 목표 │ ├─────────────────────────────────────────────────────────────────┤ │ 1. 입고 완료 → Stock 자동 증가 + StockLot 생성 │ │ 2. 자재 투입 → Stock 자동 차감 (FIFO 기반) │ │ 3. 견적 확정 → reserved_qty 증가 │ │ 4. 출하 완료 → stock_qty 차감 │ │ 5. 모든 변경에 대한 감사 로그 기록 │ └─────────────────────────────────────────────────────────────────┘ ``` ### 1.3 성공 기준 | 기준 | 측정 방법 | |------|----------| | 입고 → 재고 연동 | 입고 완료 시 Stock.stock_qty 자동 증가 확인 | | 생산 → 재고 연동 | 자재 투입 시 Stock.stock_qty 자동 감소 확인 | | 견적 → 재고 연동 | 견적 확정 시 Stock.reserved_qty 증가 확인 | | 출하 → 재고 연동 | 출하 완료 시 Stock.stock_qty 감소 확인 | | 감사 로그 | 모든 재고 변경이 audit_logs에 기록됨 | | FIFO 적용 | StockLot이 fifo_order 순서대로 차감됨 | ### 1.4 변경 승인 정책 | 분류 | 예시 | 승인 | |------|------|------| | ✅ 즉시 가능 | 메서드 추가, 파라미터 추가, 문서 수정 | 불필요 | | ⚠️ 컨펌 필요 | Service 로직 변경, 새 이벤트 추가, 마이그레이션 | **필수** | | 🔴 금지 | 기존 API 응답 구조 변경, Stock 테이블 컬럼 삭제 | 별도 협의 | ### 1.5 준수 규칙 - `docs/standards/api-rules.md` - Service-First 패턴 - `docs/standards/quality-checklist.md` - 품질 체크리스트 - `docs/specs/database-schema.md` - DB 스키마 규칙 --- ## 2. 현재 시스템 분석 ### 2.1 데이터 모델 관계 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 현재 상태 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Item (품목) │ │ ↓ 1:1 │ │ Stock (재고현황) ←── 자동 업데이트 없음 ──┐ │ │ ↓ 1:N │ │ │ StockLot (LOT별 상세) ←── 자동 생성 없음 ─┤ │ │ │ │ │ Receiving (입고) ─── 연결 끊김 ────────────┤ │ │ WorkOrder (생산) ─── 연결 없음 ────────────┤ │ │ Order (견적/수주) ─── 연결 없음 ───────────┤ │ │ Shipment (출하) ─── 연결 없음 ─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 2.2 목표 데이터 흐름 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 목표 상태 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ [입고 완료] ──→ StockLot 생성 ──→ Stock.refreshFromLots() │ │ │ │ [자재 투입] ──→ StockLot 차감(FIFO) ──→ Stock.refreshFromLots()│ │ │ │ [견적 확정] ──→ Stock.reserved_qty 증가 │ │ │ │ [출하 완료] ──→ StockLot 차감 ──→ Stock.refreshFromLots() │ │ ──→ Stock.reserved_qty 감소 │ │ │ │ [모든 변경] ──→ AuditLog 기록 │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 2.3 핵심 파일 위치 | 구분 | 경로 | |------|------| | **Stock 모델** | `api/app/Models/Tenants/Stock.php` | | **StockLot 모델** | `api/app/Models/Tenants/StockLot.php` | | **StockService** | `api/app/Services/StockService.php` | | **ReceivingService** | `api/app/Services/ReceivingService.php` | | **WorkOrderService** | `api/app/Services/WorkOrderService.php` | | **OrderService** | `api/app/Services/OrderService.php` | --- ## 3. 대상 범위 ### Phase 1: 입고 → 재고 연동 (우선순위 1) ✅ 완료 | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 1.1 | StockService에 이벤트 기반 구조 설계 | ✅ | increaseFromReceiving(), getOrCreateStock() | | 1.2 | ReceivingService.process() 수정 - Stock 연동 | ✅ | StockService 호출 추가 | | 1.3 | StockLot 자동 생성 로직 구현 | ✅ | FIFO 순서 자동 계산 | | 1.4 | 감사 로그 통합 | ✅ | logStockChange() 구현 | | 1.5 | 단위 테스트 작성 | ⏭️ | 수동 테스트로 대체 | ### Phase 2: 생산 → 재고 연동 (우선순위 2) ✅ 완료 | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 2.1 | WorkOrderService에 BOM 기반 자재 조회 구현 | ✅ | getMaterials() 실제 재고 연동 | | 2.2 | 자재 투입 시 Stock 차감 로직 (FIFO) | ✅ | StockService.decreaseFIFO() | | 2.3 | 작업 완료 시 제품 Stock 증가 로직 | ⏭️ | 추후 구현 (생산품 LOT 생성 시) | | 2.4 | 단위 테스트 작성 | ⏭️ | 수동 테스트로 대체 | ### Phase 3: 견적/출하 → 재고 연동 (우선순위 3) ✅ 완료 | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 3.1 | Order 확정 시 reserved_qty 증가 로직 | ✅ | StockService.reserve(), reserveForOrder() | | 3.2 | Shipment 출하 시 stock_qty 차감 로직 | ✅ | StockService.decreaseForShipment() | | 3.3 | 예약 취소/변경 처리 로직 | ✅ | StockService.releaseReservation() | --- ## 4. 상세 설계 ### 4.1 StockService 이벤트 구조 ```php // api/app/Services/StockService.php class StockService { /** * 입고 완료 시 재고 증가 * @param Receiving $receiving * @return StockLot */ public function increaseFromReceiving(Receiving $receiving): StockLot { // 1. StockLot 생성 // 2. Stock.refreshFromLots() 호출 // 3. 감사 로그 기록 } /** * 자재 투입 시 재고 차감 (FIFO) * @param int $itemId * @param float $qty * @param string $reason (work_order, shipment 등) * @param int $referenceId * @return array 차감된 LOT 정보 */ public function decreaseFIFO(int $itemId, float $qty, string $reason, int $referenceId): array { // 1. StockLot을 fifo_order 순서로 조회 // 2. 필요 수량만큼 차감 (여러 LOT에 걸칠 수 있음) // 3. Stock.refreshFromLots() 호출 // 4. 감사 로그 기록 } /** * 재고 예약 * @param int $itemId * @param float $qty * @param int $orderId */ public function reserve(int $itemId, float $qty, int $orderId): void { // 1. Stock.reserved_qty 증가 // 2. Stock.available_qty 재계산 // 3. 감사 로그 기록 } /** * 예약 해제 */ public function releaseReservation(int $itemId, float $qty, int $orderId): void { // reserved_qty 감소 } } ``` ### 4.2 ReceivingService 수정 사항 ```php // api/app/Services/ReceivingService.php - process() 메서드 수정 public function process(Receiving $receiving, array $data): Receiving { return DB::transaction(function () use ($receiving, $data) { // 기존 로직 유지 $receiving->update([ 'receiving_qty' => $data['receiving_qty'], 'receiving_date' => $data['receiving_date'], 'lot_no' => $data['lot_no'], 'status' => 'completed', ]); // 🆕 재고 연동 추가 app(StockService::class)->increaseFromReceiving($receiving); return $receiving->fresh(); }); } ``` ### 4.3 WorkOrderService 수정 사항 ```php // api/app/Services/WorkOrderService.php - registerMaterialInput() 수정 public function registerMaterialInput(WorkOrder $workOrder, array $data): void { DB::transaction(function () use ($workOrder, $data) { // 기존 감사 로그 유지 // 🆕 재고 차감 추가 $stockService = app(StockService::class); foreach ($data['materials'] as $material) { $stockService->decreaseFIFO( itemId: $material['item_id'], qty: $material['qty'], reason: 'work_order_input', referenceId: $workOrder->id ); } }); } ``` ### 4.4 감사 로그 구조 | 필드 | 값 | |------|------| | `auditable_type` | `Stock` | | `auditable_id` | Stock ID | | `event` | `stock_increase`, `stock_decrease`, `stock_reserve` | | `old_values` | 변경 전 수량 | | `new_values` | 변경 후 수량 + 사유 + 참조 ID | --- ## 5. 작업 절차 ### Step 1: Phase 1 - 입고 → 재고 연동 ``` 1.1 StockService 이벤트 메서드 추가 ├── increaseFromReceiving() 구현 ├── 감사 로그 통합 └── 단위 테스트 1.2 ReceivingService.process() 수정 ├── 기존 로직 분석 ├── StockService 호출 추가 └── 트랜잭션 보장 1.3 StockLot 자동 생성 ├── Receiving 정보로 StockLot 생성 ├── fifo_order 자동 계산 └── Stock.refreshFromLots() 호출 1.4 테스트 및 검증 ├── 입고 생성 → 입고처리 → Stock 확인 └── 감사 로그 확인 ``` ### Step 2: Phase 2 - 생산 → 재고 연동 ``` 2.1 BOM 기반 자재 조회 구현 ├── 품목의 BOM 정보 조회 ├── Mock 데이터 제거 └── 실제 자재 목록 반환 2.2 자재 투입 시 Stock 차감 ├── decreaseFIFO() 구현 ├── 여러 LOT 걸쳐 차감 처리 └── 재고 부족 시 예외 처리 2.3 작업 완료 시 제품 Stock 증가 ├── 생산된 제품의 StockLot 생성 ├── Stock.refreshFromLots() 호출 └── 감사 로그 기록 ``` ### Step 3: Phase 3 - 견적/출하 → 재고 연동 ``` 3.1 Order 확정 시 예약 ├── reserve() 호출 ├── available_qty 감소 └── 오버부킹 방지 검증 3.2 Shipment 출하 시 차감 ├── decreaseFIFO() 호출 ├── reserved_qty 동시 감소 └── 감사 로그 기록 ``` --- ## 6. 컨펌 대기 목록 > API 내부 로직 변경 등 승인 필요 항목 | # | 항목 | 변경 내용 | 영향 범위 | 상태 | |---|------|----------|----------|------| | 1 | ReceivingService.process() | Stock 연동 로직 추가 | 입고 프로세스 | ⏳ 대기 | | 2 | WorkOrderService.registerMaterialInput() | Stock 차감 로직 추가 | 생산 프로세스 | ⏳ 대기 | | 3 | ShipmentService (신규) | Stock 차감 로직 추가 | 출하 프로세스 | ⏳ 대기 | --- ## 7. 리스크 및 대응 ### 7.1 데이터 정합성 리스크 | 리스크 | 확률 | 영향 | 대응 | |--------|------|------|------| | 트랜잭션 실패 시 Stock만 변경됨 | 중 | 높음 | DB 트랜잭션으로 원자성 보장 | | 동시 요청 시 재고 충돌 | 중 | 높음 | 비관적 락(FOR UPDATE) 적용 | | 재고 부족 상태에서 차감 시도 | 높음 | 중 | 사전 검증 + 예외 처리 | ### 7.2 성능 리스크 | 리스크 | 확률 | 영향 | 대응 | |--------|------|------|------| | LOT가 많을 경우 FIFO 조회 느림 | 낮음 | 중 | fifo_order 인덱스 확인 | | refreshFromLots() 빈번 호출 | 중 | 낮음 | 필요 시에만 호출 (이미 구현됨) | --- ## 8. 변경 이력 | 날짜 | 항목 | 변경 내용 | 파일 | 승인 | |------|------|----------|------|------| | 2025-01-26 | Phase 3 | 견적/출하→재고 연동 구현 완료 | StockService, OrderService, ShipmentService | ✅ | | 2025-01-26 | Phase 2 | 생산→재고 연동 구현 완료 | StockService, WorkOrderService | ✅ | | 2025-01-26 | Phase 1 | 입고→재고 연동 구현 완료 | StockService, ReceivingService | ✅ | | 2025-01-26 | - | 문서 초안 작성 | - | - | --- ## 9. 참고 문서 - **API 규칙**: `docs/standards/api-rules.md` - **품질 체크리스트**: `docs/standards/quality-checklist.md` - **DB 스키마**: `docs/specs/database-schema.md` --- ## 10. 자기완결성 점검 결과 ### 10.1 체크리스트 검증 | # | 검증 항목 | 상태 | 비고 | |---|----------|:----:|------| | 1 | 작업 목적이 명확한가? | ✅ | 1.1 배경, 1.2 목표 | | 2 | 성공 기준이 정의되어 있는가? | ✅ | 1.3 성공 기준 | | 3 | 작업 범위가 구체적인가? | ✅ | 섹션 3 대상 범위 | | 4 | 의존성이 명시되어 있는가? | ✅ | 2.3 핵심 파일 위치 | | 5 | 참고 파일 경로가 정확한가? | ✅ | 섹션 9 참고 문서 | | 6 | 단계별 절차가 실행 가능한가? | ✅ | 섹션 5 작업 절차 | | 7 | 검증 방법이 명시되어 있는가? | ✅ | 1.3 성공 기준 | | 8 | 모호한 표현이 없는가? | ✅ | | ### 10.2 새 세션 시뮬레이션 테스트 | 질문 | 답변 가능 | 참조 섹션 | |------|:--------:|----------| | Q1. 이 작업의 목적은 무엇인가? | ✅ | 1.1 배경, 1.2 목표 | | Q2. 어디서부터 시작해야 하는가? | ✅ | 3. 대상 범위 Phase 1 | | Q3. 어떤 파일을 수정해야 하는가? | ✅ | 2.3 핵심 파일 위치 | | Q4. 작업 완료 확인 방법은? | ✅ | 1.3 성공 기준 | | Q5. 막혔을 때 참고 문서는? | ✅ | 9. 참고 문서 | **결과**: 5/5 통과 → ✅ 자기완결성 확보 --- *이 문서는 /sc:plan 스킬로 생성되었습니다.*