1use chrono::{DateTime, Utc};
6use rust_decimal::Decimal;
7use serde::{Deserialize, Serialize};
8use stateset_primitives::ProductId;
9use uuid::Uuid;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "snake_case")]
18#[non_exhaustive]
19pub enum TimePeriod {
20 Today,
21 Yesterday,
22 Last7Days,
23 Last30Days,
24 ThisMonth,
25 LastMonth,
26 ThisQuarter,
27 LastQuarter,
28 ThisYear,
29 LastYear,
30 AllTime,
31 Custom,
32}
33
34impl Default for TimePeriod {
35 fn default() -> Self {
36 Self::Last30Days
37 }
38}
39
40#[derive(Debug, Clone, Default, Serialize, Deserialize)]
42pub struct DateRange {
43 pub start: Option<DateTime<Utc>>,
44 pub end: Option<DateTime<Utc>>,
45}
46
47#[derive(Debug, Clone, Default, Serialize, Deserialize)]
53pub struct SalesSummary {
54 pub total_revenue: Decimal,
56 pub order_count: u64,
58 pub average_order_value: Decimal,
60 pub items_sold: u64,
62 pub unique_customers: u64,
64 pub revenue_change_percent: Option<Decimal>,
66 pub order_count_change_percent: Option<Decimal>,
68 pub period_start: Option<DateTime<Utc>>,
70 pub period_end: Option<DateTime<Utc>>,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct RevenueByPeriod {
77 pub period: String,
79 pub revenue: Decimal,
81 pub order_count: u64,
83 pub period_start: DateTime<Utc>,
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
89#[serde(rename_all = "snake_case")]
90#[non_exhaustive]
91pub enum TimeGranularity {
92 Hour,
93 Day,
94 Week,
95 Month,
96 Quarter,
97 Year,
98}
99
100impl Default for TimeGranularity {
101 fn default() -> Self {
102 Self::Day
103 }
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct TopProduct {
113 pub product_id: Option<ProductId>,
115 pub sku: String,
117 pub name: String,
119 pub units_sold: u64,
121 pub revenue: Decimal,
123 pub order_count: u64,
125 pub average_price: Decimal,
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct ProductPerformance {
132 pub product_id: ProductId,
133 pub sku: String,
134 pub name: String,
135 pub units_sold: u64,
137 pub revenue: Decimal,
139 pub previous_units_sold: u64,
141 pub previous_revenue: Decimal,
143 pub units_growth_percent: Decimal,
145 pub revenue_growth_percent: Decimal,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct CustomerMetrics {
156 pub total_customers: u64,
158 pub new_customers: u64,
160 pub returning_customers: u64,
162 pub average_lifetime_value: Decimal,
164 pub average_orders_per_customer: Decimal,
166 pub retention_rate_percent: Option<Decimal>,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct TopCustomer {
173 pub customer_id: Uuid,
174 pub email: String,
175 pub name: String,
176 pub total_spent: Decimal,
178 pub order_count: u64,
180 pub average_order_value: Decimal,
182 pub first_order_date: Option<DateTime<Utc>>,
184 pub last_order_date: Option<DateTime<Utc>>,
186}
187
188#[derive(Debug, Clone, Default, Serialize, Deserialize)]
194pub struct InventoryHealth {
195 pub total_skus: u64,
197 pub in_stock_skus: u64,
199 pub low_stock_skus: u64,
201 pub out_of_stock_skus: u64,
203 pub total_value: Decimal,
205 pub turnover_ratio: Option<Decimal>,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct LowStockItem {
212 pub sku: String,
213 pub name: String,
214 pub on_hand: Decimal,
216 pub allocated: Decimal,
218 pub available: Decimal,
220 pub reorder_point: Option<Decimal>,
222 pub average_daily_sales: Option<Decimal>,
224 pub days_of_stock: Option<Decimal>,
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct InventoryMovement {
231 pub sku: String,
232 pub name: String,
233 pub units_sold: u64,
235 pub units_received: u64,
237 pub units_returned: u64,
239 pub units_adjusted: i64,
241 pub net_change: i64,
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct DemandForecast {
252 pub sku: String,
253 pub name: String,
254 pub average_daily_demand: Decimal,
256 pub forecasted_demand: Decimal,
258 pub confidence: Decimal,
260 pub current_stock: Decimal,
262 pub days_until_stockout: Option<i32>,
264 pub recommended_reorder_qty: Option<Decimal>,
266 pub recommended_reorder_date: Option<DateTime<Utc>>,
268 pub trend: Trend,
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
274#[serde(rename_all = "snake_case")]
275#[non_exhaustive]
276pub enum Trend {
277 Rising,
278 Stable,
279 Falling,
280}
281
282impl Default for Trend {
283 fn default() -> Self {
284 Self::Stable
285 }
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct RevenueForecast {
291 pub period: String,
293 pub forecasted_revenue: Decimal,
295 pub lower_bound: Decimal,
297 pub upper_bound: Decimal,
299 pub confidence_level: Decimal,
301 pub based_on_periods: u32,
303}
304
305#[derive(Debug, Clone, Default, Serialize, Deserialize)]
311pub struct OrderStatusBreakdown {
312 pub pending: u64,
313 pub confirmed: u64,
314 pub processing: u64,
315 pub shipped: u64,
316 pub delivered: u64,
317 pub cancelled: u64,
318 pub refunded: u64,
319 pub total: u64,
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize)]
324pub struct FulfillmentMetrics {
325 pub avg_time_to_ship_hours: Option<Decimal>,
327 pub avg_time_to_deliver_hours: Option<Decimal>,
329 pub on_time_shipping_percent: Option<Decimal>,
331 pub on_time_delivery_percent: Option<Decimal>,
333 pub shipped_today: u64,
335 pub awaiting_shipment: u64,
337}
338
339#[derive(Debug, Clone, Default, Serialize, Deserialize)]
345pub struct ReturnMetrics {
346 pub total_returns: u64,
348 pub return_rate_percent: Decimal,
350 pub total_refunded: Decimal,
352 pub by_reason: Vec<ReturnReasonCount>,
354 pub top_returned_products: Vec<TopReturnedProduct>,
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize)]
360pub struct ReturnReasonCount {
361 pub reason: String,
362 pub count: u64,
363 pub percentage: Decimal,
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct TopReturnedProduct {
369 pub sku: String,
370 pub name: String,
371 pub units_returned: u64,
372 pub units_sold: u64,
373 pub return_rate_percent: Decimal,
374}
375
376#[derive(Debug, Clone, Default, Serialize, Deserialize)]
382pub struct AnalyticsQuery {
383 pub period: Option<TimePeriod>,
385 pub date_range: Option<DateRange>,
387 pub granularity: Option<TimeGranularity>,
389 pub limit: Option<u32>,
391 pub compare_previous: Option<bool>,
393}
394
395impl AnalyticsQuery {
396 #[must_use]
398 pub fn new() -> Self {
399 Self::default()
400 }
401
402 #[must_use]
404 pub const fn period(mut self, period: TimePeriod) -> Self {
405 self.period = Some(period);
406 self
407 }
408
409 #[must_use]
411 pub const fn date_range(mut self, start: DateTime<Utc>, end: DateTime<Utc>) -> Self {
412 self.period = Some(TimePeriod::Custom);
413 self.date_range = Some(DateRange { start: Some(start), end: Some(end) });
414 self
415 }
416
417 #[must_use]
419 pub const fn granularity(mut self, granularity: TimeGranularity) -> Self {
420 self.granularity = Some(granularity);
421 self
422 }
423
424 #[must_use]
426 pub const fn limit(mut self, limit: u32) -> Self {
427 self.limit = Some(limit);
428 self
429 }
430
431 #[must_use]
433 pub const fn compare_previous(mut self, compare: bool) -> Self {
434 self.compare_previous = Some(compare);
435 self
436 }
437}