1use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use uuid::Uuid;
10
11use super::ObjectAttributeValue;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct OcpmEvent {
16 pub event_id: Uuid,
18 pub activity_id: String,
20 pub activity_name: String,
22 pub timestamp: DateTime<Utc>,
24 pub lifecycle: EventLifecycle,
26 pub resource_id: String,
28 pub resource_name: Option<String>,
30 pub company_code: String,
32 pub object_refs: Vec<EventObjectRef>,
34 pub attributes: HashMap<String, ObjectAttributeValue>,
36 pub document_ref: Option<String>,
38 pub journal_entry_id: Option<Uuid>,
40 pub is_anomaly: bool,
42 pub anomaly_type: Option<String>,
44 pub case_id: Option<Uuid>,
46}
47
48impl OcpmEvent {
49 pub fn new(
51 activity_id: &str,
52 activity_name: &str,
53 timestamp: DateTime<Utc>,
54 resource_id: &str,
55 company_code: &str,
56 ) -> Self {
57 Self {
58 event_id: Uuid::new_v4(),
59 activity_id: activity_id.into(),
60 activity_name: activity_name.into(),
61 timestamp,
62 lifecycle: EventLifecycle::Complete,
63 resource_id: resource_id.into(),
64 resource_name: None,
65 company_code: company_code.into(),
66 object_refs: Vec::new(),
67 attributes: HashMap::new(),
68 document_ref: None,
69 journal_entry_id: None,
70 is_anomaly: false,
71 anomaly_type: None,
72 case_id: None,
73 }
74 }
75
76 pub fn with_id(mut self, id: Uuid) -> Self {
78 self.event_id = id;
79 self
80 }
81
82 pub fn with_lifecycle(mut self, lifecycle: EventLifecycle) -> Self {
84 self.lifecycle = lifecycle;
85 self
86 }
87
88 pub fn with_resource_name(mut self, name: &str) -> Self {
90 self.resource_name = Some(name.into());
91 self
92 }
93
94 pub fn with_object(mut self, object_ref: EventObjectRef) -> Self {
96 self.object_refs.push(object_ref);
97 self
98 }
99
100 pub fn with_objects(mut self, refs: Vec<EventObjectRef>) -> Self {
102 self.object_refs.extend(refs);
103 self
104 }
105
106 pub fn with_attribute(mut self, key: &str, value: ObjectAttributeValue) -> Self {
108 self.attributes.insert(key.into(), value);
109 self
110 }
111
112 pub fn with_document_ref(mut self, doc_ref: &str) -> Self {
114 self.document_ref = Some(doc_ref.into());
115 self
116 }
117
118 pub fn with_journal_entry(mut self, je_id: Uuid) -> Self {
120 self.journal_entry_id = Some(je_id);
121 self
122 }
123
124 pub fn with_case(mut self, case_id: Uuid) -> Self {
126 self.case_id = Some(case_id);
127 self
128 }
129
130 pub fn mark_anomaly(&mut self, anomaly_type: &str) {
132 self.is_anomaly = true;
133 self.anomaly_type = Some(anomaly_type.into());
134 }
135
136 pub fn object_ids(&self) -> Vec<Uuid> {
138 self.object_refs.iter().map(|r| r.object_id).collect()
139 }
140
141 pub fn objects_of_type(&self, type_id: &str) -> Vec<&EventObjectRef> {
143 self.object_refs
144 .iter()
145 .filter(|r| r.object_type_id == type_id)
146 .collect()
147 }
148
149 pub fn creates_objects(&self) -> bool {
151 self.object_refs
152 .iter()
153 .any(|r| r.qualifier == ObjectQualifier::Created)
154 }
155
156 pub fn completes_objects(&self) -> bool {
158 self.object_refs
159 .iter()
160 .any(|r| r.qualifier == ObjectQualifier::Consumed)
161 }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
166#[serde(rename_all = "snake_case")]
167pub enum EventLifecycle {
168 Start,
170 #[default]
172 Complete,
173 Abort,
175 Suspend,
177 Resume,
179 Atomic,
181}
182
183impl EventLifecycle {
184 pub fn is_completion(&self) -> bool {
186 matches!(self, Self::Complete | Self::Abort)
187 }
188
189 pub fn is_start(&self) -> bool {
191 matches!(self, Self::Start)
192 }
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
197pub struct EventObjectRef {
198 pub object_id: Uuid,
200 pub object_type_id: String,
202 pub external_id: Option<String>,
204 pub qualifier: ObjectQualifier,
206}
207
208impl EventObjectRef {
209 pub fn new(object_id: Uuid, object_type_id: &str, qualifier: ObjectQualifier) -> Self {
211 Self {
212 object_id,
213 object_type_id: object_type_id.into(),
214 external_id: None,
215 qualifier,
216 }
217 }
218
219 pub fn with_external_id(mut self, external_id: &str) -> Self {
221 self.external_id = Some(external_id.into());
222 self
223 }
224
225 pub fn created(object_id: Uuid, object_type_id: &str) -> Self {
227 Self::new(object_id, object_type_id, ObjectQualifier::Created)
228 }
229
230 pub fn updated(object_id: Uuid, object_type_id: &str) -> Self {
232 Self::new(object_id, object_type_id, ObjectQualifier::Updated)
233 }
234
235 pub fn read(object_id: Uuid, object_type_id: &str) -> Self {
237 Self::new(object_id, object_type_id, ObjectQualifier::Read)
238 }
239
240 pub fn consumed(object_id: Uuid, object_type_id: &str) -> Self {
242 Self::new(object_id, object_type_id, ObjectQualifier::Consumed)
243 }
244
245 pub fn context(object_id: Uuid, object_type_id: &str) -> Self {
247 Self::new(object_id, object_type_id, ObjectQualifier::Context)
248 }
249}
250
251#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
253#[serde(rename_all = "snake_case")]
254pub enum ObjectQualifier {
255 Created,
257 #[default]
259 Updated,
260 Read,
262 Consumed,
264 Context,
266}
267
268impl ObjectQualifier {
269 pub fn changes_object(&self) -> bool {
271 matches!(self, Self::Created | Self::Updated | Self::Consumed)
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 #[test]
280 fn test_event_creation() {
281 let event = OcpmEvent::new(
282 "create_po",
283 "Create Purchase Order",
284 Utc::now(),
285 "user001",
286 "1000",
287 );
288
289 assert_eq!(event.activity_id, "create_po");
290 assert_eq!(event.lifecycle, EventLifecycle::Complete);
291 assert!(!event.is_anomaly);
292 }
293
294 #[test]
295 fn test_event_with_objects() {
296 let po_id = Uuid::new_v4();
297 let vendor_id = Uuid::new_v4();
298
299 let event = OcpmEvent::new("create_po", "Create PO", Utc::now(), "user001", "1000")
300 .with_object(EventObjectRef::created(po_id, "purchase_order"))
301 .with_object(EventObjectRef::read(vendor_id, "vendor"));
302
303 assert_eq!(event.object_refs.len(), 2);
304 assert!(event.creates_objects());
305 }
306
307 #[test]
308 fn test_object_qualifier() {
309 assert!(ObjectQualifier::Created.changes_object());
310 assert!(ObjectQualifier::Updated.changes_object());
311 assert!(!ObjectQualifier::Read.changes_object());
312 assert!(!ObjectQualifier::Context.changes_object());
313 }
314}