Files
sam-docs/rules/pricing-policy.md
김보곤 eee610cb8e docs: [pricing] 단가 정책 문서 전면 개편
- 6 Depth 단가 체계 정의 (매입→표준원가→판매→견적→수주→매출)
- 전체 단가 흐름도 및 테이블별 저장 위치 매핑
- 고객그룹 차등가, 상태관리, 리비전 관리 정리
- Depth 기반 버전관리 개선방향 4Phase 기획 추가
2026-03-19 23:28:48 +09:00

406 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SAM 단가 정책 (Pricing Policy)
> **작성일**: 2025-12-08
> **최종 갱신**: 2026-03-19
> **상태**: 설계 확정 + 개선방향 기획 (Depth 버전관리)
---
## 1. 개요
### 1.1 목적
SAM 시스템 내 모든 단가의 **유형 정의**, **계산 공식**, **흐름 추적**, **버전 관리**를 명확히 한다.
### 1.2 핵심 원칙
| 원칙 | 설명 |
|------|------|
| **Depth 기반 흐름** | 매입단가 → 표준원가 → 판매단가 → 견적단가 → 수주단가 → 매출단가 |
| **실제원가 우선** | 수입검사 입고단가 > 표준원가 |
| **시점 기반 유효성** | `effective_from` ~ `effective_to` 기간 내 유효 |
| **리비전 추적** | 모든 변경 사항 이력 보관 (`price_revisions`) |
| **확정 후 불변** | `finalized` 상태는 수정/삭제 불가 |
| **단가 복제 원칙** | 하위 단계(견적→수주→매출)로 복제된 단가는 원본과 독립 |
### 1.3 단가 유형 정의 (6 Depth)
```
Depth 0 매입단가 prices.purchase_price 공급업체로부터의 구매가
Depth 1 표준원가 (매입단가 + 가공비) × LOSS 제조 기준 원가
Depth 2 판매단가 prices.sales_price 표준원가 + 마진, 반올림 적용
Depth 3 견적단가 quote_items.unit_price BOM 산출 또는 수동 입력
Depth 4 수주단가 order_items.unit_price 견적에서 복제 (확정가)
Depth 5 매출단가 sales.supply_amount 수주에서 복제 (최종가)
```
---
## 2. 단가 흐름도
### 2.1 전체 흐름
```
┌─────────────────────────────────────────────────────────────┐
│ Depth 0: 매입단가 │
│ prices.purchase_price + prices.processing_cost │
│ 소스: 공급업체 견적, 수입검사 입고단가 │
└──────────────────────┬──────────────────────────────────────┘
× (1 + LOSS율/100)
┌──────────────────────┴──────────────────────────────────────┐
│ Depth 1: 표준원가 │
│ = (매입단가 + 가공비) × (1 + LOSS율/100) │
└──────────────────────┬──────────────────────────────────────┘
× (1 + 마진율/100) + 반올림
┌──────────────────────┴──────────────────────────────────────┐
│ Depth 2: 판매단가 │
│ prices.sales_price (고객그룹별 차등가 가능) │
└──────────────────────┬──────────────────────────────────────┘
↓ BOM 산출 or 수동 입력
┌──────────────────────┴──────────────────────────────────────┐
│ Depth 3: 견적단가 │
│ quote_items.unit_price (BOM 부품별 판매단가 합산 = 완제품가) │
│ quotes.total_amount = 재료비 + 노무비 + 시공비 - 할인 │
└──────────────────────┬──────────────────────────────────────┘
↓ convertToOrder() 복제
┌──────────────────────┴──────────────────────────────────────┐
│ Depth 4: 수주단가 │
│ order_items.unit_price (견적단가 복제) │
│ + supply_amount, tax_amount, total_amount 재계산 │
└──────────────────────┬──────────────────────────────────────┘
↓ createFromOrder/Shipment 복제
┌──────────────────────┴──────────────────────────────────────┐
│ Depth 5: 매출단가 │
│ sales.supply_amount, tax_amount, total_amount (수주 복제) │
└─────────────────────────────────────────────────────────────┘
```
### 2.2 단가 저장 위치
| Depth | 테이블 | 단가 컬럼 | 금액 컬럼 |
|:-----:|--------|----------|----------|
| 0 | `prices` | `purchase_price`, `processing_cost` | — |
| 1 | (계산값) | — | `(purchase_price + processing_cost) × (1 + loss_rate)` |
| 2 | `prices` | `sales_price` | — |
| 3 | `quote_items` | `unit_price` | `total_price` |
| 3 | `quotes` | — | `material_cost`, `labor_cost`, `install_cost`, `total_amount` |
| 4 | `order_items` | `unit_price` | `supply_amount`, `tax_amount`, `total_amount` |
| 4 | `orders` | — | `supply_amount`, `tax_amount`, `total_amount` |
| 5 | `sales` | — | `supply_amount`, `tax_amount`, `total_amount` |
---
## 3. 단가 계산 공식
### 3.1 표준원가 (Depth 1)
```
표준원가 = (매입단가 + 가공비) × (1 + LOSS율/100)
예시: (10,000 + 2,000) × 1.05 = 12,600원
```
### 3.2 판매단가 (Depth 2)
```
판매단가 = 반올림(표준원가 × (1 + 마진율/100), 반올림단위, 반올림규칙)
예시: round(12,600 × 1.25 / 100) × 100 = 15,800원
```
| 반올림규칙 | 설명 | 15,750 (단위 100) |
|-----------|------|------------------|
| `round` | 반올림 | 15,800 |
| `ceil` | 올림 | 15,800 |
| `floor` | 버림 | 15,700 |
### 3.3 원가 조회 우선순위
| 순위 | 소스 | 조건 |
|:----:|------|------|
| 1 | `material_receipts.purchase_price_excl_vat` | 자재만, 최근 입고 기준 |
| 2 | `prices.purchase_price` | 해당 일자 유효 단가 |
| 3 | NULL | 단가 미등록 → 경고 반환 |
### 3.4 견적단가 (Depth 3) — BOM 산출
```
견적 품목 금액 = 계산수량 × 단가
견적 소계 = 재료비 + 노무비 + 시공비
견적 총액 = 소계 - 할인금액
```
BOM 자동산출 시 `EstimatePriceService`가 items + item_details + prices 조인으로 판매단가 조회.
### 3.5 수주단가 (Depth 4)
```
공급가액 = 수량 × 단가 - 할인금액
세액 = 공급가액 × 10%
총액 = 공급가액 + 세액
```
### 3.6 매출단가 (Depth 5)
수주의 `supply_amount`, `tax_amount`, `total_amount`를 그대로 복제.
---
## 4. 고객그룹 차등가
### 4.1 구조
`client_groups` 테이블:
| 필드 | 설명 |
|------|------|
| `group_code` | 그룹 코드 |
| `group_name` | 그룹명 |
| `price_rate` | 가격 배율 (1.0=기본, 0.9=10%할인, 1.1=10%인상) |
### 4.2 조회 우선순위
```
1순위: prices WHERE client_group_id = [거래처의 그룹ID] → 그룹 전용 단가
2순위: prices WHERE client_group_id IS NULL → 기본 단가
```
`Price::getCurrentPrice($tenantId, $itemTypeCode, $itemId, $clientGroupId)` 메서드가 이 로직을 처리한다.
---
## 5. 상태 관리
### 5.1 상태 전이
```
draft ──→ active ──→ finalized
(초안) (활성) (확정, 불변)
└──→ inactive (비활성)
```
### 5.2 상태별 권한
| 상태 | 조회 | 수정 | 삭제 | 확정 |
|------|:----:|:----:|:----:|:----:|
| `draft` | ✅ | ✅ | ✅ | ✅ |
| `active` | ✅ | ✅ | ✅ | ✅ |
| `inactive` | ✅ | ✅ | ✅ | ❌ |
| `finalized` | ✅ | ❌ | ❌ | ❌ |
### 5.3 확정 규칙
- `sales_price` 또는 `purchase_price` 중 하나 이상 존재해야 확정 가능
- 확정 시: `is_final = true`, `status = 'finalized'`, `finalized_at/by` 기록
- 확정 후: 수정 필요 시 **새 단가 레코드 생성** (기존 단가의 `effective_to` 자동 설정)
---
## 6. 리비전 관리
### 6.1 리비전 생성 시점
| 이벤트 | 리비전 | 설명 |
|--------|:------:|------|
| 최초 등록 | ✅ | `revision_number = 1`, `before_snapshot = NULL` |
| 수정 | ✅ | `revision_number++`, 변경 전/후 기록 |
| 삭제 | ✅ | soft delete, 삭제 전 스냅샷 |
| 확정 | ✅ | 확정 시점 스냅샷 |
### 6.2 기간 중복 자동 처리
```
기존: 2025-01-01 ~ NULL (무기한)
신규: 2025-06-01 ~ NULL
→ 기존의 effective_to를 2025-05-31로 자동 설정
→ 신규 단가 등록
```
---
## 7. 비즈니스 규칙
| 규칙 | 설명 |
|------|------|
| **R1** | `effective_from`은 필수 |
| **R2** | `effective_from``effective_to` |
| **R3** | 동일 품목+고객그룹+적용시작일 중복 불가 |
| **R4** | 확정된 단가는 수정/삭제 불가 |
| **R5** | 마진율: 0% 이상 (상한 없음) |
| **R6** | LOSS율: 0~100% |
| **R7** | 반올림단위: 1, 10, 100, 1000 중 택1 |
| **R8** | Soft Delete 적용 (삭제 후 리비전에서 조회 가능) |
---
## 8. API 엔드포인트
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/api/v1/pricing` | 단가 목록 |
| GET | `/api/v1/pricing/{id}` | 단가 상세 (리비전 10개 포함) |
| POST | `/api/v1/pricing` | 단가 등록 |
| PUT | `/api/v1/pricing/{id}` | 단가 수정 (`change_reason` 선택) |
| DELETE | `/api/v1/pricing/{id}` | 단가 삭제 |
| POST | `/api/v1/pricing/{id}/finalize` | 단가 확정 |
| POST | `/api/v1/pricing/by-items` | 품목별 단가 현황 |
| GET | `/api/v1/pricing/{id}/revisions` | 변경 이력 |
| POST | `/api/v1/pricing/cost` | 원가 조회 |
| GET | `/api/v1/pricing/stats` | 단가 통계 |
| DELETE | `/api/v1/pricing/bulk` | 일괄 삭제 |
---
## 9. 핵심 코드 위치
| 구분 | 파일 | 역할 |
|------|------|------|
| 모델 | `api/app/Models/Products/Price.php` | `getCurrentPrice()`, `calculateSalesPrice()`, `getSalesPriceByItemCode()` |
| 모델 | `api/app/Models/Products/PriceRevision.php` | 변경 이력 (Immutable) |
| 서비스 | `api/app/Services/PricingService.php` | 단가 CRUD, 리비전, 확정, 원가 조회 |
| 서비스 | `api/app/Services/Quote/EstimatePriceService.php` | BOM 자동산출 시 부품 단가 조회 |
| 서비스 | `api/app/Services/Quote/QuoteService.php` | 견적→수주 전환 (`convertToOrder`) |
| 모델 | `api/app/Models/Orders/OrderItem.php` | `createFromQuoteItem()`, 금액 재계산 |
| 모델 | `api/app/Models/Tenants/Sale.php` | `createFromOrder()`, `createFromShipment()` |
---
## 10. 개선방향: Depth 기반 단가 버전관리
> **상태**: 기획 단계
> **배경**: 현재 원자재 가격 인상 시 하위 Depth(판매단가, 견적단가)에 대한 영향 분석이 수동으로 이루어지고 있다. Depth 개념을 도입하여 단가 변경의 **상향 전파(Cascade)** 와 **영향 분석(Impact Analysis)** 을 체계화한다.
### 10.1 현재 한계
| 문제 | 설명 |
|------|------|
| **단절된 추적** | 매입단가 변경 시 어떤 판매단가/견적에 영향이 있는지 추적 불가 |
| **수동 갱신** | 원자재 인상 → 판매단가 수동 재계산 → 견적 재작성 필요 |
| **스냅샷 부재** | 견적/수주 시점의 Depth 0~2 단가를 역추적할 수 없음 |
| **일괄 변경 불가** | 특정 공급업체 자재 10% 인상 시 관련 판매단가 일괄 갱신 불가 |
### 10.2 개선 방향
#### Phase 1: 단가 연쇄 관계 추적
`prices` 테이블에 Depth 관계를 기록하여, 하위 Depth 단가가 어떤 상위 Depth 단가에서 파생되었는지 추적한다.
```
신규 필드 (prices 테이블):
price_depth TINYINT — 0(매입), 2(판매) 등
parent_price_id BIGINT — 이 단가의 원천 단가 ID (NULL=최상위)
price_version VARCHAR(20) — 단가 버전 (예: 'v2026-Q1')
```
**효과**: 매입단가(Depth 0) 변경 시 → 해당 품목의 판매단가(Depth 2) 목록을 즉시 조회 가능.
#### Phase 2: 변경 영향 분석 API
매입단가 변경 전 영향 범위를 분석하는 API를 제공한다.
```
POST /api/v1/pricing/impact-analysis
Body: { "price_id": 123, "new_purchase_price": 12000 }
Response:
{
"affected_sales_prices": [
{ "id": 456, "item_name": "가이드레일", "current": 15800, "projected": 17400, "diff": "+10.1%" }
],
"affected_quotes": [
{ "id": 789, "quote_number": "KD-SC-260301-01", "status": "draft", "impact": "+52,000원" }
],
"affected_orders": [
{ "id": 101, "order_number": "ORD-260215-001", "status": "confirmed", "impact": "변경 불가 (확정)" }
]
}
```
#### Phase 3: 단가 버전 그룹 관리
분기/반기 단위로 단가 버전을 묶어 관리한다.
```
신규 테이블: price_versions
id, tenant_id
version_code VARCHAR(20) — 'v2026-Q1', 'v2026-H1'
version_name VARCHAR(100) — '2026년 1분기 단가'
base_version_id BIGINT — 이전 버전 ID (NULL=최초)
effective_from DATE — 적용 시작일
status ENUM — draft/active/archived
change_summary JSON — { total_items: 150, avg_increase: 3.2% }
created_by, created_at
```
**워크플로우**:
```
1. 신규 버전 생성 (draft)
→ 이전 버전의 모든 단가를 복사
2. 변경 대상 품목의 매입단가 수정
→ 판매단가 자동 재계산 (Cascade)
3. 영향 분석 리포트 생성
→ 변경 전후 비교, 마진 변화 시각화
4. 승인 → 버전 활성화 (active)
→ effective_from 일자부터 신규 단가 적용
5. 이전 버전 자동 종료 (archived)
```
#### Phase 4: 견적/수주 단가 스냅샷
견적/수주 생성 시점의 Depth 0~2 단가를 `calculation_inputs` JSON에 보존한다.
```json
{
"price_snapshot": {
"version": "v2026-Q1",
"captured_at": "2026-03-15",
"items": [
{
"item_id": 100,
"item_name": "가이드레일",
"depth_0": { "purchase_price": 10000, "processing_cost": 2000, "loss_rate": 5 },
"depth_2": { "sales_price": 15800, "margin_rate": 25 },
"depth_3": { "unit_price": 15800, "quantity": 2.5, "total": 39500 }
}
]
}
}
```
**효과**: 수주 완료 후 원가 변동이 발생해도, 해당 수주 시점의 원가 구조를 완전히 역추적 가능.
### 10.3 기대 효과
| 항목 | 현재 | 개선 후 |
|------|------|--------|
| 원자재 인상 반영 | 수동 (1~2일) | 자동 Cascade (즉시) |
| 영향 범위 파악 | 불가능 | Impact Analysis API |
| 단가 이력 비교 | 개별 리비전만 | 버전 간 일괄 비교 |
| 견적 원가 역추적 | 불가능 | 스냅샷 보존 |
| 분기 단가 갱신 | 품목별 수동 | 버전 복사 + 일괄 변경 |
### 10.4 구현 우선순위
| Phase | 내용 | 난이도 | 우선순위 |
|:-----:|------|:------:|:-------:|
| 1 | 단가 연쇄 관계 (parent_price_id) | 낮음 | 🔴 필수 |
| 2 | 변경 영향 분석 API | 중간 | 🟡 중요 |
| 3 | 단가 버전 그룹 관리 | 높음 | 🟢 권장 |
| 4 | 견적/수주 스냅샷 보존 | 낮음 | 🟡 중요 |
---
## 11. 관련 문서
- [품목 정책](item-policy.md) — 품목 유형 체계 (FG, PT, SM, RM, CS)
- [견적 시스템](../features/quotes/README.md) — BOM 산출, 견적 생성 흐름
- [API 개발 규칙](../dev/standards/api-rules.md) — Service-First 패턴
- [DB 스키마](../system/database/README.md) — 테이블 관계
---
**최종 업데이트**: 2026-03-19