stateset-embedded 0.7.13

Embeddable commerce library - the SQLite of commerce operations
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
//! Serial Number management operations
//!
//! Comprehensive serial number tracking supporting:
//! - Individual unit tracking via unique serial numbers
//! - Full lifecycle management (production to sale to return)
//! - Serial reservations and ownership transfers
//! - Complete audit trail of all serial events
//!
//! # Example
//!
//! ```rust,no_run
//! use stateset_embedded::{Commerce, CreateSerialNumber};
//!
//! let commerce = Commerce::new("./store.db")?;
//!
//! // Create a serial number for a high-value item
//! let serial = commerce.serials().create(CreateSerialNumber {
//!     serial: Some("SN-2025-ABC123".into()),
//!     sku: "LAPTOP-PRO-15".into(),
//!     ..Default::default()
//! })?;
//!
//! println!("Created serial {}", serial.serial);
//! # Ok::<(), stateset_embedded::CommerceError>(())
//! ```

use stateset_core::{
    BatchResult, ChangeSerialStatus, CreateSerialNumber, CreateSerialNumbersBulk, MoveSerial,
    ReserveSerialNumber, Result, SerialFilter, SerialHistory, SerialHistoryFilter,
    SerialLookupResult, SerialNumber, SerialReservation, SerialValidation, TransferSerialOwnership,
    UpdateSerialNumber,
};
use stateset_db::Database;
use std::sync::Arc;
use uuid::Uuid;

/// Serial number management interface.
pub struct Serials {
    db: Arc<dyn Database>,
}

impl std::fmt::Debug for Serials {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Serials").finish_non_exhaustive()
    }
}

impl Serials {
    pub(crate) fn new(db: Arc<dyn Database>) -> Self {
        Self { db }
    }

    // ========================================================================
    // Basic CRUD
    // ========================================================================

    /// Create a serial number.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use stateset_embedded::{Commerce, CreateSerialNumber};
    /// use chrono::Utc;
    ///
    /// let commerce = Commerce::new(":memory:")?;
    ///
    /// let serial = commerce.serials().create(CreateSerialNumber {
    ///     serial: Some("SN-12345".into()),
    ///     sku: "WIDGET-001".into(),
    ///     lot_number: Some("LOT-2025-001".into()),
    ///     manufactured_at: Some(Utc::now()),
    ///     ..Default::default()
    /// })?;
    /// # Ok::<(), stateset_embedded::CommerceError>(())
    /// ```
    pub fn create(&self, input: CreateSerialNumber) -> Result<SerialNumber> {
        self.db.serials().create(input)
    }

    /// Create multiple serial numbers in bulk.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use stateset_embedded::{Commerce, CreateSerialNumbersBulk};
    ///
    /// let commerce = Commerce::new(":memory:")?;
    ///
    /// // Generate 100 serial numbers with prefix
    /// let serials = commerce.serials().create_bulk(CreateSerialNumbersBulk {
    ///     sku: "WIDGET-001".into(),
    ///     quantity: 100,
    ///     prefix: Some("WGT".into()),
    ///     lot_number: Some("LOT-2025-001".into()),
    ///     ..Default::default()
    /// })?;
    ///
    /// println!("Created {} serial numbers", serials.len());
    /// # Ok::<(), stateset_embedded::CommerceError>(())
    /// ```
    pub fn create_bulk(&self, input: CreateSerialNumbersBulk) -> Result<Vec<SerialNumber>> {
        self.db.serials().create_bulk(input)
    }

    /// Get a serial by ID.
    pub fn get(&self, id: Uuid) -> Result<Option<SerialNumber>> {
        self.db.serials().get(id)
    }

    /// Get a serial by its serial number string.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use stateset_embedded::Commerce;
    ///
    /// let commerce = Commerce::new(":memory:")?;
    ///
    /// if let Some(serial) = commerce.serials().get_by_serial("SN-12345")? {
    ///     println!("Serial {} is currently {}", serial.serial, serial.status);
    /// }
    /// # Ok::<(), stateset_embedded::CommerceError>(())
    /// ```
    pub fn get_by_serial(&self, serial: &str) -> Result<Option<SerialNumber>> {
        self.db.serials().get_by_serial(serial)
    }

    /// List serials with optional filtering.
    pub fn list(&self, filter: SerialFilter) -> Result<Vec<SerialNumber>> {
        self.db.serials().list(filter)
    }

    /// Update a serial number.
    pub fn update(&self, id: Uuid, input: UpdateSerialNumber) -> Result<SerialNumber> {
        self.db.serials().update(id, input)
    }

