# 단가관리 API 개선 요청서

> **작성일**: 2025-12-08
> **요청자**: 프론트엔드 개발팀
> **대상**: sam-api 백엔드 팀

---

## 1. 현황 요약

### 현재 API 구조
| Endpoint | Method | 상태 |
|----------|--------|------|
| `/api/v1/pricing` | GET | 목록 조회 |
| `/api/v1/pricing/show` | GET | 단일 가격 조회 |
| `/api/v1/pricing/bulk` | POST | 일괄 가격 조회 |
| `/api/v1/pricing/upsert` | POST | 등록/수정 |
| `/api/v1/pricing/{id}` | DELETE | 삭제 |

### ✅ 이미 지원됨 (품목 정보)
- `item_type_code` (품목유형) - PriceHistory 테이블
- `item_code`, `item_name`, `specification`, `unit` - item 관계 JOIN으로 조회 가능

### ❌ 문제점 (단가 상세 정보)
- 프론트엔드 단가관리 화면에서 요구하는 **단가 계산 필드** 대부분 누락
- 현재 `price_histories` 테이블은 **단순 가격 이력**만 저장 (`price` 단일 필드)
- 프론트엔드는 **원가 계산, 마진 관리, 리비전 관리** 기능 필요

---

## 2. 테이블 스키마 변경 요청

### 2.1 `price_histories` 테이블 필드 추가

```sql
ALTER TABLE price_histories ADD COLUMN purchase_price DECIMAL(15,4) NULL COMMENT '매입단가(입고가)';
ALTER TABLE price_histories ADD COLUMN processing_cost DECIMAL(15,4) NULL COMMENT '가공비';
ALTER TABLE price_histories ADD COLUMN loss_rate DECIMAL(5,2) NULL COMMENT 'LOSS율(%)';
ALTER TABLE price_histories ADD COLUMN rounding_rule ENUM('round','ceil','floor') DEFAULT 'round' COMMENT '반올림 규칙';
ALTER TABLE price_histories ADD COLUMN rounding_unit INT DEFAULT 1 COMMENT '반올림 단위(1,10,100,1000)';
ALTER TABLE price_histories ADD COLUMN margin_rate DECIMAL(5,2) NULL COMMENT '마진율(%)';
ALTER TABLE price_histories ADD COLUMN sales_price DECIMAL(15,4) NULL COMMENT '판매단가';
ALTER TABLE price_histories ADD COLUMN supplier VARCHAR(255) NULL COMMENT '공급업체';
ALTER TABLE price_histories ADD COLUMN author VARCHAR(100) NULL COMMENT '작성자';
ALTER TABLE price_histories ADD COLUMN receive_date DATE NULL COMMENT '입고일';
ALTER TABLE price_histories ADD COLUMN note TEXT NULL COMMENT '비고';
ALTER TABLE price_histories ADD COLUMN revision_number INT DEFAULT 0 COMMENT '리비전 번호';
ALTER TABLE price_histories ADD COLUMN is_final BOOLEAN DEFAULT FALSE COMMENT '최종 확정 여부';
ALTER TABLE price_histories ADD COLUMN finalized_at DATETIME NULL COMMENT '확정일시';
ALTER TABLE price_histories ADD COLUMN finalized_by INT NULL COMMENT '확정자 ID';
ALTER TABLE price_histories ADD COLUMN status ENUM('draft','active','inactive','finalized') DEFAULT 'draft' COMMENT '상태';
```

### 2.2 기존 `price` 필드 처리 방안

**옵션 A (권장)**: `price` 필드를 `sales_price`로 마이그레이션
```sql
UPDATE price_histories SET sales_price = price WHERE price_type_code = 'SALE';
UPDATE price_histories SET purchase_price = price WHERE price_type_code = 'PURCHASE';
-- 이후 price 필드 deprecated 또는 삭제
```

**옵션 B**: `price` 필드 유지, 새 필드와 병행 사용
- 기존 로직 호환성 유지
- 점진적 마이그레이션

---

## 3. API 엔드포인트 수정 요청

### 3.1 `POST /api/v1/pricing/upsert` 수정

**현재 Request Body:**
```json
{
  "item_type_code": "PRODUCT",
  "item_id": 10,
  "price_type_code": "SALE",
  "client_group_id": 1,
  "price": 50000.00,
  "started_at": "2025-01-01",
  "ended_at": "2025-12-31"
}
```

**요청 Request Body:**
```json
{
  "item_type_code": "PRODUCT",
  "item_id": 10,
  "client_group_id": 1,

  "purchase_price": 45000,
  "processing_cost": 5000,
  "loss_rate": 3.5,
  "rounding_rule": "round",
  "rounding_unit": 100,
  "margin_rate": 20.0,
  "sales_price": 60000,

  "supplier": "ABC공급",
  "author": "홍길동",
  "receive_date": "2025-01-01",
  "started_at": "2025-01-01",
  "ended_at": null,
  "note": "2025년 1분기 단가",

  "is_revision": false,
  "revision_reason": "가격 인상"
}
```

### 3.2 `GET /api/v1/pricing` 수정 (목록 조회)

**현재 Response:**
```json
{
  "data": {
    "data": [
      {
        "id": 1,
        "item_type_code": "PRODUCT",
        "item_id": 10,
        "price_type_code": "SALE",
        "price": 50000,
        "started_at": "2025-01-01"
      }
    ]
  }
}
```

