1use 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#[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 #[default]
22 Active,
23 Expired,
25 Voided,
27 Transferred,
29}
30
31#[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 #[default]
41 Standard,
42 Extended,
44 Limited,
46 Lifetime,
48 AccidentalDamage,
50 Comprehensive,
52}
53
54#[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 #[default]
64 Submitted,
65 UnderReview,
67 InfoRequested,
69 Approved,
71 Denied,
73 InProgress,
75 Completed,
77 #[strum(serialize = "cancelled", serialize = "canceled")]
79 Cancelled,
80}
81
82#[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 #[default]
92 None,
93 Repair,
95 Replacement,
97 Refund,
99 StoreCredit,
101 Denied,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct Warranty {
108 pub id: WarrantyId,
110 pub warranty_number: String,
112 pub customer_id: CustomerId,
114 pub order_id: Option<OrderId>,
116 pub order_item_id: Option<OrderItemId>,
118 pub product_id: Option<ProductId>,
120 pub sku: Option<String>,
122 pub serial_number: Option<String>,
124 pub status: WarrantyStatus,
126 pub warranty_type: WarrantyType,
128 pub provider: Option<String>,
130 pub coverage_description: Option<String>,
132 pub purchase_date: DateTime<Utc>,
134 pub start_date: DateTime<Utc>,
136 pub end_date: Option<DateTime<Utc>>,
138 pub duration_months: Option<i32>,
140 pub max_coverage_amount: Option<Decimal>,
142 pub deductible: Option<Decimal>,
144 pub max_claims: Option<i32>,
146 pub claims_used: i32,
148 pub terms: Option<String>,
150 pub notes: Option<String>,
152 pub created_at: DateTime<Utc>,
154 pub updated_at: DateTime<Utc>,
156}
157
158impl Warranty {
159 #[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 if let Some(max) = self.max_claims {
179 if self.claims_used >= max {
180 return false;
181 }
182 }
183
184 true
185 }
186
187 #[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#[derive(Debug, Clone, Default, Serialize, Deserialize)]
199pub struct CreateWarranty {
200 pub customer_id: CustomerId,
202 pub order_id: Option<OrderId>,
204 pub order_item_id: Option<OrderItemId>,
206 pub product_id: Option<ProductId>,
208 pub sku: Option<String>,
210 pub serial_number: Option<String>,
212 pub warranty_type: Option<WarrantyType>,
214 pub provider: Option<String>,
216 pub coverage_description: Option<String>,
218 pub purchase_date: Option<DateTime<Utc>>,
220 pub start_date: Option<DateTime<Utc>>,
222 pub end_date: Option<DateTime<Utc>>,
224 pub duration_months: Option<i32>,
226 pub max_coverage_amount: Option<Decimal>,
228 pub deductible: Option<Decimal>,
230 pub max_claims: Option<i32>,
232 pub terms: Option<String>,
234 pub notes: Option<String>,
236}
237
238#[derive(Debug, Clone, Default, Serialize, Deserialize)]
240pub struct UpdateWarranty {
241 pub status: Option<WarrantyStatus>,
243 pub serial_number: Option<String>,
245 pub end_date: Option<DateTime<Utc>>,
247 pub coverage_description: Option<String>,
249 pub terms: Option<String>,
251 pub notes: Option<String>,
253}
254
255#[derive(Debug, Clone, Default, Serialize, Deserialize)]
257pub struct WarrantyFilter {
258 pub customer_id: Option<CustomerId>,
260 pub order_id: Option<OrderId>,
262 pub product_id: Option<ProductId>,
264 pub sku: Option<String>,
266 pub serial_number: Option<String>,
268 pub status: Option<WarrantyStatus>,
270 pub warranty_type: Option<WarrantyType>,
272 pub active_only: Option<bool>,
274 pub expiring_within_days: Option<i32>,
276 pub limit: Option<u32>,
278 pub offset: Option<u32>,
280}
281
282#[derive(Debug, Clone, Serialize, Deserialize)]
284pub struct WarrantyClaim {
285 pub id: Uuid,
287 pub claim_number: String,
289 pub warranty_id: WarrantyId,
291 pub customer_id: CustomerId,
293 pub status: ClaimStatus,
295 pub resolution: ClaimResolution,
297 pub issue_description: String,
299 pub issue_category: Option<String>,
301 pub issue_date: Option<DateTime<Utc>>,
303 pub contact_phone: Option<String>,
305 pub contact_email: Option<String>,
307 pub shipping_address: Option<String>,
309 pub repair_cost: Option<Decimal>,
311 pub replacement_product_id: Option<ProductId>,
313 pub refund_amount: Option<Decimal>,
315 pub denial_reason: Option<String>,
317 pub internal_notes: Option<String>,
319 pub customer_notes: Option<String>,
321 pub submitted_at: DateTime<Utc>,
323 pub approved_at: Option<DateTime<Utc>>,
325 pub resolved_at: Option<DateTime<Utc>>,
327 pub created_at: DateTime<Utc>,
329 pub updated_at: DateTime<Utc>,
331}
332
333#[derive(Debug, Clone, Default, Serialize, Deserialize)]
335pub struct CreateWarrantyClaim {
336 pub warranty_id: WarrantyId,
338 pub issue_description: String,
340 pub issue_category: Option<String>,
342 pub issue_date: Option<DateTime<Utc>>,
344 pub contact_phone: Option<String>,
346 pub contact_email: Option<String>,
348 pub shipping_address: Option<String>,
350 pub customer_notes: Option<String>,
352}
353
354#[derive(Debug, Clone, Default, Serialize, Deserialize)]
356pub struct UpdateWarrantyClaim {
357 pub status: Option<ClaimStatus>,
359 pub resolution: Option<ClaimResolution>,
361 pub repair_cost: Option<Decimal>,
363 pub replacement_product_id: Option<ProductId>,
365 pub refund_amount: Option<Decimal>,
367 pub denial_reason: Option<String>,
369 pub internal_notes: Option<String>,
371 pub customer_notes: Option<String>,
373}
374
375#[derive(Debug, Clone, Default, Serialize, Deserialize)]
377pub struct WarrantyClaimFilter {
378 pub warranty_id: Option<WarrantyId>,
380 pub customer_id: Option<CustomerId>,
382 pub status: Option<ClaimStatus>,
384 pub resolution: Option<ClaimResolution>,
386 pub from_date: Option<DateTime<Utc>>,
388 pub to_date: Option<DateTime<Utc>>,
390 pub limit: Option<u32>,
392 pub offset: Option<u32>,
394}
395
396#[must_use]
398pub fn generate_warranty_number() -> String {
399 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#[must_use]
408pub fn generate_claim_number() -> String {
409 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}