Files
sam-docs/front/[API-2025-12-08] pricing-api-enhancement-request.md
hskwon e152de469a docs: Client API Flow Tester JSON 추가 및 문서 업데이트
- Client API CRUD 플로우 테스트 JSON 추가 (11단계)
- Client Group API CRUD 플로우 테스트 JSON 추가 (10단계)
- client_groups 테이블 스키마 정정 (group_code, group_name, price_rate 필드)
- API 응답 구조 및 필드 타입 주의사항 문서화
- Flow Tester 섹션 추가 (사용법 및 주의사항)
2025-12-08 20:19:32 +09:00

358 lines
9.6 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.

# 단가관리 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`