**요청 Response:**
```json
{
  "data": {
    "data": [
      {
        "id": 1,
        "item_type_code": "PRODUCT",
        "item_id": 10,
        "item_code": "SCREEN-001",
        "item_name": "스크린 셔터 기본형",
        "specification": "표준형",
        "unit": "SET",

        "purchase_price": 45000,
        "processing_cost": 5000,
        "loss_rate": 3.5,
        "margin_rate": 20.0,
        "sales_price": 60000,

        "started_at": "2025-01-01",
        "ended_at": null,
        "status": "active",
        "revision_number": 1,
        "is_final": false,

        "supplier": "ABC공급",
        "created_at": "2025-01-01 10:00:00"
      }
    ]
  }
}
```

**핵심 변경**: 품목 정보 JOIN 필요 (`item_masters` 또는 `products`/`materials` 테이블)

---

## 4. 신규 API 엔드포인트 요청

### 4.1 품목 기반 단가 현황 조회 (신규)

**용도**: 품목 마스터 기준으로 단가 등록/미등록 현황 조회

**Endpoint**: `GET /api/v1/pricing/by-items`

**Query Parameters:**
| 파라미터 | 타입 | 설명 |
|---------|------|------|
| `item_type_code` | string | 품목 유형 (FG, PT, SM, RM, CS) |
| `search` | string | 품목코드/품목명 검색 |
| `status` | string | `all`, `registered`, `not_registered` |
| `size` | int | 페이지당 항목 수 |
| `page` | int | 페이지 번호 |

**Response:**
```json
{
  "data": {
    "data": [
      {
        "item_id": 1,
        "item_code": "SCREEN-001",
        "item_name": "스크린 셔터 기본형",
        "item_type": "FG",
        "specification": "표준형",
        "unit": "SET",

        "pricing_id": null,
        "has_pricing": false,
        "purchase_price": null,
        "sales_price": null,
        "margin_rate": null,
        "status": "not_registered"
      },
      {
        "item_id": 2,
        "item_code": "GR-001",
        "item_name": "가이드레일 130×80",
        "item_type": "PT",
        "specification": "130×80×2438",
        "unit": "EA",

        "pricing_id": 5,
        "has_pricing": true,
        "purchase_price": 45000,
        "sales_price": 60000,
        "margin_rate": 20.0,
        "effective_date": "2025-01-01",
        "status": "active",
        "revision_number": 1,
        "is_final": false
      }
    ],
    "stats": {
      "total_items": 100,
      "registered": 45,
      "not_registered": 55,
      "finalized": 10
    }
  }
}
```

### 4.2 단가 이력 조회 (신규)

**Endpoint**: `GET /api/v1/pricing/{id}/revisions`

**Response:**
```json
{
  "data": [
    {
      "revision_number": 2,
      "revision_date": "2025-06-01",
      "revision_by": "김철수",
      "revision_reason": "원자재 가격 인상",
      "previous_purchase_price": 40000,
      "previous_sales_price": 55000,
      "new_purchase_price": 45000,
      "new_sales_price": 60000
    },
    {
      "revision_number": 1,
      "revision_date": "2025-01-01",
      "revision_by": "홍길동",
      "revision_reason": "최초 등록",
      "previous_purchase_price": null,
      "previous_sales_price": null,
      "new_purchase_price": 40000,
      "new_sales_price": 55000
    }
  ]
}
```

### 4.3 단가 확정 (신규)

**Endpoint**: `POST /api/v1/pricing/{id}/finalize`

**Request Body:**
```json
{
  "finalize_reason": "2025년 1분기 단가 확정"
}
```

**Response:**
```json
{
  "data": {
    "id": 5,
    "is_final": true,
    "finalized_at": "2025-12-08 14:30:00",
    "finalized_by": 1,
    "status": "finalized"
  },
  "message": "단가가 최종 확정되었습니다."
}
```

---

## 5. 프론트엔드 타입 참조

프론트엔드에서 사용하는 타입 정의 (`src/components/pricing/types.ts`):

```typescript
interface PricingData {
  id: string;
  itemId: string;
  itemCode: string;
  itemName: string;
  itemType: string;
  specification?: string;
  unit: string;

  // 단가 정보
  effectiveDate: string;      // started_at
  receiveDate?: string;       // receive_date
  author?: string;            // author
  purchasePrice?: number;     // purchase_price
  processingCost?: number;    // processing_cost
  loss?: number;              // loss_rate
  roundingRule?: RoundingRule; // rounding_rule
  roundingUnit?: number;      // rounding_unit
  marginRate?: number;        // margin_rate
  salesPrice?: number;        // sales_price
  supplier?: string;          // supplier
  note?: string;              // note

  // 리비전 관리
  currentRevision: number;    // revision_number
  isFinal: boolean;           // is_final
  revisions?: PricingRevision[];
  finalizedDate?: string;     // finalized_at
  finalizedBy?: string;       // finalized_by
  status: PricingStatus;      // status
}
```

---

## 6. 우선순위

| 순위 | 항목 | 중요도 |
|------|------|--------|
| 1 | 테이블 스키마 변경 (필드 추가) | 🔴 필수 |
| 2 | `POST /pricing/upsert` 수정 | 🔴 필수 |
| 3 | `GET /pricing/by-items` 신규 | 🔴 필수 |
| 4 | `GET /pricing` 응답 확장 | 🟡 중요 |
| 5 | `GET /pricing/{id}/revisions` 신규 | 🟡 중요 |
| 6 | `POST /pricing/{id}/finalize` 신규 | 🟢 권장 |

---

## 7. 질문/협의 사항

1. **기존 price 필드 처리**: 마이그레이션 vs 병행 사용?
2. **리비전 테이블 분리**: `price_history_revisions` 별도 테이블 vs 현재 테이블 확장?
3. **품목 연결**: `item_masters` 테이블 사용 vs `products`/`materials` 각각 JOIN?

---

**연락처**: 프론트엔드 개발팀
**관련 파일**: `src/components/pricing/types.ts`