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