Files
sam-docs/front/[API-2025-12-08] pricing-api-enhancement-request.md
hskwon 5d1190a0d3 docs: plans 폴더 추가 및 HR API 규칙 문서 정리
- plans/ 폴더 신규 생성 (개발 계획 임시 문서용)
- hr-api-react-sync-plan.md를 specs → plans로 이동
- INDEX.md 업데이트 (폴더 구조, 워크플로우)
- rules/ HR API 규칙 문서 추가 (employee, attendance, department-tree)
- pricing API 요청 문서 업데이트
2025-12-09 14:44:39 +09:00

379 lines
9.9 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
> **최종 업데이트**: 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