pricing.rs (3221B)
1 //! Pricing API handlers 2 //! 3 //! This module contains the HTTP request handlers for pricing-related endpoints. 4 //! Database operations are delegated to the repository layer in `db::pricing`. 5 6 use axum::{ 7 extract::{Path, Query, State}, 8 http::StatusCode, 9 response::{IntoResponse, Json}, 10 }; 11 use sqlx::PgPool; 12 use uuid::Uuid; 13 14 use crate::db; 15 use crate::models::{PricingQueryParams, PricingResponse, UpdatePricingRequest}; 16 use crate::utils::{calculate_pagination, calculate_total_pages, internal_error, not_found_error}; 17 18 /// List pricing with pagination and filtering 19 /// 20 /// # Endpoint 21 /// `GET /pricing` 22 /// 23 /// # Query Parameters 24 /// - `page` (default: 1) - Page number (1-indexed) 25 /// - `page_size` (default: 20, max: 100) - Items per page 26 /// - `product_uuid` - Filter by specific product 27 /// - `min_price` / `max_price` - Filter by price range 28 /// - `has_discount` - Filter by discount presence 29 pub async fn list_pricing( 30 State(pool): State<PgPool>, 31 Query(params): Query<PricingQueryParams>, 32 ) -> impl IntoResponse { 33 // Apply pagination defaults and constraints 34 let (page, page_size, offset) = calculate_pagination(params.page, params.page_size); 35 36 // Delegate to repository layer for database operations 37 let (pricing, total_count) = 38 match db::list_pricing(&pool, ¶ms, page, page_size, offset).await { 39 Ok(result) => result, 40 Err(e) => { 41 return internal_error("Failed to fetch pricing", e.to_string()); 42 } 43 }; 44 45 let total_pages = calculate_total_pages(total_count, page_size); 46 47 let response = PricingResponse { 48 pricing, 49 total_count, 50 page, 51 page_size, 52 total_pages, 53 }; 54 55 (StatusCode::OK, Json(response)).into_response() 56 } 57 58 /// Get pricing for a specific product by UUID 59 /// 60 /// # Endpoint 61 /// `GET /pricing/{product_uuid}` 62 pub async fn get_pricing_by_product( 63 State(pool): State<PgPool>, 64 Path(product_uuid): Path<Uuid>, 65 ) -> impl IntoResponse { 66 // Delegate to repository layer for database lookup 67 match db::get_pricing_by_product(&pool, product_uuid).await { 68 Ok(Some(pricing)) => (StatusCode::OK, Json(pricing)).into_response(), 69 Ok(None) => not_found_error( 70 "Pricing not found for product", 71 serde_json::json!({"product_uuid": product_uuid.to_string()}), 72 ), 73 Err(e) => internal_error("Failed to fetch pricing", e.to_string()), 74 } 75 } 76 77 /// Create or update pricing for a product 78 /// 79 /// # Endpoint 80 /// `PUT /pricing/{product_uuid}` 81 pub async fn upsert_pricing( 82 State(pool): State<PgPool>, 83 Path(product_uuid): Path<Uuid>, 84 Json(request): Json<UpdatePricingRequest>, 85 ) -> impl IntoResponse { 86 // Delegate to repository layer for pricing upsert 87 match db::upsert_pricing( 88 &pool, 89 product_uuid, 90 request.final_price, 91 request.initial_price, 92 request.currency.unwrap_or_else(|| "USD".to_string()), 93 request.price_valid_from, 94 request.price_valid_until, 95 ) 96 .await 97 { 98 Ok(pricing) => (StatusCode::OK, Json(pricing)).into_response(), 99 Err(e) => internal_error("Failed to update pricing", e.to_string()), 100 } 101 }