feat: 단가 관리 API 구현 및 Flow Tester 호환성 개선

- Price, PriceRevision 모델 추가 (PriceHistory 대체)
- PricingService: CRUD, 원가 조회, 확정 기능
- PricingController: statusCode 파라미터로 201 반환 지원
- NotFoundHttpException(404) 적용 (존재하지 않는 리소스)
- FormRequest 분리 (Store, Update, Index, Cost, ByItems)
- Swagger 문서 업데이트
- ApiResponse::handle()에 statusCode 옵션 추가
- prices/price_revisions 마이그레이션 및 데이터 이관
This commit is contained in:
2025-12-08 19:03:50 +09:00
parent 56c707f033
commit 8d3ea4bb39
18 changed files with 1933 additions and 251 deletions

View File

@@ -3,87 +3,137 @@
namespace App\Swagger\v1;
/**
* @OA\Tag(name="Pricing", description="가격 이력 관리")
* @OA\Tag(name="Pricing", description="가 관리")
*
* ========= 스키마 정의 =========
*
* @OA\Schema(
* schema="PriceHistory",
* schema="Price",
* type="object",
* required={"id","item_type_code","item_id","price_type_code","price","started_at"},
* description="단가 마스터",
*
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="tenant_id", type="integer", example=1),
* @OA\Property(property="item_type_code", type="string", enum={"PRODUCT","MATERIAL"}, example="PRODUCT", description="목 유형"),
* @OA\Property(property="item_id", type="integer", example=10, description="제품/자재 ID"),
* @OA\Property(property="price_type_code", type="string", enum={"SALE","PURCHASE"}, example="SALE", description="가격 유형"),
* @OA\Property(property="client_group_id", type="integer", nullable=true, example=1, description="고객 그룹 ID (NULL=기본 가격)"),
* @OA\Property(property="price", type="number", format="decimal", example=50000.00),
* @OA\Property(property="started_at", type="string", format="date", example="2025-01-01"),
* @OA\Property(property="ended_at", type="string", format="date", nullable=true, example="2025-12-31"),
* @OA\Property(property="created_at", type="string", example="2025-10-01 12:00:00"),
* @OA\Property(property="updated_at", type="string", example="2025-10-01 12:00:00")
* @OA\Property(property="item_type_code", type="string", enum={"PRODUCT","MATERIAL"}, example="PRODUCT", description="목 유형"),
* @OA\Property(property="item_id", type="integer", example=10, description="품목 ID"),
* @OA\Property(property="client_group_id", type="integer", nullable=true, example=1, description="고객그룹 ID (NULL=기본가)"),
* @OA\Property(property="purchase_price", type="number", format="decimal", nullable=true, example=10000.00, description="매입단가 (표준원가)"),
* @OA\Property(property="processing_cost", type="number", format="decimal", nullable=true, example=2000.00, description="가공비"),
* @OA\Property(property="loss_rate", type="number", format="decimal", nullable=true, example=5.00, description="LOSS율 (%)"),
* @OA\Property(property="margin_rate", type="number", format="decimal", nullable=true, example=25.00, description="마진율 (%)"),
* @OA\Property(property="sales_price", type="number", format="decimal", nullable=true, example=15800.00, description="판매단가"),
* @OA\Property(property="rounding_rule", type="string", enum={"round","ceil","floor"}, example="round", description="반올림 규칙"),
* @OA\Property(property="rounding_unit", type="integer", example=100, description="반올림 단위 (1,10,100,1000)"),
* @OA\Property(property="supplier", type="string", nullable=true, example="ABC공급", description="공급업체"),
* @OA\Property(property="effective_from", type="string", format="date", example="2025-01-01", description="적용 시작일"),
* @OA\Property(property="effective_to", type="string", format="date", nullable=true, example="2025-12-31", description="적용 종료일"),
* @OA\Property(property="note", type="string", nullable=true, example="2025년 상반기 가격", description="비고"),
* @OA\Property(property="status", type="string", enum={"draft","active","inactive","finalized"}, example="active", description="상태"),
* @OA\Property(property="is_final", type="boolean", example=false, description="최종 확정 여부"),
* @OA\Property(property="finalized_at", type="string", format="datetime", nullable=true, example="2025-01-15 10:30:00", description="확정 일시"),
* @OA\Property(property="finalized_by", type="integer", nullable=true, example=1, description="확정자 ID"),
* @OA\Property(property="created_at", type="string", example="2025-01-01 12:00:00"),
* @OA\Property(property="updated_at", type="string", example="2025-01-01 12:00:00"),
* @OA\Property(
* property="client_group",
* type="object",
* nullable=true,
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="name", type="string", example="VIP 고객")
* )
* )
*
* @OA\Schema(
* schema="PriceHistoryPagination",
* schema="PricePagination",
* type="object",
*
* @OA\Property(property="current_page", type="integer", example=1),
* @OA\Property(
* property="data",
* type="array",
*
* @OA\Items(ref="#/components/schemas/PriceHistory")
* ),
*
* @OA\Property(property="data", type="array", @OA\Items(ref="#/components/schemas/Price")),
* @OA\Property(property="first_page_url", type="string", example="/api/v1/pricing?page=1"),
* @OA\Property(property="from", type="integer", example=1),
* @OA\Property(property="last_page", type="integer", example=3),
* @OA\Property(property="last_page_url", type="string", example="/api/v1/pricing?page=3"),
* @OA\Property(
* property="links",
* type="array",
*
* @OA\Items(type="object",
*
* @OA\Property(property="url", type="string", nullable=true, example=null),
* @OA\Property(property="label", type="string", example="« Previous"),
* @OA\Property(property="active", type="boolean", example=false)
* )
* ),
* @OA\Property(property="next_page_url", type="string", nullable=true, example="/api/v1/pricing?page=2"),
* @OA\Property(property="links", type="array", @OA\Items(type="object",
* @OA\Property(property="url", type="string", nullable=true),
* @OA\Property(property="label", type="string"),
* @OA\Property(property="active", type="boolean")
* )),
* @OA\Property(property="next_page_url", type="string", nullable=true),
* @OA\Property(property="path", type="string", example="/api/v1/pricing"),
* @OA\Property(property="per_page", type="integer", example=15),
* @OA\Property(property="prev_page_url", type="string", nullable=true, example=null),
* @OA\Property(property="to", type="integer", example=15),
* @OA\Property(property="per_page", type="integer", example=20),
* @OA\Property(property="prev_page_url", type="string", nullable=true),
* @OA\Property(property="to", type="integer", example=20),
* @OA\Property(property="total", type="integer", example=50)
* )
*
* @OA\Schema(
* schema="PriceUpsertRequest",
* schema="PriceRevision",
* type="object",
* required={"item_type_code","item_id","price_type_code","price","started_at"},
* description="단가 변경 이력",
*
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="price_id", type="integer", example=1),
* @OA\Property(property="revision_number", type="integer", example=1, description="리비전 번호"),
* @OA\Property(property="changed_at", type="string", format="datetime", example="2025-01-01 12:00:00", description="변경 일시"),
* @OA\Property(property="changed_by", type="integer", example=1, description="변경자 ID"),
* @OA\Property(property="change_reason", type="string", nullable=true, example="2025년 단가 인상", description="변경 사유"),
* @OA\Property(property="before_snapshot", type="object", nullable=true, description="변경 전 데이터"),
* @OA\Property(property="after_snapshot", type="object", description="변경 후 데이터"),
* @OA\Property(
* property="changed_by_user",
* type="object",
* nullable=true,
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="name", type="string", example="홍길동")
* )
* )
*
* @OA\Schema(
* schema="PriceStoreRequest",
* type="object",
* required={"item_type_code","item_id","effective_from"},
*
* @OA\Property(property="item_type_code", type="string", enum={"PRODUCT","MATERIAL"}, example="PRODUCT"),
* @OA\Property(property="item_id", type="integer", example=10),
* @OA\Property(property="price_type_code", type="string", enum={"SALE","PURCHASE"}, example="SALE"),
* @OA\Property(property="client_group_id", type="integer", nullable=true, example=1, description="NULL=기본 가격"),
* @OA\Property(property="price", type="number", format="decimal", example=50000.00),
* @OA\Property(property="started_at", type="string", format="date", example="2025-01-01"),
* @OA\Property(property="ended_at", type="string", format="date", nullable=true, example="2025-12-31")
* @OA\Property(property="client_group_id", type="integer", nullable=true, example=1),
* @OA\Property(property="purchase_price", type="number", nullable=true, example=10000.00),
* @OA\Property(property="processing_cost", type="number", nullable=true, example=2000.00),
* @OA\Property(property="loss_rate", type="number", nullable=true, example=5.00),
* @OA\Property(property="margin_rate", type="number", nullable=true, example=25.00),
* @OA\Property(property="sales_price", type="number", nullable=true, example=15800.00),
* @OA\Property(property="rounding_rule", type="string", enum={"round","ceil","floor"}, example="round"),
* @OA\Property(property="rounding_unit", type="integer", enum={1,10,100,1000}, example=100),
* @OA\Property(property="supplier", type="string", nullable=true, example="ABC공급"),
* @OA\Property(property="effective_from", type="string", format="date", example="2025-01-01"),
* @OA\Property(property="effective_to", type="string", format="date", nullable=true, example="2025-12-31"),
* @OA\Property(property="note", type="string", nullable=true, example="2025년 상반기 가격"),
* @OA\Property(property="status", type="string", enum={"draft","active","inactive"}, example="draft")
* )
*
* @OA\Schema(
* schema="PriceQueryResult",
* schema="PriceUpdateRequest",
* type="object",
*
* @OA\Property(property="price", type="number", format="decimal", nullable=true, example=50000.00),
* @OA\Property(property="price_history_id", type="integer", nullable=true, example=1),
* @OA\Property(property="item_type_code", type="string", enum={"PRODUCT","MATERIAL"}, example="PRODUCT"),
* @OA\Property(property="item_id", type="integer", example=10),
* @OA\Property(property="client_group_id", type="integer", nullable=true, example=1),
* @OA\Property(property="warning", type="string", nullable=true, example="가격을 찾을 수 없습니다")
* @OA\Property(property="purchase_price", type="number", nullable=true, example=10000.00),
* @OA\Property(property="processing_cost", type="number", nullable=true, example=2000.00),
* @OA\Property(property="loss_rate", type="number", nullable=true, example=5.00),
* @OA\Property(property="margin_rate", type="number", nullable=true, example=25.00),
* @OA\Property(property="sales_price", type="number", nullable=true, example=15800.00),
* @OA\Property(property="rounding_rule", type="string", enum={"round","ceil","floor"}, example="round"),
* @OA\Property(property="rounding_unit", type="integer", enum={1,10,100,1000}, example=100),
* @OA\Property(property="supplier", type="string", nullable=true, example="ABC공급"),
* @OA\Property(property="effective_from", type="string", format="date", example="2025-01-01"),
* @OA\Property(property="effective_to", type="string", format="date", nullable=true, example="2025-12-31"),
* @OA\Property(property="note", type="string", nullable=true, example="2025년 상반기 가격"),
* @OA\Property(property="status", type="string", enum={"draft","active","inactive"}, example="active"),
* @OA\Property(property="change_reason", type="string", nullable=true, example="단가 인상", description="변경 사유 (리비전 기록용)")
* )
*
* @OA\Schema(
* schema="BulkPriceQueryRequest",
* schema="PriceByItemsRequest",
* type="object",
* required={"items"},
*
@@ -93,32 +143,39 @@
*
* @OA\Items(type="object",
*
* @OA\Property(property="item_type", type="string", enum={"PRODUCT","MATERIAL"}, example="PRODUCT"),
* @OA\Property(property="item_type_code", type="string", enum={"PRODUCT","MATERIAL"}, example="PRODUCT"),
* @OA\Property(property="item_id", type="integer", example=10)
* )
* ),
* @OA\Property(property="client_id", type="integer", nullable=true, example=5),
* @OA\Property(property="date", type="string", format="date", nullable=true, example="2025-10-13")
* @OA\Property(property="client_group_id", type="integer", nullable=true, example=1),
* @OA\Property(property="date", type="string", format="date", nullable=true, example="2025-01-15")
* )
*
* @OA\Schema(
* schema="BulkPriceQueryResult",
* schema="PriceByItemsResult",
* type="array",
*
* @OA\Items(type="object",
*
* @OA\Property(property="item_type_code", type="string", example="PRODUCT"),
* @OA\Property(property="item_id", type="integer", example=10),
* @OA\Property(property="price", ref="#/components/schemas/Price", nullable=true),
* @OA\Property(property="has_price", type="boolean", example=true)
* )
* )
*
* @OA\Schema(
* schema="PriceCostResult",
* type="object",
*
* @OA\Property(
* property="prices",
* type="array",
*
* @OA\Items(type="object",
*
* @OA\Property(property="item_type", type="string", example="PRODUCT"),
* @OA\Property(property="item_id", type="integer", example=10),
* @OA\Property(property="price", type="number", nullable=true, example=50000.00),
* @OA\Property(property="price_history_id", type="integer", nullable=true, example=1),
* @OA\Property(property="client_group_id", type="integer", nullable=true, example=1)
* )
* ),
* @OA\Property(property="warnings", type="array", @OA\Items(type="string"))
* @OA\Property(property="item_type_code", type="string", example="MATERIAL"),
* @OA\Property(property="item_id", type="integer", example=123),
* @OA\Property(property="date", type="string", format="date", example="2025-01-15"),
* @OA\Property(property="cost_source", type="string", enum={"receipt","standard","not_found"}, example="receipt", description="원가 출처"),
* @OA\Property(property="purchase_price", type="number", nullable=true, example=10500.00),
* @OA\Property(property="receipt_id", type="integer", nullable=true, example=456, description="수입검사 ID (cost_source=receipt일 때)"),
* @OA\Property(property="receipt_date", type="string", format="date", nullable=true, example="2025-01-10"),
* @OA\Property(property="price_id", type="integer", nullable=true, example=null, description="단가 ID (cost_source=standard일 때)")
* )
*/
class PricingApi
@@ -127,22 +184,24 @@ class PricingApi
* @OA\Get(
* path="/api/v1/pricing",
* tags={"Pricing"},
* summary="가격 이력 목록",
* summary="단가 목록 조회",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="q", in="query", description="검색어 (supplier, note)", @OA\Schema(type="string")),
* @OA\Parameter(name="item_type_code", in="query", @OA\Schema(type="string", enum={"PRODUCT","MATERIAL"})),
* @OA\Parameter(name="item_id", in="query", @OA\Schema(type="integer")),
* @OA\Parameter(name="price_type_code", in="query", @OA\Schema(type="string", enum={"SALE","PURCHASE"})),
* @OA\Parameter(name="client_group_id", in="query", @OA\Schema(type="integer")),
* @OA\Parameter(name="date", in="query", description="특정 날짜 기준 유효한 가격", @OA\Schema(type="string", format="date")),
* @OA\Parameter(name="size", in="query", @OA\Schema(type="integer", example=15)),
* @OA\Parameter(name="client_group_id", in="query", description="고객그룹 ID (빈값/null=기본가만)", @OA\Schema(type="string")),
* @OA\Parameter(name="status", in="query", @OA\Schema(type="string", enum={"draft","active","inactive","finalized"})),
* @OA\Parameter(name="valid_at", in="query", description="특정 날짜 유효한 단가만", @OA\Schema(type="string", format="date")),
* @OA\Parameter(name="size", in="query", @OA\Schema(type="integer", example=20)),
* @OA\Parameter(name="page", in="query", @OA\Schema(type="integer", example=1)),
*
* @OA\Response(response=200, description="조회 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/PriceHistoryPagination"))
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/PricePagination"))
* })
* ),
*
@@ -153,86 +212,188 @@ public function index() {}
/**
* @OA\Get(
* path="/api/v1/pricing/show",
* path="/api/v1/pricing/{id}",
* tags={"Pricing"},
* summary="단일 항목 가격 조회",
* description="특정 제품/자재의 현재 유효한 가격 조회",
* summary="단가 상세 조회",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="item_type", in="query", required=true, @OA\Schema(type="string", enum={"PRODUCT","MATERIAL"})),
* @OA\Parameter(name="item_id", in="query", required=true, @OA\Schema(type="integer")),
* @OA\Parameter(name="client_id", in="query", @OA\Schema(type="integer"), description="고객 ID (고객 그룹별 가격 적용)"),
* @OA\Parameter(name="date", in="query", @OA\Schema(type="string", format="date"), description="기준일 (미지정시 오늘)"),
* @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")),
*
* @OA\Response(response=200, description="조회 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/PriceQueryResult"))
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/Price"))
* })
* )
* ),
*
* @OA\Response(response=404, description="미존재", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function show() {}
/**
* @OA\Post(
* path="/api/v1/pricing/bulk",
* path="/api/v1/pricing",
* tags={"Pricing"},
* summary="여러 항목 일괄 가격 조회",
* description="여러 제품/자재의 가격을 한 번에 조회",
* summary="단가 등록",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\RequestBody(required=true, @OA\JsonContent(ref="#/components/schemas/BulkPriceQueryRequest")),
* @OA\RequestBody(required=true, @OA\JsonContent(ref="#/components/schemas/PriceStoreRequest")),
*
* @OA\Response(response=200, description="조회 성공",
* @OA\Response(response=200, description="등록 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/BulkPriceQueryResult"))
* })
* )
* )
*/
public function bulk() {}
/**
* @OA\Post(
* path="/api/v1/pricing/upsert",
* tags={"Pricing"},
* summary="가격 등록/수정",
* description="가격 이력 등록 (동일 조건 존재 시 업데이트)",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\RequestBody(required=true, @OA\JsonContent(ref="#/components/schemas/PriceUpsertRequest")),
*
* @OA\Response(response=200, description="저장 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/PriceHistory"))
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/Price"))
* })
* ),
*
* @OA\Response(response=400, description="검증 실패", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function upsert() {}
public function store() {}
/**
* @OA\Put(
* path="/api/v1/pricing/{id}",
* tags={"Pricing"},
* summary="단가 수정",
* description="확정(finalized) 상태의 단가는 수정 불가",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")),
*
* @OA\RequestBody(required=true, @OA\JsonContent(ref="#/components/schemas/PriceUpdateRequest")),
*
* @OA\Response(response=200, description="수정 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/Price"))
* })
* ),
*
* @OA\Response(response=400, description="수정 불가 (확정된 단가)", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function update() {}
/**
* @OA\Delete(
* path="/api/v1/pricing/{id}",
* tags={"Pricing"},
* summary="가격 이력 삭제(soft)",
* summary="가 삭제 (soft)",
* description="확정(finalized) 상태의 단가는 삭제 불가",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")),
*
* @OA\Response(response=200, description="삭제 성공", @OA\JsonContent(ref="#/components/schemas/ApiResponse"))
* @OA\Response(response=200, description="삭제 성공", @OA\JsonContent(ref="#/components/schemas/ApiResponse")),
* @OA\Response(response=400, description="삭제 불가", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function destroy() {}
/**
* @OA\Post(
* path="/api/v1/pricing/{id}/finalize",
* tags={"Pricing"},
* summary="단가 확정",
* description="단가를 확정 상태로 변경 (확정 후 수정/삭제 불가)",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")),
*
* @OA\Response(response=200, description="확정 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/Price"))
* })
* ),
*
* @OA\Response(response=400, description="확정 불가", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function finalize() {}
/**
* @OA\Post(
* path="/api/v1/pricing/by-items",
* tags={"Pricing"},
* summary="품목별 단가 현황 조회",
* description="여러 품목의 현재 유효한 단가를 한번에 조회",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\RequestBody(required=true, @OA\JsonContent(ref="#/components/schemas/PriceByItemsRequest")),
*
* @OA\Response(response=200, description="조회 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/PriceByItemsResult"))
* })
* )
* )
*/
public function byItems() {}
/**
* @OA\Get(
* path="/api/v1/pricing/{id}/revisions",
* tags={"Pricing"},
* summary="변경 이력 조회",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")),
* @OA\Parameter(name="size", in="query", @OA\Schema(type="integer", example=20)),
*
* @OA\Response(response=200, description="조회 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", type="object",
*
* @OA\Property(property="current_page", type="integer"),
* @OA\Property(property="data", type="array", @OA\Items(ref="#/components/schemas/PriceRevision")),
* @OA\Property(property="total", type="integer")
* ))
* })
* ),
*
* @OA\Response(response=404, description="단가 미존재", @OA\JsonContent(ref="#/components/schemas/ErrorResponse"))
* )
*/
public function revisions() {}
/**
* @OA\Get(
* path="/api/v1/pricing/cost",
* tags={"Pricing"},
* summary="원가 조회",
* description="품목의 원가를 조회. 자재는 수입검사 입고단가 우선, 없으면 표준원가 사용",
* security={{"ApiKeyAuth":{}},{"BearerAuth":{}}},
*
* @OA\Parameter(name="item_type_code", in="query", required=true, @OA\Schema(type="string", enum={"PRODUCT","MATERIAL"})),
* @OA\Parameter(name="item_id", in="query", required=true, @OA\Schema(type="integer")),
* @OA\Parameter(name="date", in="query", description="기준일 (미지정시 오늘)", @OA\Schema(type="string", format="date")),
*
* @OA\Response(response=200, description="조회 성공",
*
* @OA\JsonContent(allOf={
*
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(@OA\Property(property="data", ref="#/components/schemas/PriceCostResult"))
* })
* )
* )
*/
public function cost() {}
}