Skip to main content

stateset_embedded/
backorder.rs

1//! Backorder Management operations
2//!
3//! Comprehensive backorder management supporting:
4//! - Backorder creation when inventory is unavailable
5//! - Priority-based fulfillment
6//! - Allocation and auto-allocation
7//! - Status tracking and reporting
8//!
9//! # Example
10//!
11//! ```rust,ignore
12//! use stateset_embedded::{Commerce, CreateBackorder, BackorderPriority};
13//! use rust_decimal_macros::dec;
14//! use uuid::Uuid;
15//!
16//! let commerce = Commerce::new("./store.db")?;
17//!
18//! // Create a backorder when inventory is unavailable
19//! let backorder = commerce.backorder().create_backorder(CreateBackorder {
20//!     order_id: Uuid::new_v4(),
21//!     customer_id: Uuid::new_v4(),
22//!     sku: "WIDGET-001".into(),
23//!     quantity: dec!(50),
24//!     priority: Some(BackorderPriority::High),
25//!     ..Default::default()
26//! })?;
27//!
28//! println!("Created backorder {}", backorder.backorder_number);
29//! # Ok::<(), stateset_embedded::CommerceError>(())
30//! ```
31
32use stateset_core::{
33    AllocateBackorder, Backorder, BackorderAllocation, BackorderFilter, BackorderFulfillment,
34    BackorderSummary, CreateBackorder, FulfillBackorder, Result, SkuBackorderSummary,
35    UpdateBackorder,
36};
37use stateset_db::Database;
38use std::sync::Arc;
39use uuid::Uuid;
40
41/// Backorder Management interface.
42pub struct Backorders {
43    db: Arc<dyn Database>,
44}
45
46impl std::fmt::Debug for Backorders {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        f.debug_struct("Backorders").finish_non_exhaustive()
49    }
50}
51
52impl Backorders {
53    pub(crate) fn new(db: Arc<dyn Database>) -> Self {
54        Self { db }
55    }
56
57    // ========================================================================
58    // Backorder Operations
59    // ========================================================================
60
61    /// Create a backorder.
62    ///
63    /// # Example
64    ///
65    /// ```rust,ignore
66    /// use stateset_embedded::{Commerce, CreateBackorder, BackorderPriority};
67    /// use rust_decimal_macros::dec;
68    /// use chrono::{Utc, Duration};
69    /// use uuid::Uuid;
70    ///
71    /// let commerce = Commerce::new(":memory:")?;
72    ///
73    /// let backorder = commerce.backorder().create_backorder(CreateBackorder {
74    ///     order_id: Uuid::new_v4(),
75    ///     order_line_id: Some(Uuid::new_v4()),
76    ///     customer_id: Uuid::new_v4(),
77    ///     sku: "GADGET-002".into(),
78    ///     quantity: dec!(25),
79    ///     priority: Some(BackorderPriority::Critical),
80    ///     expected_date: Some(Utc::now() + Duration::days(7)),
81    ///     notes: Some("Rush order - customer requested expedite".into()),
82    ///     ..Default::default()
83    /// })?;
84    /// # Ok::<(), stateset_embedded::CommerceError>(())
85    /// ```
86    pub fn create_backorder(&self, input: CreateBackorder) -> Result<Backorder> {
87        self.db.backorder().create_backorder(input)
88    }
89
90    /// Get a backorder by ID.
91    pub fn get_backorder(&self, id: Uuid) -> Result<Option<Backorder>> {
92        self.db.backorder().get_backorder(id)
93    }
94
95    /// Get a backorder by backorder number.
96    pub fn get_backorder_by_number(&self, number: &str) -> Result<Option<Backorder>> {
97        self.db.backorder().get_backorder_by_number(number)
98    }
99
100    /// Update a backorder.
101    pub fn update_backorder(&self, id: Uuid, input: UpdateBackorder) -> Result<Backorder> {
102        self.db.backorder().update_backorder(id, input)
103    }
104
105    /// List backorders with optional filtering.
106    ///
107    /// # Example
108    ///
109    /// ```rust,ignore
110    /// use stateset_embedded::{Commerce, BackorderFilter, BackorderStatus, BackorderPriority};
111    ///
112    /// let commerce = Commerce::new(":memory:")?;
113    ///
114    /// // Get all critical pending backorders
115    /// let backorders = commerce.backorder().list_backorders(BackorderFilter {
116    ///     status: Some(BackorderStatus::Pending),
117    ///     priority: Some(BackorderPriority::Critical),
118    ///     limit: Some(50),
119    ///     ..Default::default()
120    /// })?;
121    /// # Ok::<(), stateset_embedded::CommerceError>(())
122    /// ```
123    pub fn list_backorders(&self, filter: BackorderFilter) -> Result<Vec<Backorder>> {
124        self.db.backorder().list_backorders(filter)
125    }
126
127    /// Cancel a backorder.
128    pub fn cancel_backorder(&self, id: Uuid) -> Result<Backorder> {
129        self.db.backorder().cancel_backorder(id)
130    }
131
132    /// Get all backorders for an order.
133    pub fn get_backorders_for_order(&self, order_id: Uuid) -> Result<Vec<Backorder>> {
134        self.db.backorder().get_backorders_for_order(order_id)
135    }
136
137    /// Get all backorders for a customer.
138    pub fn get_backorders_for_customer(&self, customer_id: Uuid) -> Result<Vec<Backorder>> {
139        self.db.backorder().get_backorders_for_customer(customer_id)
140    }
141
142    /// Get all backorders for a SKU.
143    pub fn get_backorders_for_sku(&self, sku: &str) -> Result<Vec<Backorder>> {
144        self.db.backorder().get_backorders_for_sku(sku)
145    }
146
147    // ========================================================================
148    // Fulfillment Operations
149    // ========================================================================
150
151    /// Fulfill a backorder (partial or complete).
152    ///
153    /// # Example
154    ///
155    /// ```rust,ignore
156    /// use stateset_embedded::{Commerce, FulfillBackorder, FulfillmentSourceType};
157    /// use rust_decimal_macros::dec;
158    /// use uuid::Uuid;
159    ///
160    /// let commerce = Commerce::new(":memory:")?;
161    ///
162    /// // Fulfill from received inventory
163    /// let backorder = commerce.backorder().fulfill_backorder(FulfillBackorder {
164    ///     backorder_id: Uuid::new_v4(),
165    ///     quantity: dec!(25),
166    ///     source_type: FulfillmentSourceType::PurchaseOrder,
167    ///     source_id: Some(Uuid::new_v4()), // PO receipt ID
168    ///     notes: Some("Fulfilled from PO-123".into()),
169    ///     fulfilled_by: Some("warehouse_user".into()),
170    /// })?;
171    ///
172    /// println!("Remaining: {}", backorder.quantity_remaining);
173    /// # Ok::<(), stateset_embedded::CommerceError>(())
174    /// ```
175    pub fn fulfill_backorder(&self, input: FulfillBackorder) -> Result<Backorder> {
176        self.db.backorder().fulfill_backorder(input)
177    }
178
179    /// Get fulfillment history for a backorder.
180    pub fn get_fulfillment_history(&self, backorder_id: Uuid) -> Result<Vec<BackorderFulfillment>> {
181        self.db.backorder().get_fulfillment_history(backorder_id)
182    }
183
184    // ========================================================================
185    // Allocation Operations
186    // ========================================================================
187
188    /// Allocate inventory to a backorder.
189    ///
190    /// Reserves inventory for the backorder until it can be fulfilled.
191    pub fn allocate_backorder(&self, input: AllocateBackorder) -> Result<BackorderAllocation> {
192        self.db.backorder().allocate_backorder(input)
193    }
194
195    /// Get allocations for a backorder.
196    pub fn get_allocations(&self, backorder_id: Uuid) -> Result<Vec<BackorderAllocation>> {
197        self.db.backorder().get_allocations(backorder_id)
198    }
199
200    /// Release an allocation.
201    pub fn release_allocation(&self, allocation_id: Uuid) -> Result<BackorderAllocation> {
202        self.db.backorder().release_allocation(allocation_id)
203    }
204
205    /// Confirm an allocation.
206    pub fn confirm_allocation(&self, allocation_id: Uuid) -> Result<BackorderAllocation> {
207        self.db.backorder().confirm_allocation(allocation_id)
208    }
209
210    /// Expire old allocations past their expiration date.
211    pub fn expire_allocations(&self) -> Result<u32> {
212        self.db.backorder().expire_allocations()
213    }
214
215    /// Automatically allocate available inventory to pending backorders.
216    ///
217    /// Allocates in priority order (critical first, then by oldest date).
218    pub fn auto_allocate_inventory(&self, sku: &str) -> Result<Vec<BackorderAllocation>> {
219        self.db.backorder().auto_allocate_inventory(sku)
220    }
221
222    // ========================================================================
223    // Analytics
224    // ========================================================================
225
226    /// Get overall backorder summary.
227    ///
228    /// # Example
229    ///
230    /// ```rust,ignore
231    /// use stateset_embedded::Commerce;
232    ///
233    /// let commerce = Commerce::new(":memory:")?;
234    ///
235    /// let summary = commerce.backorder().get_summary()?;
236    /// println!("Total backorders: {}", summary.total_backorders);
237    /// println!("Critical: {}", summary.critical_count);
238    /// println!("Overdue: {}", summary.overdue_count);
239    /// # Ok::<(), stateset_embedded::CommerceError>(())
240    /// ```
241    pub fn get_summary(&self) -> Result<BackorderSummary> {
242        self.db.backorder().get_summary()
243    }
244
245    /// Get backorder summary for a specific SKU.
246    pub fn get_sku_summary(&self, sku: &str) -> Result<Option<SkuBackorderSummary>> {
247        self.db.backorder().get_sku_summary(sku)
248    }
249
250    /// Get overdue backorders.
251    pub fn get_overdue_backorders(&self) -> Result<Vec<Backorder>> {
252        self.db.backorder().get_overdue_backorders()
253    }
254
255    /// Count pending backorders.
256    pub fn count_pending(&self) -> Result<u64> {
257        self.db.backorder().count_pending()
258    }
259}