    /// Delete a serial (only if never used).
    pub fn delete(&self, id: Uuid) -> Result<()> {
        self.db.serials().delete(id)
    }

    // ========================================================================
    // Status Management
    // ========================================================================

    /// Change serial status with full tracking.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use stateset_embedded::{Commerce, ChangeSerialStatus, SerialStatus};
    /// use uuid::Uuid;
    ///
    /// let commerce = Commerce::new(":memory:")?;
    ///
    /// commerce.serials().change_status(ChangeSerialStatus {
    ///     serial_id: Uuid::new_v4(),
    ///     new_status: SerialStatus::InService,
    ///     reference_type: Some("repair_order".into()),
    ///     reference_id: Some(Uuid::new_v4()),
    ///     notes: Some("Sent for repair".into()),
    ///     ..Default::default()
    /// })?;
    /// # Ok::<(), stateset_embedded::CommerceError>(())
    /// ```
    pub fn change_status(&self, input: ChangeSerialStatus) -> Result<SerialNumber> {
        self.db.serials().change_status(input)
    }

    /// Mark a serial as sold.
    pub fn mark_sold(
        &self,
        id: Uuid,
        customer_id: Uuid,
        order_id: Option<Uuid>,
    ) -> Result<SerialNumber> {
        self.db.serials().mark_sold(id, customer_id, order_id)
    }

    /// Mark a serial as shipped.
    pub fn mark_shipped(&self, id: Uuid, shipment_id: Uuid) -> Result<SerialNumber> {
        self.db.serials().mark_shipped(id, shipment_id)
    }

    /// Mark a serial as returned.
    pub fn mark_returned(&self, id: Uuid, return_id: Uuid) -> Result<SerialNumber> {
        self.db.serials().mark_returned(id, return_id)
    }

    /// Activate a serial (e.g., for warranty start).
    pub fn activate(&self, id: Uuid) -> Result<SerialNumber> {
        self.db.serials().activate(id)
    }

    /// Quarantine a serial.
    pub fn quarantine(&self, id: Uuid, reason: &str) -> Result<SerialNumber> {
        self.db.serials().quarantine(id, reason)
    }

    /// Release a serial from quarantine.
    pub fn release_quarantine(&self, id: Uuid) -> Result<SerialNumber> {
        self.db.serials().release_quarantine(id)
    }

    /// Scrap a serial.
    pub fn scrap(&self, id: Uuid, reason: &str) -> Result<SerialNumber> {
        self.db.serials().scrap(id, reason)
    }

    // ========================================================================
    // Reservations
    // ========================================================================

    /// Reserve a serial for an order or other purpose.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use stateset_embedded::{Commerce, ReserveSerialNumber};
    /// use uuid::Uuid;
    ///
    /// let commerce = Commerce::new(":memory:")?;
    ///
    /// let reservation = commerce.serials().reserve(ReserveSerialNumber {
    ///     serial_id: Uuid::new_v4(),
    ///     reference_type: "order".into(),
    ///     reference_id: Uuid::new_v4(),
    ///     reserved_by: Some("sales_user".into()),
    ///     expires_in_seconds: Some(3600), // 1 hour
    ///     ..Default::default()
    /// })?;
    ///
    /// println!("Reservation created, expires at {:?}", reservation.expires_at);
    /// # Ok::<(), stateset_embedded::CommerceError>(())
    /// ```
    pub fn reserve(&self, input: ReserveSerialNumber) -> Result<SerialReservation> {
        self.db.serials().reserve(input)
    }

    /// Release a reservation.
    pub fn release_reservation(&self, reservation_id: Uuid) -> Result<()> {
        self.db.serials().release_reservation(reservation_id)
    }

    /// Confirm a reservation (finalize the allocation).
    pub fn confirm_reservation(&self, reservation_id: Uuid) -> Result<()> {
        self.db.serials().confirm_reservation(reservation_id)
    }

    // ========================================================================
    // Location & Ownership
    // ========================================================================

    /// Move a serial to a new location.
    pub fn move_serial(&self, input: MoveSerial) -> Result<SerialNumber> {
        self.db.serials().move_serial(input)
    }

