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

16 KiB
Raw Blame History

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_fromeffective_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에 보존한다.

{
  "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. 관련 문서


최종 업데이트: 2026-03-19