Skip to main content

backbone_maintenance/domain/entity/
maintenance_request.rs

1use chrono::{DateTime, Utc, NaiveDate};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use uuid::Uuid;
5use rust_decimal::Decimal;
6
7use super::RequestKanbanState;
8use super::RequestPriority;
9use super::MaintenanceType;
10use super::RepeatUnit;
11use super::RepeatType;
12use super::AuditMetadata;
13
14/// Strongly-typed ID for MaintenanceRequest
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
16#[serde(transparent)]
17pub struct MaintenanceRequestId(pub Uuid);
18
19impl MaintenanceRequestId {
20    pub fn new(id: Uuid) -> Self { Self(id) }
21    pub fn generate() -> Self { Self(Uuid::new_v4()) }
22    pub fn into_inner(self) -> Uuid { self.0 }
23}
24
25impl std::fmt::Display for MaintenanceRequestId {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        write!(f, "{}", self.0)
28    }
29}
30
31impl std::str::FromStr for MaintenanceRequestId {
32    type Err = uuid::Error;
33    fn from_str(s: &str) -> Result<Self, Self::Err> {
34        Ok(Self(Uuid::parse_str(s)?))
35    }
36}
37
38impl From<Uuid> for MaintenanceRequestId {
39    fn from(id: Uuid) -> Self { Self(id) }
40}
41
42impl From<MaintenanceRequestId> for Uuid {
43    fn from(id: MaintenanceRequestId) -> Self { id.0 }
44}
45
46impl AsRef<Uuid> for MaintenanceRequestId {
47    fn as_ref(&self) -> &Uuid { &self.0 }
48}
49
50impl std::ops::Deref for MaintenanceRequestId {
51    type Target = Uuid;
52    fn deref(&self) -> &Self::Target { &self.0 }
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
56pub struct MaintenanceRequest {
57    pub id: Uuid,
58    pub name: String,
59    pub description: Option<String>,
60    pub request_date: NaiveDate,
61    pub schedule_date: Option<DateTime<Utc>>,
62    pub schedule_end: Option<DateTime<Utc>>,
63    pub close_date: Option<NaiveDate>,
64    pub duration: Decimal,
65    pub owner_user_id: Option<Uuid>,
66    pub user_id: Option<Uuid>,
67    pub asset_id: Option<Uuid>,
68    pub stage_id: Uuid,
69    pub kanban_state: RequestKanbanState,
70    pub priority: RequestPriority,
71    pub maintenance_type: MaintenanceType,
72    pub recurring: bool,
73    pub repeat_interval: i32,
74    pub repeat_unit: RepeatUnit,
75    pub repeat_type: RepeatType,
76    pub repeat_until: Option<NaiveDate>,
77    pub successor_request_id: Option<Uuid>,
78    pub successor_of_request_id: Option<Uuid>,
79    #[serde(default)]
80    #[sqlx(json)]
81    pub metadata: AuditMetadata,
82}
83
84impl MaintenanceRequest {
85    /// Create a builder for MaintenanceRequest
86    pub fn builder() -> MaintenanceRequestBuilder {
87        <MaintenanceRequestBuilder as Default>::default()
88    }
89
90    /// Create a new MaintenanceRequest with required fields
91    pub fn new(name: String, request_date: NaiveDate, duration: Decimal, stage_id: Uuid, kanban_state: RequestKanbanState, priority: RequestPriority, maintenance_type: MaintenanceType, recurring: bool, repeat_interval: i32, repeat_unit: RepeatUnit, repeat_type: RepeatType) -> Self {
92        Self {
93            id: Uuid::new_v4(),
94            name,
95            description: None,
96            request_date,
97            schedule_date: None,
98            schedule_end: None,
99            close_date: None,
100            duration,
101            owner_user_id: None,
102            user_id: None,
103            asset_id: None,
104            stage_id,
105            kanban_state,
106            priority,
107            maintenance_type,
108            recurring,
109            repeat_interval,
110            repeat_unit,
111            repeat_type,
112            repeat_until: None,
113            successor_request_id: None,
114            successor_of_request_id: None,
115            metadata: AuditMetadata::default(),
116        }
117    }
118
119    /// Get the entity's unique identifier
120    pub fn id(&self) -> &Uuid {
121        &self.id
122    }
123
124    /// Get a strongly-typed ID for this entity
125    pub fn typed_id(&self) -> MaintenanceRequestId {
126        MaintenanceRequestId(self.id)
127    }
128
129    /// Get when this entity was created
130    pub fn created_at(&self) -> Option<&DateTime<Utc>> {
131        self.metadata.created_at.as_ref()
132    }
133
134    /// Get when this entity was last updated
135    pub fn updated_at(&self) -> Option<&DateTime<Utc>> {
136        self.metadata.updated_at.as_ref()
137    }
138
139    /// Check if this entity is soft deleted
140    pub fn is_deleted(&self) -> bool {
141        self.metadata.deleted_at.is_some()
142    }
143
144    /// Check if this entity is active (not deleted)
145    pub fn is_active(&self) -> bool {
146        self.metadata.deleted_at.is_none()
147    }
148
149    /// Get when this entity was deleted
150    pub fn deleted_at(&self) -> Option<&DateTime<Utc>> {
151        self.metadata.deleted_at.as_ref()
152    }
153
154    /// Get who created this entity
155    pub fn created_by(&self) -> Option<&Uuid> {
156        self.metadata.created_by.as_ref()
157    }
158
159    /// Get who last updated this entity
160    pub fn updated_by(&self) -> Option<&Uuid> {
161        self.metadata.updated_by.as_ref()
162    }
163
164    /// Get who deleted this entity
165    pub fn deleted_by(&self) -> Option<&Uuid> {
166        self.metadata.deleted_by.as_ref()
167    }
168
169
170    // ==========================================================
171    // Fluent Setters (with_* for optional fields)
172    // ==========================================================
173
174    /// Set the description field (chainable)
175    pub fn with_description(mut self, value: String) -> Self {
176        self.description = Some(value);
177        self
178    }
179
180    /// Set the schedule_date field (chainable)
181    pub fn with_schedule_date(mut self, value: DateTime<Utc>) -> Self {
182        self.schedule_date = Some(value);
183        self
184    }
185
186    /// Set the schedule_end field (chainable)
187    pub fn with_schedule_end(mut self, value: DateTime<Utc>) -> Self {
188        self.schedule_end = Some(value);
189        self
190    }
191
192    /// Set the close_date field (chainable)
193    pub fn with_close_date(mut self, value: NaiveDate) -> Self {
194        self.close_date = Some(value);
195        self
196    }
197
198    /// Set the owner_user_id field (chainable)
199    pub fn with_owner_user_id(mut self, value: Uuid) -> Self {
200        self.owner_user_id = Some(value);
201        self
202    }
203
204    /// Set the user_id field (chainable)
205    pub fn with_user_id(mut self, value: Uuid) -> Self {
206        self.user_id = Some(value);
207        self
208    }
209
210    /// Set the asset_id field (chainable)
211    pub fn with_asset_id(mut self, value: Uuid) -> Self {
212        self.asset_id = Some(value);
213        self
214    }
215
216    /// Set the repeat_until field (chainable)
217    pub fn with_repeat_until(mut self, value: NaiveDate) -> Self {
218        self.repeat_until = Some(value);
219        self
220    }
221
222    /// Set the successor_request_id field (chainable)
223    pub fn with_successor_request_id(mut self, value: Uuid) -> Self {
224        self.successor_request_id = Some(value);
225        self
226    }
227
228    /// Set the successor_of_request_id field (chainable)
229    pub fn with_successor_of_request_id(mut self, value: Uuid) -> Self {
230        self.successor_of_request_id = Some(value);
231        self
232    }
233
234    // ==========================================================
235    // Partial Update
236    // ==========================================================
237
238    /// Apply partial updates from a map of field name to JSON value
239    pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
240        for (key, value) in fields {
241            match key.as_str() {
242                "name" => {
243                    if let Ok(v) = serde_json::from_value(value) { self.name = v; }
244                }
245                "description" => {
246                    if let Ok(v) = serde_json::from_value(value) { self.description = v; }
247                }
248                "request_date" => {
249                    if let Ok(v) = serde_json::from_value(value) { self.request_date = v; }
250                }
251                "schedule_date" => {
252                    if let Ok(v) = serde_json::from_value(value) { self.schedule_date = v; }
253                }
254                "schedule_end" => {
255                    if let Ok(v) = serde_json::from_value(value) { self.schedule_end = v; }
256                }
257                "close_date" => {
258                    if let Ok(v) = serde_json::from_value(value) { self.close_date = v; }
259                }
260                "duration" => {
261                    if let Ok(v) = serde_json::from_value(value) { self.duration = v; }
262                }
263                "owner_user_id" => {
264                    if let Ok(v) = serde_json::from_value(value) { self.owner_user_id = v; }
265                }
266                "user_id" => {
267                    if let Ok(v) = serde_json::from_value(value) { self.user_id = v; }
268                }
269                "asset_id" => {
270                    if let Ok(v) = serde_json::from_value(value) { self.asset_id = v; }
271                }
272                "stage_id" => {
273                    if let Ok(v) = serde_json::from_value(value) { self.stage_id = v; }
274                }
275                "kanban_state" => {
276                    if let Ok(v) = serde_json::from_value(value) { self.kanban_state = v; }
277                }
278                "priority" => {
279                    if let Ok(v) = serde_json::from_value(value) { self.priority = v; }
280                }
281                "maintenance_type" => {
282                    if let Ok(v) = serde_json::from_value(value) { self.maintenance_type = v; }
283                }
284                "recurring" => {
285                    if let Ok(v) = serde_json::from_value(value) { self.recurring = v; }
286                }
287                "repeat_interval" => {
288                    if let Ok(v) = serde_json::from_value(value) { self.repeat_interval = v; }
289                }
290                "repeat_unit" => {
291                    if let Ok(v) = serde_json::from_value(value) { self.repeat_unit = v; }
292                }
293                "repeat_type" => {
294                    if let Ok(v) = serde_json::from_value(value) { self.repeat_type = v; }
295                }
296                "repeat_until" => {
297                    if let Ok(v) = serde_json::from_value(value) { self.repeat_until = v; }
298                }
299                "successor_request_id" => {
300                    if let Ok(v) = serde_json::from_value(value) { self.successor_request_id = v; }
301                }
302                "successor_of_request_id" => {
303                    if let Ok(v) = serde_json::from_value(value) { self.successor_of_request_id = v; }
304                }
305                _ => {} // ignore unknown fields
306            }
307        }
308    }
309
310    // <<< CUSTOM METHODS START >>>
311    // <<< CUSTOM METHODS END >>>
312}
313
314impl super::Entity for MaintenanceRequest {
315    type Id = Uuid;
316
317    fn entity_id(&self) -> &Self::Id {
318        &self.id
319    }
320
321    fn entity_type() -> &'static str {
322        "MaintenanceRequest"
323    }
324}
325
326impl backbone_core::PersistentEntity for MaintenanceRequest {
327    fn entity_id(&self) -> String {
328        self.id.to_string()
329    }
330    fn set_entity_id(&mut self, id: String) {
331        if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
332            self.id = uuid;
333        }
334    }
335    fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
336        self.metadata.created_at
337    }
338    fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
339        self.metadata.created_at = Some(ts);
340    }
341    fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
342        self.metadata.updated_at
343    }
344    fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
345        self.metadata.updated_at = Some(ts);
346    }
347    fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
348        self.metadata.deleted_at
349    }
350    fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
351        self.metadata.deleted_at = ts;
352    }
353}
354
355impl backbone_orm::EntityRepoMeta for MaintenanceRequest {
356    fn column_types() -> std::collections::HashMap<String, String> {
357        let mut m = std::collections::HashMap::new();
358        m.insert("id".to_string(), "uuid".to_string());
359        m.insert("owner_user_id".to_string(), "uuid".to_string());
360        m.insert("user_id".to_string(), "uuid".to_string());
361        m.insert("asset_id".to_string(), "uuid".to_string());
362        m.insert("stage_id".to_string(), "uuid".to_string());
363        m.insert("successor_request_id".to_string(), "uuid".to_string());
364        m.insert("successor_of_request_id".to_string(), "uuid".to_string());
365        m.insert("kanban_state".to_string(), "request_kanban_state".to_string());
366        m.insert("priority".to_string(), "request_priority".to_string());
367        m.insert("maintenance_type".to_string(), "maintenance_type".to_string());
368        m.insert("repeat_unit".to_string(), "repeat_unit".to_string());
369        m.insert("repeat_type".to_string(), "repeat_type".to_string());
370        m.insert("request_date".to_string(), "date".to_string());
371        m.insert("schedule_date".to_string(), "timestamptz".to_string());
372        m.insert("schedule_end".to_string(), "timestamptz".to_string());
373        m.insert("close_date".to_string(), "date".to_string());
374        m.insert("repeat_until".to_string(), "date".to_string());
375        m
376    }
377    fn search_fields() -> &'static [&'static str] {
378        &["name"]
379    }
380}
381
382/// Builder for MaintenanceRequest entity
383///
384/// Provides a fluent API for constructing MaintenanceRequest instances.
385/// System fields (id, metadata, timestamps) are auto-initialized.
386#[derive(Debug, Clone, Default)]
387pub struct MaintenanceRequestBuilder {
388    name: Option<String>,
389    description: Option<String>,
390    request_date: Option<NaiveDate>,
391    schedule_date: Option<DateTime<Utc>>,
392    schedule_end: Option<DateTime<Utc>>,
393    close_date: Option<NaiveDate>,
394    duration: Option<Decimal>,
395    owner_user_id: Option<Uuid>,
396    user_id: Option<Uuid>,
397    asset_id: Option<Uuid>,
398    stage_id: Option<Uuid>,
399    kanban_state: Option<RequestKanbanState>,
400    priority: Option<RequestPriority>,
401    maintenance_type: Option<MaintenanceType>,
402    recurring: Option<bool>,
403    repeat_interval: Option<i32>,
404    repeat_unit: Option<RepeatUnit>,
405    repeat_type: Option<RepeatType>,
406    repeat_until: Option<NaiveDate>,
407    successor_request_id: Option<Uuid>,
408    successor_of_request_id: Option<Uuid>,
409}
410
411impl MaintenanceRequestBuilder {
412    /// Set the name field (required)
413    pub fn name(mut self, value: String) -> Self {
414        self.name = Some(value);
415        self
416    }
417
418    /// Set the description field (optional)
419    pub fn description(mut self, value: String) -> Self {
420        self.description = Some(value);
421        self
422    }
423
424    /// Set the request_date field (default: `Default::default()`)
425    pub fn request_date(mut self, value: NaiveDate) -> Self {
426        self.request_date = Some(value);
427        self
428    }
429
430    /// Set the schedule_date field (optional)
431    pub fn schedule_date(mut self, value: DateTime<Utc>) -> Self {
432        self.schedule_date = Some(value);
433        self
434    }
435
436    /// Set the schedule_end field (optional)
437    pub fn schedule_end(mut self, value: DateTime<Utc>) -> Self {
438        self.schedule_end = Some(value);
439        self
440    }
441
442    /// Set the close_date field (optional)
443    pub fn close_date(mut self, value: NaiveDate) -> Self {
444        self.close_date = Some(value);
445        self
446    }
447
448    /// Set the duration field (default: `Decimal::from(0)`)
449    pub fn duration(mut self, value: Decimal) -> Self {
450        self.duration = Some(value);
451        self
452    }
453
454    /// Set the owner_user_id field (optional)
455    pub fn owner_user_id(mut self, value: Uuid) -> Self {
456        self.owner_user_id = Some(value);
457        self
458    }
459
460    /// Set the user_id field (optional)
461    pub fn user_id(mut self, value: Uuid) -> Self {
462        self.user_id = Some(value);
463        self
464    }
465
466    /// Set the asset_id field (optional)
467    pub fn asset_id(mut self, value: Uuid) -> Self {
468        self.asset_id = Some(value);
469        self
470    }
471
472    /// Set the stage_id field (required)
473    pub fn stage_id(mut self, value: Uuid) -> Self {
474        self.stage_id = Some(value);
475        self
476    }
477
478    /// Set the kanban_state field (default: `RequestKanbanState::default()`)
479    pub fn kanban_state(mut self, value: RequestKanbanState) -> Self {
480        self.kanban_state = Some(value);
481        self
482    }
483
484    /// Set the priority field (required)
485    pub fn priority(mut self, value: RequestPriority) -> Self {
486        self.priority = Some(value);
487        self
488    }
489
490    /// Set the maintenance_type field (default: `MaintenanceType::default()`)
491    pub fn maintenance_type(mut self, value: MaintenanceType) -> Self {
492        self.maintenance_type = Some(value);
493        self
494    }
495
496    /// Set the recurring field (default: `false`)
497    pub fn recurring(mut self, value: bool) -> Self {
498        self.recurring = Some(value);
499        self
500    }
501
502    /// Set the repeat_interval field (default: `1`)
503    pub fn repeat_interval(mut self, value: i32) -> Self {
504        self.repeat_interval = Some(value);
505        self
506    }
507
508    /// Set the repeat_unit field (default: `RepeatUnit::default()`)
509    pub fn repeat_unit(mut self, value: RepeatUnit) -> Self {
510        self.repeat_unit = Some(value);
511        self
512    }
513
514    /// Set the repeat_type field (default: `RepeatType::default()`)
515    pub fn repeat_type(mut self, value: RepeatType) -> Self {
516        self.repeat_type = Some(value);
517        self
518    }
519
520    /// Set the repeat_until field (optional)
521    pub fn repeat_until(mut self, value: NaiveDate) -> Self {
522        self.repeat_until = Some(value);
523        self
524    }
525
526    /// Set the successor_request_id field (optional)
527    pub fn successor_request_id(mut self, value: Uuid) -> Self {
528        self.successor_request_id = Some(value);
529        self
530    }
531
532    /// Set the successor_of_request_id field (optional)
533    pub fn successor_of_request_id(mut self, value: Uuid) -> Self {
534        self.successor_of_request_id = Some(value);
535        self
536    }
537
538    /// Build the MaintenanceRequest entity
539    ///
540    /// Returns Err if any required field without a default is missing.
541    pub fn build(self) -> Result<MaintenanceRequest, String> {
542        let name = self.name.ok_or_else(|| "name is required".to_string())?;
543        let stage_id = self.stage_id.ok_or_else(|| "stage_id is required".to_string())?;
544        let priority = self.priority.ok_or_else(|| "priority is required".to_string())?;
545
546        Ok(MaintenanceRequest {
547            id: Uuid::new_v4(),
548            name,
549            description: self.description,
550            request_date: self.request_date.unwrap_or_default(),
551            schedule_date: self.schedule_date,
552            schedule_end: self.schedule_end,
553            close_date: self.close_date,
554            duration: self.duration.unwrap_or(Decimal::from(0)),
555            owner_user_id: self.owner_user_id,
556            user_id: self.user_id,
557            asset_id: self.asset_id,
558            stage_id,
559            kanban_state: self.kanban_state.unwrap_or_default(),
560            priority,
561            maintenance_type: self.maintenance_type.unwrap_or_default(),
562            recurring: self.recurring.unwrap_or(false),
563            repeat_interval: self.repeat_interval.unwrap_or(1),
564            repeat_unit: self.repeat_unit.unwrap_or_default(),
565            repeat_type: self.repeat_type.unwrap_or_default(),
566            repeat_until: self.repeat_until,
567            successor_request_id: self.successor_request_id,
568            successor_of_request_id: self.successor_of_request_id,
569            metadata: AuditMetadata::default(),
570        })
571    }
572}