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}