1use 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 PayrollRunStatus {
17 #[default]
19 Draft,
20 Calculated,
22 Approved,
24 Posted,
26 Reversed,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct PayrollRun {
33 pub company_code: String,
35 pub payroll_id: String,
37 pub pay_period_start: NaiveDate,
39 pub pay_period_end: NaiveDate,
41 pub run_date: NaiveDate,
43 pub status: PayrollRunStatus,
45 #[serde(with = "rust_decimal::serde::str")]
47 pub total_gross: Decimal,
48 #[serde(with = "rust_decimal::serde::str")]
50 pub total_deductions: Decimal,
51 #[serde(with = "rust_decimal::serde::str")]
53 pub total_net: Decimal,
54 #[serde(with = "rust_decimal::serde::str")]
56 pub total_employer_cost: Decimal,
57 pub employee_count: u32,
59 pub currency: String,
61 pub posted_by: Option<String>,
63 pub approved_by: Option<String>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct PayrollLineItem {
70 pub payroll_id: String,
72 pub employee_id: String,
74 pub line_id: String,
76 #[serde(with = "rust_decimal::serde::str")]
78 pub gross_pay: Decimal,
79 #[serde(with = "rust_decimal::serde::str")]
81 pub base_salary: Decimal,
82 #[serde(with = "rust_decimal::serde::str")]
84 pub overtime_pay: Decimal,
85 #[serde(with = "rust_decimal::serde::str")]
87 pub bonus: Decimal,
88 #[serde(with = "rust_decimal::serde::str")]
90 pub tax_withholding: Decimal,
91 #[serde(with = "rust_decimal::serde::str")]
93 pub social_security: Decimal,
94 #[serde(with = "rust_decimal::serde::str")]
96 pub health_insurance: Decimal,
97 #[serde(with = "rust_decimal::serde::str")]
99 pub retirement_contribution: Decimal,
100 #[serde(with = "rust_decimal::serde::str")]
102 pub other_deductions: Decimal,
103 #[serde(with = "rust_decimal::serde::str")]
105 pub net_pay: Decimal,
106 pub hours_worked: f64,
108 pub overtime_hours: f64,
110 pub pay_date: NaiveDate,
112 pub cost_center: Option<String>,
114 pub department: Option<String>,
116
117 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub tax_withholding_label: Option<String>,
124 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub social_security_label: Option<String>,
127 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub health_insurance_label: Option<String>,
130 #[serde(default, skip_serializing_if = "Option::is_none")]
132 pub retirement_contribution_label: Option<String>,
133 #[serde(default, skip_serializing_if = "Option::is_none")]
135 pub employer_contribution_label: Option<String>,
136}
137
138impl ToNodeProperties for PayrollRun {
139 fn node_type_name(&self) -> &'static str {
140 "payroll_run"
141 }
142 fn node_type_code(&self) -> u16 {
143 330
144 }
145 fn to_node_properties(&self) -> HashMap<String, GraphPropertyValue> {
146 let mut p = HashMap::new();
147 p.insert(
148 "entityCode".into(),
149 GraphPropertyValue::String(self.company_code.clone()),
150 );
151 p.insert(
152 "payrollId".into(),
153 GraphPropertyValue::String(self.payroll_id.clone()),
154 );
155 p.insert(
156 "periodStart".into(),
157 GraphPropertyValue::Date(self.pay_period_start),
158 );
159 p.insert(
160 "periodEnd".into(),
161 GraphPropertyValue::Date(self.pay_period_end),
162 );
163 p.insert("runDate".into(), GraphPropertyValue::Date(self.run_date));
164 p.insert(
165 "status".into(),
166 GraphPropertyValue::String(format!("{:?}", self.status)),
167 );
168 p.insert(
169 "employeeCount".into(),
170 GraphPropertyValue::Int(self.employee_count as i64),
171 );
172 p.insert(
173 "grossPay".into(),
174 GraphPropertyValue::Decimal(self.total_gross),
175 );
176 p.insert("netPay".into(), GraphPropertyValue::Decimal(self.total_net));
177 p.insert(
178 "taxWithheld".into(),
179 GraphPropertyValue::Decimal(self.total_deductions),
180 );
181 p.insert(
182 "currency".into(),
183 GraphPropertyValue::String(self.currency.clone()),
184 );
185 p.insert(
186 "isApproved".into(),
187 GraphPropertyValue::Bool(matches!(
188 self.status,
189 PayrollRunStatus::Approved | PayrollRunStatus::Posted
190 )),
191 );
192 p
193 }
194}
195
196#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
202#[serde(rename_all = "snake_case")]
203pub enum BenefitPlanType {
204 #[default]
205 Health,
206 Dental,
207 Vision,
208 Retirement401k,
209 StockPurchase,
210 LifeInsurance,
211 Disability,
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
216#[serde(rename_all = "snake_case")]
217pub enum BenefitStatus {
218 #[default]
219 Active,
220 Pending,
221 Terminated,
222 OnLeave,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct BenefitEnrollment {
228 pub id: String,
230 pub entity_code: String,
232 pub employee_id: String,
234 pub employee_name: String,
236 pub plan_type: BenefitPlanType,
238 pub plan_name: String,
240 pub enrollment_date: NaiveDate,
242 pub effective_date: NaiveDate,
244 pub period: String,
246 #[serde(with = "rust_decimal::serde::str")]
248 pub employee_contribution: Decimal,
249 #[serde(with = "rust_decimal::serde::str")]
251 pub employer_contribution: Decimal,
252 pub currency: String,
254 pub status: BenefitStatus,
256 pub is_active: bool,
258}
259
260impl BenefitEnrollment {
261 #[allow(clippy::too_many_arguments)]
263 pub fn new(
264 id: impl Into<String>,
265 entity_code: impl Into<String>,
266 employee_id: impl Into<String>,
267 employee_name: impl Into<String>,
268 plan_type: BenefitPlanType,
269 plan_name: impl Into<String>,
270 enrollment_date: NaiveDate,
271 effective_date: NaiveDate,
272 period: impl Into<String>,
273 employee_contribution: Decimal,
274 employer_contribution: Decimal,
275 currency: impl Into<String>,
276 status: BenefitStatus,
277 is_active: bool,
278 ) -> Self {
279 Self {
280 id: id.into(),
281 entity_code: entity_code.into(),
282 employee_id: employee_id.into(),
283 employee_name: employee_name.into(),
284 plan_type,
285 plan_name: plan_name.into(),
286 enrollment_date,
287 effective_date,
288 period: period.into(),
289 employee_contribution,
290 employer_contribution,
291 currency: currency.into(),
292 status,
293 is_active,
294 }
295 }
296}
297
298impl ToNodeProperties for BenefitEnrollment {
299 fn node_type_name(&self) -> &'static str {
300 "benefit_enrollment"
301 }
302 fn node_type_code(&self) -> u16 {
303 333
304 }
305 fn to_node_properties(&self) -> HashMap<String, GraphPropertyValue> {
306 let mut p = HashMap::new();
307 p.insert(
308 "entityCode".into(),
309 GraphPropertyValue::String(self.entity_code.clone()),
310 );
311 p.insert(
312 "employeeId".into(),
313 GraphPropertyValue::String(self.employee_id.clone()),
314 );
315 p.insert(
316 "employeeName".into(),
317 GraphPropertyValue::String(self.employee_name.clone()),
318 );
319 p.insert(
320 "planType".into(),
321 GraphPropertyValue::String(format!("{:?}", self.plan_type)),
322 );
323 p.insert(
324 "planName".into(),
325 GraphPropertyValue::String(self.plan_name.clone()),
326 );
327 p.insert(
328 "enrollmentDate".into(),
329 GraphPropertyValue::Date(self.enrollment_date),
330 );
331 p.insert(
332 "effectiveDate".into(),
333 GraphPropertyValue::Date(self.effective_date),
334 );
335 p.insert(
336 "period".into(),
337 GraphPropertyValue::String(self.period.clone()),
338 );
339 p.insert(
340 "employeeContribution".into(),
341 GraphPropertyValue::Decimal(self.employee_contribution),
342 );
343 p.insert(
344 "employerContribution".into(),
345 GraphPropertyValue::Decimal(self.employer_contribution),
346 );
347 p.insert(
348 "currency".into(),
349 GraphPropertyValue::String(self.currency.clone()),
350 );
351 p.insert(
352 "status".into(),
353 GraphPropertyValue::String(format!("{:?}", self.status)),
354 );
355 p.insert("isActive".into(), GraphPropertyValue::Bool(self.is_active));
356 p
357 }
358}