stateset_embedded/analytics.rs
1//! Analytics and forecasting operations
2
3use rust_decimal::Decimal;
4use stateset_core::{
5 AnalyticsQuery, CustomerMetrics, DemandForecast, FulfillmentMetrics, InventoryHealth,
6 InventoryMovement, LowStockItem, OrderStatusBreakdown, ProductPerformance, Result,
7 ReturnMetrics, RevenueByPeriod, RevenueForecast, SalesSummary, TimeGranularity, TopCustomer,
8 TopProduct,
9};
10use stateset_db::Database;
11use std::sync::Arc;
12
13/// Analytics operations interface.
14///
15/// Provides sales analytics, inventory forecasting, and business intelligence.
16///
17/// # Example
18///
19/// ```rust,no_run
20/// use stateset_embedded::{Commerce, AnalyticsQuery, TimePeriod};
21///
22/// let commerce = Commerce::new("./store.db")?;
23///
24/// // Get sales summary for last 30 days
25/// let summary = commerce.analytics().sales_summary(
26/// AnalyticsQuery::new().period(TimePeriod::Last30Days)
27/// )?;
28/// println!("Revenue: ${}", summary.total_revenue);
29///
30/// // Get top selling products
31/// let top_products = commerce.analytics().top_products(
32/// AnalyticsQuery::new().period(TimePeriod::ThisMonth).limit(10)
33/// )?;
34///
35/// // Get demand forecast
36/// let forecasts = commerce.analytics().demand_forecast(None, 30)?;
37/// # Ok::<(), stateset_embedded::CommerceError>(())
38/// ```
39pub struct Analytics {
40 db: Arc<dyn Database>,
41}
42
43impl std::fmt::Debug for Analytics {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 f.debug_struct("Analytics").finish_non_exhaustive()
46 }
47}
48
49impl Analytics {
50 pub(crate) fn new(db: Arc<dyn Database>) -> Self {
51 Self { db }
52 }
53
54 // ========================================================================
55 // Sales Analytics
56 // ========================================================================
57
58 /// Get sales summary for a time period.
59 ///
60 /// Returns total revenue, order count, average order value, and more.
61 ///
62 /// # Example
63 ///
64 /// ```rust,no_run
65 /// # use stateset_embedded::*;
66 /// # let commerce = Commerce::new(":memory:")?;
67 /// let summary = commerce.analytics().sales_summary(
68 /// AnalyticsQuery::new().period(TimePeriod::Last30Days)
69 /// )?;
70 /// println!("Revenue: ${}", summary.total_revenue);
71 /// println!("Orders: {}", summary.order_count);
72 /// println!("AOV: ${}", summary.average_order_value);
73 /// # Ok::<(), CommerceError>(())
74 /// ```
75 pub fn sales_summary(&self, query: AnalyticsQuery) -> Result<SalesSummary> {
76 self.db.analytics().get_sales_summary(query)
77 }
78
79 /// Get revenue broken down by time periods.
80 ///
81 /// # Example
82 ///
83 /// ```rust,no_run
84 /// # use stateset_embedded::*;
85 /// # let commerce = Commerce::new(":memory:")?;
86 /// let revenue = commerce.analytics().revenue_by_period(
87 /// AnalyticsQuery::new()
88 /// .period(TimePeriod::Last30Days)
89 /// .granularity(TimeGranularity::Day)
90 /// )?;
91 /// for day in revenue {
92 /// println!("{}: ${}", day.period, day.revenue);
93 /// }
94 /// # Ok::<(), CommerceError>(())
95 /// ```
96 pub fn revenue_by_period(&self, query: AnalyticsQuery) -> Result<Vec<RevenueByPeriod>> {
97 self.db.analytics().get_revenue_by_period(query)
98 }
99
100 // ========================================================================
101 // Product Analytics
102 // ========================================================================
103
104 /// Get top selling products.
105 ///
106 /// # Example
107 ///
108 /// ```rust,no_run
109 /// # use stateset_embedded::*;
110 /// # let commerce = Commerce::new(":memory:")?;
111 /// let top = commerce.analytics().top_products(
112 /// AnalyticsQuery::new()
113 /// .period(TimePeriod::ThisMonth)
114 /// .limit(10)
115 /// )?;
116 /// for product in top {
117 /// println!("{}: {} units, ${}", product.name, product.units_sold, product.revenue);
118 /// }
119 /// # Ok::<(), CommerceError>(())
120 /// ```
121 pub fn top_products(&self, query: AnalyticsQuery) -> Result<Vec<TopProduct>> {
122 self.db.analytics().get_top_products(query)
123 }
124
125 /// Get product performance with period comparison.
126 pub fn product_performance(&self, query: AnalyticsQuery) -> Result<Vec<ProductPerformance>> {
127 self.db.analytics().get_product_performance(query)
128 }
129
130 // ========================================================================
131 // Customer Analytics
132 // ========================================================================
133
134 /// Get customer metrics.
135 ///
136 /// # Example
137 ///
138 /// ```rust,no_run
139 /// # use stateset_embedded::*;
140 /// # let commerce = Commerce::new(":memory:")?;
141 /// let metrics = commerce.analytics().customer_metrics(
142 /// AnalyticsQuery::new().period(TimePeriod::ThisMonth)
143 /// )?;
144 /// println!("Total customers: {}", metrics.total_customers);
145 /// println!("New this month: {}", metrics.new_customers);
146 /// println!("Avg LTV: ${}", metrics.average_lifetime_value);
147 /// # Ok::<(), CommerceError>(())
148 /// ```
149 pub fn customer_metrics(&self, query: AnalyticsQuery) -> Result<CustomerMetrics> {
150 self.db.analytics().get_customer_metrics(query)
151 }
152
153 /// Get top customers by spend.
154 ///
155 /// # Example
156 ///
157 /// ```rust,no_run
158 /// # use stateset_embedded::*;
159 /// # let commerce = Commerce::new(":memory:")?;
160 /// let top = commerce.analytics().top_customers(
161 /// AnalyticsQuery::new().period(TimePeriod::AllTime).limit(10)
162 /// )?;
163 /// for customer in top {
164 /// println!("{}: {} orders, ${}", customer.name, customer.order_count, customer.total_spent);
165 /// }
166 /// # Ok::<(), CommerceError>(())
167 /// ```
168 pub fn top_customers(&self, query: AnalyticsQuery) -> Result<Vec<TopCustomer>> {
169 self.db.analytics().get_top_customers(query)
170 }
171
172 // ========================================================================
173 // Inventory Analytics
174 // ========================================================================
175
176 /// Get inventory health summary.
177 ///
178 /// # Example
179 ///
180 /// ```rust,no_run
181 /// # use stateset_embedded::*;
182 /// # let commerce = Commerce::new(":memory:")?;
183 /// let health = commerce.analytics().inventory_health()?;
184 /// println!("Total SKUs: {}", health.total_skus);
185 /// println!("Low stock: {}", health.low_stock_skus);
186 /// println!("Out of stock: {}", health.out_of_stock_skus);
187 /// # Ok::<(), CommerceError>(())
188 /// ```
189 pub fn inventory_health(&self) -> Result<InventoryHealth> {
190 self.db.analytics().get_inventory_health()
191 }
192
193 /// Get low stock items.
194 ///
195 /// # Example
196 ///
197 /// ```rust,no_run
198 /// # use stateset_embedded::*;
199 /// # use rust_decimal_macros::dec;
200 /// # let commerce = Commerce::new(":memory:")?;
201 /// let low_stock = commerce.analytics().low_stock_items(Some(dec!(20)))?;
202 /// for item in low_stock {
203 /// println!("{}: {} available", item.sku, item.available);
204 /// }
205 /// # Ok::<(), CommerceError>(())
206 /// ```
207 pub fn low_stock_items(&self, threshold: Option<Decimal>) -> Result<Vec<LowStockItem>> {
208 self.db.analytics().get_low_stock_items(threshold)
209 }
210
211 /// Get inventory movement summary.
212 pub fn inventory_movement(&self, query: AnalyticsQuery) -> Result<Vec<InventoryMovement>> {
213 self.db.analytics().get_inventory_movement(query)
214 }
215
216 // ========================================================================
217 // Order Analytics
218 // ========================================================================
219
220 /// Get order status breakdown.
221 ///
222 /// # Example
223 ///
224 /// ```rust,no_run
225 /// # use stateset_embedded::*;
226 /// # let commerce = Commerce::new(":memory:")?;
227 /// let breakdown = commerce.analytics().order_status_breakdown(
228 /// AnalyticsQuery::new().period(TimePeriod::Last30Days)
229 /// )?;
230 /// println!("Pending: {}", breakdown.pending);
231 /// println!("Shipped: {}", breakdown.shipped);
232 /// println!("Delivered: {}", breakdown.delivered);
233 /// # Ok::<(), CommerceError>(())
234 /// ```
235 pub fn order_status_breakdown(&self, query: AnalyticsQuery) -> Result<OrderStatusBreakdown> {
236 self.db.analytics().get_order_status_breakdown(query)
237 }
238
239 /// Get fulfillment metrics.
240 pub fn fulfillment_metrics(&self, query: AnalyticsQuery) -> Result<FulfillmentMetrics> {
241 self.db.analytics().get_fulfillment_metrics(query)
242 }
243
244 // ========================================================================
245 // Return Analytics
246 // ========================================================================
247
248 /// Get return metrics.
249 ///
250 /// # Example
251 ///
252 /// ```rust,no_run
253 /// # use stateset_embedded::*;
254 /// # let commerce = Commerce::new(":memory:")?;
255 /// let metrics = commerce.analytics().return_metrics(
256 /// AnalyticsQuery::new().period(TimePeriod::ThisMonth)
257 /// )?;
258 /// println!("Returns: {}", metrics.total_returns);
259 /// println!("Return rate: {}%", metrics.return_rate_percent);
260 /// # Ok::<(), CommerceError>(())
261 /// ```
262 pub fn return_metrics(&self, query: AnalyticsQuery) -> Result<ReturnMetrics> {
263 self.db.analytics().get_return_metrics(query)
264 }
265
266 // ========================================================================
267 // Forecasting
268 // ========================================================================
269
270 /// Get demand forecast for inventory items.
271 ///
272 /// Predicts future demand based on historical sales data.
273 ///
274 /// # Arguments
275 ///
276 /// * `skus` - Optional list of SKUs to forecast. If None, forecasts all items.
277 /// * `days_ahead` - Number of days to forecast ahead.
278 ///
279 /// # Example
280 ///
281 /// ```rust,no_run
282 /// # use stateset_embedded::*;
283 /// # let commerce = Commerce::new(":memory:")?;
284 /// // Forecast for all items
285 /// let forecasts = commerce.analytics().demand_forecast(None, 30)?;
286 /// for f in forecasts {
287 /// println!("{}: {} units/day, {} days until stockout",
288 /// f.sku,
289 /// f.average_daily_demand,
290 /// f.days_until_stockout.unwrap_or(999)
291 /// );
292 /// }
293 ///
294 /// // Forecast for specific SKUs
295 /// let forecasts = commerce.analytics().demand_forecast(
296 /// Some(vec!["SKU-001".to_string(), "SKU-002".to_string()]),
297 /// 14
298 /// )?;
299 /// # Ok::<(), CommerceError>(())
300 /// ```
301 pub fn demand_forecast(
302 &self,
303 skus: Option<Vec<String>>,
304 days_ahead: u32,
305 ) -> Result<Vec<DemandForecast>> {
306 self.db.analytics().get_demand_forecast(skus, days_ahead)
307 }
308
309 /// Get revenue forecast.
310 ///
311 /// Predicts future revenue based on historical trends.
312 ///
313 /// # Arguments
314 ///
315 /// * `periods_ahead` - Number of periods to forecast.
316 /// * `granularity` - Time granularity (Day, Week, Month).
317 ///
318 /// # Example
319 ///
320 /// ```rust,no_run
321 /// # use stateset_embedded::*;
322 /// # let commerce = Commerce::new(":memory:")?;
323 /// let forecasts = commerce.analytics().revenue_forecast(3, TimeGranularity::Month)?;
324 /// for f in forecasts {
325 /// println!("{}: ${} (${} - ${})",
326 /// f.period,
327 /// f.forecasted_revenue,
328 /// f.lower_bound,
329 /// f.upper_bound
330 /// );
331 /// }
332 /// # Ok::<(), CommerceError>(())
333 /// ```
334 pub fn revenue_forecast(
335 &self,
336 periods_ahead: u32,
337 granularity: TimeGranularity,
338 ) -> Result<Vec<RevenueForecast>> {
339 self.db.analytics().get_revenue_forecast(periods_ahead, granularity)
340 }
341}