datasynth_core/models/
expense_report.rs1use chrono::NaiveDate;
7use rust_decimal::Decimal;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11use super::graph_properties::{GraphPropertyValue, ToNodeProperties};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
15#[serde(rename_all = "snake_case")]
16pub enum ExpenseStatus {
17 #[default]
19 Draft,
20 Submitted,
22 Approved,
24 Rejected,
26 Paid,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
32#[serde(rename_all = "snake_case")]
33pub enum ExpenseCategory {
34 Travel,
36 Meals,
38 Lodging,
40 Transportation,
42 Office,
44 Entertainment,
46 Training,
48 Other,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct ExpenseReport {
55 pub report_id: String,
57 pub employee_id: String,
59 pub submission_date: NaiveDate,
61 pub description: String,
63 pub status: ExpenseStatus,
65 #[serde(with = "rust_decimal::serde::str")]
67 pub total_amount: Decimal,
68 pub currency: String,
70 pub line_items: Vec<ExpenseLineItem>,
72 pub approved_by: Option<String>,
74 pub approved_date: Option<NaiveDate>,
76 pub paid_date: Option<NaiveDate>,
78 pub cost_center: Option<String>,
80 pub department: Option<String>,
82 pub policy_violations: Vec<String>,
84 #[serde(default, skip_serializing_if = "Option::is_none")]
86 pub employee_name: Option<String>,
87}
88
89impl ToNodeProperties for ExpenseReport {
90 fn node_type_name(&self) -> &'static str {
91 "expense_report"
92 }
93 fn node_type_code(&self) -> u16 {
94 332
95 }
96 fn to_node_properties(&self) -> HashMap<String, GraphPropertyValue> {
97 let mut p = HashMap::new();
98 p.insert(
99 "reportId".into(),
100 GraphPropertyValue::String(self.report_id.clone()),
101 );
102 p.insert(
103 "employeeId".into(),
104 GraphPropertyValue::String(self.employee_id.clone()),
105 );
106 if let Some(ref name) = self.employee_name {
107 p.insert(
108 "employeeName".into(),
109 GraphPropertyValue::String(name.clone()),
110 );
111 }
112 p.insert(
113 "submissionDate".into(),
114 GraphPropertyValue::Date(self.submission_date),
115 );
116 p.insert(
117 "totalAmount".into(),
118 GraphPropertyValue::Decimal(self.total_amount),
119 );
120 p.insert(
121 "currency".into(),
122 GraphPropertyValue::String(self.currency.clone()),
123 );
124 p.insert(
125 "lineCount".into(),
126 GraphPropertyValue::Int(self.line_items.len() as i64),
127 );
128 p.insert(
129 "status".into(),
130 GraphPropertyValue::String(format!("{:?}", self.status)),
131 );
132 p.insert(
133 "isApproved".into(),
134 GraphPropertyValue::Bool(matches!(
135 self.status,
136 ExpenseStatus::Approved | ExpenseStatus::Paid
137 )),
138 );
139 if let Some(ref dept) = self.department {
140 p.insert(
141 "department".into(),
142 GraphPropertyValue::String(dept.clone()),
143 );
144 }
145 p
146 }
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct ExpenseLineItem {
152 pub item_id: String,
154 pub category: ExpenseCategory,
156 pub date: NaiveDate,
158 #[serde(with = "rust_decimal::serde::str")]
160 pub amount: Decimal,
161 pub currency: String,
163 pub description: String,
165 pub receipt_attached: bool,
167 pub merchant: Option<String>,
169}