use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use stateset_primitives::ProductId;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum TimePeriod {
Today,
Yesterday,
Last7Days,
Last30Days,
ThisMonth,
LastMonth,
ThisQuarter,
LastQuarter,
ThisYear,
LastYear,
AllTime,
Custom,
}
impl Default for TimePeriod {
fn default() -> Self {
Self::Last30Days
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct DateRange {
pub start: Option<DateTime<Utc>>,
pub end: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SalesSummary {
pub total_revenue: Decimal,
pub order_count: u64,
pub average_order_value: Decimal,
pub items_sold: u64,
pub unique_customers: u64,
pub revenue_change_percent: Option<Decimal>,
pub order_count_change_percent: Option<Decimal>,
pub period_start: Option<DateTime<Utc>>,
pub period_end: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RevenueByPeriod {
pub period: String,
pub revenue: Decimal,
pub order_count: u64,
pub period_start: DateTime<Utc>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum TimeGranularity {
Hour,
Day,
Week,
Month,
Quarter,
Year,
}
impl Default for TimeGranularity {
fn default() -> Self {
Self::Day
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TopProduct {
pub product_id: Option<ProductId>,
pub sku: String,
pub name: String,
pub units_sold: u64,
pub revenue: Decimal,
pub order_count: u64,
pub average_price: Decimal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProductPerformance {
pub product_id: ProductId,
pub sku: String,
pub name: String,
pub units_sold: u64,
pub revenue: Decimal,
pub previous_units_sold: u64,
pub previous_revenue: Decimal,
pub units_growth_percent: Decimal,
pub revenue_growth_percent: Decimal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomerMetrics {
pub total_customers: u64,
pub new_customers: u64,
pub returning_customers: u64,
pub average_lifetime_value: Decimal,
pub average_orders_per_customer: Decimal,
pub retention_rate_percent: Option<Decimal>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TopCustomer {
pub customer_id: Uuid,
pub email: String,
pub name: String,
pub total_spent: Decimal,
pub order_count: u64,
pub average_order_value: Decimal,
pub first_order_date: Option<DateTime<Utc>>,
pub last_order_date: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct InventoryHealth {
pub total_skus: u64,
pub in_stock_skus: u64,
pub low_stock_skus: u64,
pub out_of_stock_skus: u64,
pub total_value: Decimal,
pub turnover_ratio: Option<Decimal>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LowStockItem {
pub sku: String,
pub name: String,
pub on_hand: Decimal,
pub allocated: Decimal,
pub available: Decimal,
pub reorder_point: Option<Decimal>,
pub average_daily_sales: Option<Decimal>,
pub days_of_stock: Option<Decimal>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InventoryMovement {
pub sku: String,
pub name: String,
pub units_sold: u64,
pub units_received: u64,
pub units_returned: u64,
pub units_adjusted: i64,
pub net_change: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DemandForecast {
pub sku: String,
pub name: String,
pub average_daily_demand: Decimal,
pub forecasted_demand: Decimal,
pub confidence: Decimal,
pub current_stock: Decimal,
pub days_until_stockout: Option<i32>,
pub recommended_reorder_qty: Option<Decimal>,
pub recommended_reorder_date: Option<DateTime<Utc>>,
pub trend: Trend,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum Trend {
Rising,
Stable,
Falling,
}
impl Default for Trend {
fn default() -> Self {
Self::Stable
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RevenueForecast {
pub period: String,
pub forecasted_revenue: Decimal,
pub lower_bound: Decimal,
pub upper_bound: Decimal,
pub confidence_level: Decimal,
pub based_on_periods: u32,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct OrderStatusBreakdown {
pub pending: u64,
pub confirmed: u64,
pub processing: u64,
pub shipped: u64,
pub delivered: u64,
pub cancelled: u64,
pub refunded: u64,
pub total: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FulfillmentMetrics {
pub avg_time_to_ship_hours: Option<Decimal>,
pub avg_time_to_deliver_hours: Option<Decimal>,
pub on_time_shipping_percent: Option<Decimal>,
pub on_time_delivery_percent: Option<Decimal>,
pub shipped_today: u64,
pub awaiting_shipment: u64,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ReturnMetrics {
pub total_returns: u64,
pub return_rate_percent: Decimal,
pub total_refunded: Decimal,
pub by_reason: Vec<ReturnReasonCount>,
pub top_returned_products: Vec<TopReturnedProduct>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReturnReasonCount {
pub reason: String,
pub count: u64,
pub percentage: Decimal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TopReturnedProduct {
pub sku: String,
pub name: String,
pub units_returned: u64,
pub units_sold: u64,
pub return_rate_percent: Decimal,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AnalyticsQuery {
pub period: Option<TimePeriod>,
pub date_range: Option<DateRange>,
pub granularity: Option<TimeGranularity>,
pub limit: Option<u32>,
pub compare_previous: Option<bool>,
}
impl AnalyticsQuery {
pub fn new() -> Self {
Self::default()
}
pub const fn period(mut self, period: TimePeriod) -> Self {
self.period = Some(period);
self
}
pub const fn date_range(mut self, start: DateTime<Utc>, end: DateTime<Utc>) -> Self {
self.period = Some(TimePeriod::Custom);
self.date_range = Some(DateRange { start: Some(start), end: Some(end) });
self
}
pub const fn granularity(mut self, granularity: TimeGranularity) -> Self {
self.granularity = Some(granularity);
self
}
pub const fn limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}
pub const fn compare_previous(mut self, compare: bool) -> Self {
self.compare_previous = Some(compare);
self
}
}