    /// Transfer ownership of a serial.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use stateset_embedded::{Commerce, TransferSerialOwnership};
    /// use uuid::Uuid;
    ///
    /// let commerce = Commerce::new(":memory:")?;
    ///
    /// commerce.serials().transfer_ownership(TransferSerialOwnership {
    ///     serial_id: Uuid::new_v4(),
    ///     new_owner_id: Uuid::new_v4(),
    ///     new_owner_type: "customer".into(),
    ///     notes: Some("Warranty transfer requested".into()),
    ///     ..Default::default()
    /// })?;
    /// # Ok::<(), stateset_embedded::CommerceError>(())
    /// ```
    pub fn transfer_ownership(&self, input: TransferSerialOwnership) -> Result<SerialNumber> {
        self.db.serials().transfer_ownership(input)
    }

    // ========================================================================
    // History & Lookup
    // ========================================================================

    /// Get serial history.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use stateset_embedded::{Commerce, SerialHistoryFilter};
    /// use uuid::Uuid;
    ///
    /// let commerce = Commerce::new(":memory:")?;
    ///
    /// let history = commerce.serials().get_history(
    ///     Uuid::new_v4(),
    ///     SerialHistoryFilter {
    ///         limit: Some(50),
    ///         ..Default::default()
    ///     },
    /// )?;
    ///
    /// for event in history {
    ///     println!("{}: {} -> {}", event.event_type, event.from_status, event.to_status);
    /// }
    /// # Ok::<(), stateset_embedded::CommerceError>(())
    /// ```
    pub fn get_history(
        &self,
        serial_id: Uuid,
        filter: SerialHistoryFilter,
    ) -> Result<Vec<SerialHistory>> {
        self.db.serials().get_history(serial_id, filter)
    }

    /// Full serial lookup with related data.
    ///
    /// Returns the serial along with lot info, warranty status, and recent history.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use stateset_embedded::Commerce;
    ///
    /// let commerce = Commerce::new(":memory:")?;
    ///
    /// if let Some(result) = commerce.serials().lookup("SN-12345")? {
    ///     println!("Serial: {}", result.serial.serial);
    ///     println!("SKU: {}", result.serial.sku);
    ///     println!("Status: {}", result.serial.status);
    ///     if let Some(warranty) = result.warranty_status {
    ///         println!("Warranty active: {}", warranty.is_active);
    ///     }
    /// }
    /// # Ok::<(), stateset_embedded::CommerceError>(())
    /// ```
    pub fn lookup(&self, serial: &str) -> Result<Option<SerialLookupResult>> {
        self.db.serials().lookup(serial)
    }

    /// Validate a serial number.
    ///
    /// Returns validation info without the full serial data.
    pub fn validate(&self, serial: &str) -> Result<SerialValidation> {
        self.db.serials().validate(serial)
    }

    // ========================================================================
    // Queries
    // ========================================================================

    /// Get available serials for a SKU.
    pub fn get_available(&self, sku: &str, limit: u32) -> Result<Vec<SerialNumber>> {
        self.db.serials().get_available_for_sku(sku, limit)
    }

    /// Get serials for a lot.
    pub fn get_for_lot(&self, lot_id: Uuid) -> Result<Vec<SerialNumber>> {
        self.db.serials().get_for_lot(lot_id)
    }

    /// Get serials owned by a customer.
    pub fn get_for_customer(&self, customer_id: Uuid) -> Result<Vec<SerialNumber>> {
        self.db.serials().get_for_customer(customer_id)
    }

    /// Count serials matching filter.
    pub fn count(&self, filter: SerialFilter) -> Result<u64> {
        self.db.serials().count(filter)
    }

    // ========================================================================
    // Batch Operations
    // ========================================================================

    /// Create multiple serials with partial success handling.
    pub fn create_batch(
        &self,
        inputs: Vec<CreateSerialNumber>,
    ) -> Result<BatchResult<SerialNumber>> {
        self.db.serials().create_batch(inputs)
    }

    /// Get multiple serials by ID.
    pub fn get_batch(&self, ids: Vec<Uuid>) -> Result<Vec<SerialNumber>> {
        self.db.serials().get_batch(ids)
    }

    /// Get multiple serials by serial string.
    pub fn get_batch_by_serial(&self, serials: Vec<String>) -> Result<Vec<SerialNumber>> {
        self.db.serials().get_batch_by_serial(serials)
    }

    // ========================================================================
    // Convenience Methods
    // ========================================================================

    /// Check if a serial is available for sale.
    pub fn is_available(&self, serial: &str) -> Result<bool> {
        if let Some(s) = self.get_by_serial(serial)? { Ok(s.is_available()) } else { Ok(false) }
    }

    /// Check if a serial can be shipped.
    pub fn can_ship(&self, serial: &str) -> Result<bool> {
        if let Some(s) = self.get_by_serial(serial)? { Ok(s.can_ship()) } else { Ok(false) }
    }
}