Skip to main content

backbone_maintenance/domain/entity/
maintenance_schedule.rs

1use chrono::{DateTime, Utc, NaiveDate};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use uuid::Uuid;
5
6use super::MaintenanceScheduleStatus;
7use super::AuditMetadata;
8
9/// Strongly-typed ID for MaintenanceSchedule
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(transparent)]
12pub struct MaintenanceScheduleId(pub Uuid);
13
14impl MaintenanceScheduleId {
15    pub fn new(id: Uuid) -> Self { Self(id) }
16    pub fn generate() -> Self { Self(Uuid::new_v4()) }
17    pub fn into_inner(self) -> Uuid { self.0 }
18}
19
20impl std::fmt::Display for MaintenanceScheduleId {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        write!(f, "{}", self.0)
23    }
24}
25
26impl std::str::FromStr for MaintenanceScheduleId {
27    type Err = uuid::Error;
28    fn from_str(s: &str) -> Result<Self, Self::Err> {
29        Ok(Self(Uuid::parse_str(s)?))
30    }
31}
32
33impl From<Uuid> for MaintenanceScheduleId {
34    fn from(id: Uuid) -> Self { Self(id) }
35}
36
37impl From<MaintenanceScheduleId> for Uuid {
38    fn from(id: MaintenanceScheduleId) -> Self { id.0 }
39}
40
41impl AsRef<Uuid> for MaintenanceScheduleId {
42    fn as_ref(&self) -> &Uuid { &self.0 }
43}
44
45impl std::ops::Deref for MaintenanceScheduleId {
46    type Target = Uuid;
47    fn deref(&self) -> &Self::Target { &self.0 }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
51pub struct MaintenanceSchedule {
52    pub id: Uuid,
53    pub asset_id: Uuid,
54    pub name: String,
55    pub interval_days: i32,
56    pub next_due_date: NaiveDate,
57    pub status: MaintenanceScheduleStatus,
58    #[serde(default)]
59    #[sqlx(json)]
60    pub metadata: AuditMetadata,
61}
62
63impl MaintenanceSchedule {
64    /// Create a builder for MaintenanceSchedule
65    pub fn builder() -> MaintenanceScheduleBuilder {
66        <MaintenanceScheduleBuilder as Default>::default()
67    }
68
69    /// Create a new MaintenanceSchedule with required fields
70    pub fn new(asset_id: Uuid, name: String, interval_days: i32, next_due_date: NaiveDate, status: MaintenanceScheduleStatus) -> Self {
71        Self {
72            id: Uuid::new_v4(),
73            asset_id,
74            name,
75            interval_days,
76            next_due_date,
77            status,
78            metadata: AuditMetadata::default(),
79        }
80    }
81
82    /// Get the entity's unique identifier
83    pub fn id(&self) -> &Uuid {
84        &self.id
85    }
86
87    /// Get a strongly-typed ID for this entity
88    pub fn typed_id(&self) -> MaintenanceScheduleId {
89        MaintenanceScheduleId(self.id)
90    }
91
92    /// Get when this entity was created
93    pub fn created_at(&self) -> Option<&DateTime<Utc>> {
94        self.metadata.created_at.as_ref()
95    }
96
97    /// Get when this entity was last updated
98    pub fn updated_at(&self) -> Option<&DateTime<Utc>> {
99        self.metadata.updated_at.as_ref()
100    }
101
102    /// Check if this entity is soft deleted
103    pub fn is_deleted(&self) -> bool {
104        self.metadata.deleted_at.is_some()
105    }
106
107    /// Check if this entity is active (not deleted)
108    pub fn is_active(&self) -> bool {
109        self.metadata.deleted_at.is_none()
110    }
111
112    /// Get when this entity was deleted
113    pub fn deleted_at(&self) -> Option<&DateTime<Utc>> {
114        self.metadata.deleted_at.as_ref()
115    }
116
117    /// Get who created this entity
118    pub fn created_by(&self) -> Option<&Uuid> {
119        self.metadata.created_by.as_ref()
120    }
121
122    /// Get who last updated this entity
123    pub fn updated_by(&self) -> Option<&Uuid> {
124        self.metadata.updated_by.as_ref()
125    }
126
127    /// Get who deleted this entity
128    pub fn deleted_by(&self) -> Option<&Uuid> {
129        self.metadata.deleted_by.as_ref()
130    }
131
132    /// Get the current status
133    pub fn status(&self) -> &MaintenanceScheduleStatus {
134        &self.status
135    }
136
137
138    // ==========================================================
139    // Partial Update
140    // ==========================================================
141
142    /// Apply partial updates from a map of field name to JSON value
143    pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
144        for (key, value) in fields {
145            match key.as_str() {
146                "asset_id" => {
147                    if let Ok(v) = serde_json::from_value(value) { self.asset_id = v; }
148                }
149                "name" => {
150                    if let Ok(v) = serde_json::from_value(value) { self.name = v; }
151                }
152                "interval_days" => {
153                    if let Ok(v) = serde_json::from_value(value) { self.interval_days = v; }
154                }
155                "next_due_date" => {
156                    if let Ok(v) = serde_json::from_value(value) { self.next_due_date = v; }
157                }
158                "status" => {
159                    if let Ok(v) = serde_json::from_value(value) { self.status = v; }
160                }
161                _ => {} // ignore unknown fields
162            }
163        }
164    }
165
166    // <<< CUSTOM METHODS START >>>
167    // <<< CUSTOM METHODS END >>>
168}
169
170impl super::Entity for MaintenanceSchedule {
171    type Id = Uuid;
172
173    fn entity_id(&self) -> &Self::Id {
174        &self.id
175    }
176
177    fn entity_type() -> &'static str {
178        "MaintenanceSchedule"
179    }
180}
181
182impl backbone_core::PersistentEntity for MaintenanceSchedule {
183    fn entity_id(&self) -> String {
184        self.id.to_string()
185    }
186    fn set_entity_id(&mut self, id: String) {
187        if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
188            self.id = uuid;
189        }
190    }
191    fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
192        self.metadata.created_at
193    }
194    fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
195        self.metadata.created_at = Some(ts);
196    }
197    fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
198        self.metadata.updated_at
199    }
200    fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
201        self.metadata.updated_at = Some(ts);
202    }
203    fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
204        self.metadata.deleted_at
205    }
206    fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
207        self.metadata.deleted_at = ts;
208    }
209}
210
211impl backbone_orm::EntityRepoMeta for MaintenanceSchedule {
212    fn column_types() -> std::collections::HashMap<String, String> {
213        let mut m = std::collections::HashMap::new();
214        m.insert("id".to_string(), "uuid".to_string());
215        m.insert("asset_id".to_string(), "uuid".to_string());
216        m.insert("status".to_string(), "maintenance_schedule_status".to_string());
217        m.insert("next_due_date".to_string(), "date".to_string());
218        m
219    }
220    fn search_fields() -> &'static [&'static str] {
221        &["name"]
222    }
223}
224
225/// Builder for MaintenanceSchedule entity
226///
227/// Provides a fluent API for constructing MaintenanceSchedule instances.
228/// System fields (id, metadata, timestamps) are auto-initialized.
229#[derive(Debug, Clone, Default)]
230pub struct MaintenanceScheduleBuilder {
231    asset_id: Option<Uuid>,
232    name: Option<String>,
233    interval_days: Option<i32>,
234    next_due_date: Option<NaiveDate>,
235    status: Option<MaintenanceScheduleStatus>,
236}
237
238impl MaintenanceScheduleBuilder {
239    /// Set the asset_id field (required)
240    pub fn asset_id(mut self, value: Uuid) -> Self {
241        self.asset_id = Some(value);
242        self
243    }
244
245    /// Set the name field (required)
246    pub fn name(mut self, value: String) -> Self {
247        self.name = Some(value);
248        self
249    }
250
251    /// Set the interval_days field (required)
252    pub fn interval_days(mut self, value: i32) -> Self {
253        self.interval_days = Some(value);
254        self
255    }
256
257    /// Set the next_due_date field (required)
258    pub fn next_due_date(mut self, value: NaiveDate) -> Self {
259        self.next_due_date = Some(value);
260        self
261    }
262
263    /// Set the status field (default: `MaintenanceScheduleStatus::default()`)
264    pub fn status(mut self, value: MaintenanceScheduleStatus) -> Self {
265        self.status = Some(value);
266        self
267    }
268
269    /// Build the MaintenanceSchedule entity
270    ///
271    /// Returns Err if any required field without a default is missing.
272    pub fn build(self) -> Result<MaintenanceSchedule, String> {
273        let asset_id = self.asset_id.ok_or_else(|| "asset_id is required".to_string())?;
274        let name = self.name.ok_or_else(|| "name is required".to_string())?;
275        let interval_days = self.interval_days.ok_or_else(|| "interval_days is required".to_string())?;
276        let next_due_date = self.next_due_date.ok_or_else(|| "next_due_date is required".to_string())?;
277
278        Ok(MaintenanceSchedule {
279            id: Uuid::new_v4(),
280            asset_id,
281            name,
282            interval_days,
283            next_due_date,
284            status: self.status.unwrap_or_default(),
285            metadata: AuditMetadata::default(),
286        })
287    }
288}