Skip to main content

stateset_core/models/
warranty.rs

1//! Warranty domain models
2//!
3//! Handles warranty registration, coverage tracking, and claims processing.
4
5use chrono::{DateTime, Utc};
6use rust_decimal::Decimal;
7use serde::{Deserialize, Serialize};
8use stateset_primitives::{CustomerId, OrderId, OrderItemId, ProductId, WarrantyId};
9use strum::{Display, EnumString};
10use uuid::Uuid;
11
12/// Warranty status
13#[derive(
14    Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, Display, EnumString,
15)]
16#[serde(rename_all = "snake_case")]
17#[strum(serialize_all = "snake_case", ascii_case_insensitive)]
18#[non_exhaustive]
19pub enum WarrantyStatus {
20    /// Warranty is active and valid
21    #[default]
22    Active,
23    /// Warranty has expired
24    Expired,
25    /// Warranty was voided
26    Voided,
27    /// Warranty was transferred to another owner
28    Transferred,
29}
30
31/// Warranty type/tier
32#[derive(
33    Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, Display, EnumString,
34)]
35#[serde(rename_all = "snake_case")]
36#[strum(serialize_all = "snake_case", ascii_case_insensitive)]
37#[non_exhaustive]
38pub enum WarrantyType {
39    /// Standard manufacturer warranty
40    #[default]
41    Standard,
42    /// Extended warranty
43    Extended,
44    /// Limited warranty
45    Limited,
46    /// Lifetime warranty
47    Lifetime,
48    /// Accidental damage protection
49    AccidentalDamage,
50    /// Comprehensive coverage
51    Comprehensive,
52}
53
54/// Warranty claim status
55#[derive(
56    Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, Display, EnumString,
57)]
58#[serde(rename_all = "snake_case")]
59#[strum(serialize_all = "snake_case", ascii_case_insensitive)]
60#[non_exhaustive]
61pub enum ClaimStatus {
62    /// Claim submitted, awaiting review
63    #[default]
64    Submitted,
65    /// Claim is under review
66    UnderReview,
67    /// Additional information requested
68    InfoRequested,
69    /// Claim approved
70    Approved,
71    /// Claim denied
72    Denied,
73    /// Repair/replacement in progress
74    InProgress,
75    /// Claim completed/resolved
76    Completed,
77    /// Claim was cancelled
78    #[strum(serialize = "cancelled", serialize = "canceled")]
79    Cancelled,
80}
81
82/// Claim resolution type
83#[derive(
84    Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, Display, EnumString,
85)]
86#[serde(rename_all = "snake_case")]
87#[strum(serialize_all = "snake_case", ascii_case_insensitive)]
88#[non_exhaustive]
89pub enum ClaimResolution {
90    /// No resolution yet
91    #[default]
92    None,
93    /// Item was repaired
94    Repair,
95    /// Item was replaced
96    Replacement,
97    /// Customer received refund
98    Refund,
99    /// Store credit issued
100    StoreCredit,
101    /// Claim was denied
102    Denied,
103}
104
105/// A warranty registration
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct Warranty {
108    /// Unique warranty ID
109    pub id: WarrantyId,
110    /// Human-readable warranty number
111    pub warranty_number: String,
112    /// Customer who owns the warranty
113    pub customer_id: CustomerId,
114    /// Associated order ID
115    pub order_id: Option<OrderId>,
116    /// Associated order item ID
117    pub order_item_id: Option<OrderItemId>,
118    /// Product ID
119    pub product_id: Option<ProductId>,
120    /// Product SKU
121    pub sku: Option<String>,
122    /// Product serial number
123    pub serial_number: Option<String>,
124    /// Warranty status
125    pub status: WarrantyStatus,
126    /// Warranty type
127    pub warranty_type: WarrantyType,
128    /// Warranty provider/issuer
129    pub provider: Option<String>,
130    /// Coverage description
131    pub coverage_description: Option<String>,
132    /// Purchase date
133    pub purchase_date: DateTime<Utc>,
134    /// Warranty start date
135    pub start_date: DateTime<Utc>,
136    /// Warranty end date
137    pub end_date: Option<DateTime<Utc>>,
138    /// Duration in months (for non-lifetime)
139    pub duration_months: Option<i32>,
140    /// Maximum coverage amount
141    pub max_coverage_amount: Option<Decimal>,
142    /// Deductible amount
143    pub deductible: Option<Decimal>,
144    /// Number of claims allowed
145    pub max_claims: Option<i32>,
146    /// Number of claims used
147    pub claims_used: i32,
148    /// Additional terms and conditions
149    pub terms: Option<String>,
150    /// Notes
151    pub notes: Option<String>,
152    /// When warranty was created
153    pub created_at: DateTime<Utc>,
154    /// When warranty was last updated
155    pub updated_at: DateTime<Utc>,
156}
157
158impl Warranty {
159    /// Check if the warranty is currently valid
160    #[must_use]
161    pub fn is_valid(&self) -> bool {
162        if self.status != WarrantyStatus::Active {
163            return false;
164        }
165
166        let now = Utc::now();
167        if now < self.start_date {
168            return false;
169        }
170
171        if let Some(end_date) = self.end_date {
172            if now > end_date {
173                return false;
174            }
175        }
176
177        // Check if max claims exceeded
178        if let Some(max) = self.max_claims {
179            if self.claims_used >= max {
180                return false;
181            }
182        }
183
184        true
185    }
186
187    /// Get remaining days of coverage
188    #[must_use]
189    pub fn days_remaining(&self) -> Option<i64> {
190        self.end_date.map(|end| {
191            let now = Utc::now();
192            (end - now).num_days().max(0)
193        })
194    }
195}
196
197/// Input for creating a warranty
198#[derive(Debug, Clone, Default, Serialize, Deserialize)]
199pub struct CreateWarranty {
200    /// Customer ID
201    pub customer_id: CustomerId,
202    /// Order ID
203    pub order_id: Option<OrderId>,
204    /// Order item ID
205    pub order_item_id: Option<OrderItemId>,
206    /// Product ID
207    pub product_id: Option<ProductId>,
208    /// Product SKU
209    pub sku: Option<String>,
210    /// Serial number
211    pub serial_number: Option<String>,
212    /// Warranty type
213    pub warranty_type: Option<WarrantyType>,
214    /// Provider
215    pub provider: Option<String>,
216    /// Coverage description
217    pub coverage_description: Option<String>,
218    /// Purchase date (defaults to now)
219    pub purchase_date: Option<DateTime<Utc>>,
220    /// Start date (defaults to purchase date)
221    pub start_date: Option<DateTime<Utc>>,
222    /// End date (calculated from duration if not provided)
223    pub end_date: Option<DateTime<Utc>>,
224    /// Duration in months
225    pub duration_months: Option<i32>,
226    /// Max coverage amount
227    pub max_coverage_amount: Option<Decimal>,
228    /// Deductible
229    pub deductible: Option<Decimal>,
230    /// Max claims allowed
231    pub max_claims: Option<i32>,
232    /// Terms and conditions
233    pub terms: Option<String>,
234    /// Notes
235    pub notes: Option<String>,
236}
237
238/// Input for updating a warranty
239#[derive(Debug, Clone, Default, Serialize, Deserialize)]
240pub struct UpdateWarranty {
241    /// Update status
242    pub status: Option<WarrantyStatus>,
243    /// Update serial number
244    pub serial_number: Option<String>,
245    /// Update end date
246    pub end_date: Option<DateTime<Utc>>,
247    /// Update coverage description
248    pub coverage_description: Option<String>,
249    /// Update terms
250    pub terms: Option<String>,
251    /// Update notes
252    pub notes: Option<String>,
253}
254
255/// Filter for listing warranties
256#[derive(Debug, Clone, Default, Serialize, Deserialize)]
257pub struct WarrantyFilter {
258    /// Filter by customer ID
259    pub customer_id: Option<CustomerId>,
260    /// Filter by order ID
261    pub order_id: Option<OrderId>,
262    /// Filter by product ID
263    pub product_id: Option<ProductId>,
264    /// Filter by SKU
265    pub sku: Option<String>,
266    /// Filter by serial number
267    pub serial_number: Option<String>,
268    /// Filter by status
269    pub status: Option<WarrantyStatus>,
270    /// Filter by warranty type
271    pub warranty_type: Option<WarrantyType>,
272    /// Filter by active only (not expired)
273    pub active_only: Option<bool>,
274    /// Filter by expiring within days
275    pub expiring_within_days: Option<i32>,
276    /// Limit results
277    pub limit: Option<u32>,
278    /// Offset for pagination
279    pub offset: Option<u32>,
280}
281
282/// A warranty claim
283#[derive(Debug, Clone, Serialize, Deserialize)]
284pub struct WarrantyClaim {
285    /// Unique claim ID
286    pub id: Uuid,
287    /// Human-readable claim number
288    pub claim_number: String,
289    /// Associated warranty ID
290    pub warranty_id: WarrantyId,
291    /// Customer ID
292    pub customer_id: CustomerId,
293    /// Claim status
294    pub status: ClaimStatus,
295    /// Resolution type
296    pub resolution: ClaimResolution,
297    /// Issue description
298    pub issue_description: String,
299    /// Issue category
300    pub issue_category: Option<String>,
301    /// Date issue occurred
302    pub issue_date: Option<DateTime<Utc>>,
303    /// Contact phone
304    pub contact_phone: Option<String>,
305    /// Contact email
306    pub contact_email: Option<String>,
307    /// Shipping address for returns/replacements
308    pub shipping_address: Option<String>,
309    /// Repair cost (if repair)
310    pub repair_cost: Option<Decimal>,
311    /// Replacement product ID (if replacement)
312    pub replacement_product_id: Option<ProductId>,
313    /// Refund amount (if refund)
314    pub refund_amount: Option<Decimal>,
315    /// Denial reason (if denied)
316    pub denial_reason: Option<String>,
317    /// Internal notes
318    pub internal_notes: Option<String>,
319    /// Customer-facing notes
320    pub customer_notes: Option<String>,
321    /// When claim was submitted
322    pub submitted_at: DateTime<Utc>,
323    /// When claim was approved
324    pub approved_at: Option<DateTime<Utc>>,
325    /// When claim was resolved
326    pub resolved_at: Option<DateTime<Utc>>,
327    /// When claim was created
328    pub created_at: DateTime<Utc>,
329    /// When claim was last updated
330    pub updated_at: DateTime<Utc>,
331}
332
333/// Input for creating a warranty claim
334#[derive(Debug, Clone, Default, Serialize, Deserialize)]
335pub struct CreateWarrantyClaim {
336    /// Warranty ID
337    pub warranty_id: WarrantyId,
338    /// Issue description
339    pub issue_description: String,
340    /// Issue category
341    pub issue_category: Option<String>,
342    /// Date issue occurred
343    pub issue_date: Option<DateTime<Utc>>,
344    /// Contact phone
345    pub contact_phone: Option<String>,
346    /// Contact email
347    pub contact_email: Option<String>,
348    /// Shipping address
349    pub shipping_address: Option<String>,
350    /// Customer notes
351    pub customer_notes: Option<String>,
352}
353
354/// Input for updating a warranty claim
355#[derive(Debug, Clone, Default, Serialize, Deserialize)]
356pub struct UpdateWarrantyClaim {
357    /// Update status
358    pub status: Option<ClaimStatus>,
359    /// Update resolution
360    pub resolution: Option<ClaimResolution>,
361    /// Update repair cost
362    pub repair_cost: Option<Decimal>,
363    /// Update replacement product ID
364    pub replacement_product_id: Option<ProductId>,
365    /// Update refund amount
366    pub refund_amount: Option<Decimal>,
367    /// Update denial reason
368    pub denial_reason: Option<String>,
369    /// Update internal notes
370    pub internal_notes: Option<String>,
371    /// Update customer notes
372    pub customer_notes: Option<String>,
373}
374
375/// Filter for listing warranty claims
376#[derive(Debug, Clone, Default, Serialize, Deserialize)]
377pub struct WarrantyClaimFilter {
378    /// Filter by warranty ID
379    pub warranty_id: Option<WarrantyId>,
380    /// Filter by customer ID
381    pub customer_id: Option<CustomerId>,
382    /// Filter by status
383    pub status: Option<ClaimStatus>,
384    /// Filter by resolution
385    pub resolution: Option<ClaimResolution>,
386    /// Filter by date range start
387    pub from_date: Option<DateTime<Utc>>,
388    /// Filter by date range end
389    pub to_date: Option<DateTime<Utc>>,
390    /// Limit results
391    pub limit: Option<u32>,
392    /// Offset for pagination
393    pub offset: Option<u32>,
394}
395
396/// Generate a unique warranty number
397#[must_use]
398pub fn generate_warranty_number() -> String {
399    // Millisecond timestamp + random UUID suffix so concurrent or rapid-fire
400    // warranty creation cannot collide on the UNIQUE constraint.
401    let now = chrono::Utc::now();
402    let suffix = &uuid::Uuid::new_v4().simple().to_string()[..8];
403    format!("WRN-{}-{suffix}", now.format("%Y%m%d%H%M%S%3f"))
404}
405
406/// Generate a unique claim number
407#[must_use]
408pub fn generate_claim_number() -> String {
409    // Millisecond timestamp + random UUID suffix so concurrent or rapid-fire
410    // claim creation cannot collide on the UNIQUE constraint.
411    let now = chrono::Utc::now();
412    let suffix = &uuid::Uuid::new_v4().simple().to_string()[..8];
413    format!("CLM-{}-{suffix}", now.format("%Y%m%d%H%M%S%3f"))
414}