Files
sam-docs/front/[API-2025-12-08] pricing-api-enhancement-request.md

379 lines
9.9 KiB
Markdown
Raw Normal View History

# 단가관리 API 분석 및 구현 현황
> **작성일**: 2025-12-08
> **최종 업데이트**: 2025-12-08
> **상태**: ✅ **백엔드 API 구현 완료**
---
## 1. 구현 현황 요약
### ✅ 현재 API 구조 (구현 완료)
| Method | Endpoint | 설명 | 상태 |
|--------|----------|------|------|
| `GET` | `/api/v1/pricing` | 목록 조회 (페이지네이션, 필터) | ✅ 완료 |
| `GET` | `/api/v1/pricing/{id}` | 단건 조회 | ✅ 완료 |
| `POST` | `/api/v1/pricing` | 등록 | ✅ 완료 |
| `PUT` | `/api/v1/pricing/{id}` | 수정 | ✅ 완료 |
| `DELETE` | `/api/v1/pricing/{id}` | 삭제 (soft delete) | ✅ 완료 |
| `POST` | `/api/v1/pricing/{id}/finalize` | 확정 | ✅ 완료 |
| `GET` | `/api/v1/pricing/{id}/revisions` | 변경이력 조회 | ✅ 완료 |
| `POST` | `/api/v1/pricing/by-items` | 품목별 단가 현황 (다건) | ✅ 완료 |
| `GET` | `/api/v1/pricing/cost` | 원가 조회 (수입검사 > 표준원가) | ✅ 완료 |
### ✅ 완료된 사항
- **새 테이블 구조**: `prices`, `price_revisions` 테이블 생성
- **기존 테이블 마이그레이션**: `price_histories``prices` 데이터 이관
- **모든 요청 필드**: 원가 계산, 마진 관리, 리비전 관리 기능 구현
---
## 2. 테이블 스키마 (구현 완료)
### 2.1 `prices` 테이블 (신규 생성) ✅
```sql
CREATE TABLE prices (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT NOT NULL,
-- 품목 연결
item_type_code VARCHAR(20) NOT NULL, -- 'PRODUCT' | 'MATERIAL'
item_id BIGINT NOT NULL,
client_group_id BIGINT NULL, -- NULL=기본가
-- 원가 정보
purchase_price DECIMAL(15,4) NULL, -- 매입단가
processing_cost DECIMAL(15,4) NULL, -- 가공비
loss_rate DECIMAL(5,2) NULL, -- LOSS율 (%)
-- 판매가 정보
margin_rate DECIMAL(5,2) NULL, -- 마진율 (%)
sales_price DECIMAL(15,4) NULL, -- 판매단가
rounding_rule ENUM('round','ceil','floor') DEFAULT 'round',
rounding_unit INT DEFAULT 1, -- 1, 10, 100, 1000
-- 메타 정보
supplier VARCHAR(255) NULL,
effective_from DATE NOT NULL,
effective_to DATE NULL,
note TEXT NULL,
-- 상태 관리
status ENUM('draft','active','inactive','finalized') DEFAULT 'draft',
is_final BOOLEAN DEFAULT FALSE,
finalized_at DATETIME NULL,
finalized_by BIGINT NULL,
-- 감사 컬럼
created_by, updated_by, deleted_by, timestamps, soft_deletes
);
```
### 2.2 `price_revisions` 테이블 (변경 이력) ✅
```sql
CREATE TABLE price_revisions (
id BIGINT PRIMARY KEY,
tenant_id BIGINT NOT NULL,
price_id BIGINT NOT NULL,
revision_number INT NOT NULL,
changed_at DATETIME NOT NULL,
changed_by BIGINT NOT NULL,
change_reason VARCHAR(500) NULL,
before_snapshot JSON NULL,
after_snapshot JSON NOT NULL
);
```
### 2.3 기존 `price_histories` 테이블 처리 ✅
-`prices` 테이블로 데이터 마이그레이션 완료
-`price_histories` 테이블 삭제됨
---
## 3. API 엔드포인트 상세 (구현 완료)
### 3.1 단가 등록 `POST /api/v1/pricing` ✅
**Request Body:**
```json
{
"item_type_code": "MATERIAL",
"item_id": 123,
"client_group_id": null,
"purchase_price": 1000,
"processing_cost": 100,
"loss_rate": 5,
"margin_rate": 20,
"rounding_rule": "round",
"rounding_unit": 10,
"supplier": "ABC상사",
"effective_from": "2025-01-01",
"effective_to": null,
"note": "2025년 1분기 단가",
"status": "active"
}
```
**자동 처리:**
- `sales_price` 자동 계산 (입력 안해도 됨)
- 기존 무기한 단가의 `effective_to` 자동 설정
- 최초 리비전 자동 생성
### 3.2 목록 조회 `GET /api/v1/pricing` ✅
**Query Parameters:**
| 파라미터 | 타입 | 설명 |
|---------|------|------|
| `page` | int | 페이지 번호 |
| `size` | int | 페이지당 개수 (max 100) |
| `q` | string | 검색어 (공급업체, 비고) |
| `item_type_code` | string | `PRODUCT` / `MATERIAL` |
| `item_id` | int | 품목 ID |
| `client_group_id` | int/null | 고객그룹 ID |
| `status` | string | `draft`, `active`, `inactive`, `finalized` |
| `valid_at` | date | 특정 일자에 유효한 단가 |
**Response:**
```json
{
"success": true,
"message": "message.fetched",
"data": {
"current_page": 1,
"data": [
{
"id": 1,
"item_type_code": "MATERIAL",
"item_id": 123,
"client_group_id": null,
"purchase_price": "1000.0000",
"processing_cost": "100.0000",
"loss_rate": "5.00",
"margin_rate": "20.00",
"sales_price": "1386.0000",
"rounding_rule": "round",
"rounding_unit": 10,
"supplier": "ABC상사",
"effective_from": "2025-01-01",
"effective_to": null,
"status": "active",
"is_final": false,
"client_group": null
}
],
"total": 1
}
}
```
---
## 4. 추가 API 엔드포인트 (구현 완료)
### 4.1 품목별 단가 현황 `POST /api/v1/pricing/by-items` ✅
**용도**: 여러 품목의 현재 유효한 단가를 한번에 조회
**Request Body:**
```json
{
"items": [
{ "item_type_code": "MATERIAL", "item_id": 123 },
{ "item_type_code": "MATERIAL", "item_id": 124 },
{ "item_type_code": "PRODUCT", "item_id": 10 }
],
"client_group_id": 1,
"date": "2025-12-08"
}
```
**조회 우선순위:**
1. 고객그룹별 단가 (`client_group_id` 지정 시)
2. 기본 단가 (`client_group_id = NULL`)
**Response:**
```json
{
"success": true,
"data": [
{
"item_type_code": "MATERIAL",
"item_id": 123,
"price": { ... },
"has_price": true
},
{
"item_type_code": "MATERIAL",
"item_id": 124,
"price": null,
"has_price": false
}
]
}
```
### 4.2 변경 이력 조회 `GET /api/v1/pricing/{id}/revisions` ✅
**Response:**
```json
{
"success": true,
"data": {
"data": [
{
"id": 2,
"revision_number": 2,
"changed_at": "2025-12-08T11:00:00.000000Z",
"changed_by": 1,
"change_reason": "마진율 조정",
"before_snapshot": {
"purchase_price": "1000.0000",
"margin_rate": "15.00",
"sales_price": "1265.0000"
},
"after_snapshot": {
"purchase_price": "1000.0000",
"margin_rate": "20.00",
"sales_price": "1386.0000"
},
"changed_by_user": { "id": 1, "name": "홍길동" }
}
]
}
}
```
### 4.3 단가 확정 `POST /api/v1/pricing/{id}/finalize` ✅
**동작:**
- `is_final``true`
- `status``finalized`
- 확정 후 **수정/삭제 불가**
**Response:**
```json
{
"success": true,
"message": "message.pricing.finalized",
"data": {
"id": 5,
"is_final": true,
"finalized_at": "2025-12-08T14:30:00.000000Z",
"finalized_by": 1,
"status": "finalized"
}
}
```
### 4.4 원가 조회 `GET /api/v1/pricing/cost` ✅
**용도**: 수입검사 입고단가 > 표준원가 우선순위로 원가 조회
**Query Parameters:**
| 파라미터 | 타입 | 필수 | 설명 |
|---------|------|------|------|
| `item_type_code` | string | ✅ | `PRODUCT` / `MATERIAL` |
| `item_id` | int | ✅ | 품목 ID |
| `date` | date | ❌ | 조회 기준일 (기본: 오늘) |
**Response:**
```json
{
"success": true,
"data": {
"item_type_code": "MATERIAL",
"item_id": 123,
"date": "2025-12-08",
"cost_source": "receipt",
"purchase_price": 1050.00,
"receipt_id": 45,
"receipt_date": "2025-12-01",
"price_id": null
}
}
```
| cost_source | 설명 |
|-------------|------|
| `receipt` | 수입검사 입고단가 |
| `standard` | 표준원가 (prices 테이블) |
| `not_found` | 단가 미등록 |
---
## 5. 비즈니스 로직
### 5.1 판매단가 자동 계산
```
총원가 = (매입단가 + 가공비) × (1 + LOSS율/100)
판매단가 = 반올림(총원가 × (1 + 마진율/100), 반올림단위, 반올림규칙)
```
### 5.2 상태 흐름
```
draft → active → finalized
inactive
```
### 5.3 주요 검증 규칙
- 동일 품목+고객그룹+시작일 조합 중복 불가
- 확정된 단가는 수정/삭제 불가
- 마진율/LOSS율은 0~100% 범위
- 반올림단위는 1, 10, 100, 1000 중 하나
---
## 6. 프론트엔드 작업 현황
| 작업 | 상태 | 비고 |
|------|------|------|
| `usePricingList` 훅 생성 | ⬜ 미완료 | |
| 타입 정의 | ⬜ 미완료 | |
| 단가 목록 페이지 | ⬜ 미완료 | |
| 단가 등록/수정 페이지 | ⬜ 미완료 | |
| 단가 상세 페이지 (이력 포함) | ⬜ 미완료 | |
| 품목별 단가 현황 컴포넌트 | ⬜ 미완료 | |
---
## 7. 백엔드 참고 파일
### 컨트롤러/서비스
- `api/app/Http/Controllers/Api/V1/PricingController.php`
- `api/app/Services/PricingService.php`
### 모델
- `api/app/Models/Products/Price.php`
- `api/app/Models/Products/PriceRevision.php`
### 요청 클래스
- `api/app/Http/Requests/Pricing/PriceIndexRequest.php`
- `api/app/Http/Requests/Pricing/PriceStoreRequest.php`
- `api/app/Http/Requests/Pricing/PriceUpdateRequest.php`
- `api/app/Http/Requests/Pricing/PriceByItemsRequest.php`
- `api/app/Http/Requests/Pricing/PriceCostRequest.php`
### 마이그레이션
- `api/database/migrations/2025_12_08_154633_create_prices_table.php`
- `api/database/migrations/2025_12_08_154634_create_price_revisions_table.php`
- `api/database/migrations/2025_12_08_154635_migrate_price_histories_to_prices.php`
- `api/database/migrations/2025_12_08_154636_drop_price_histories_table.php`
### 라우트
- `api/routes/api.php` (Line 368-379)
---
## 8. 관련 문서
- [단가 정책](../rules/pricing-policy.md) - 상세 정책 및 계산 공식
---
**최종 업데이트**: 2025-12-08