datasynth_core/models/payroll.rs
1//! Payroll models for the Hire-to-Retire (H2R) process.
2//!
3//! These models represent payroll runs and individual employee pay line items,
4//! supporting the full payroll cycle from draft calculation through posting.
5
6use chrono::NaiveDate;
7use rust_decimal::Decimal;
8use serde::{Deserialize, Serialize};
9
10/// Status of a payroll run through the processing lifecycle.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
12#[serde(rename_all = "snake_case")]
13pub enum PayrollRunStatus {
14 /// Initial draft state before calculation
15 #[default]
16 Draft,
17 /// Payroll has been calculated but not yet approved
18 Calculated,
19 /// Payroll approved for posting
20 Approved,
21 /// Payroll posted to GL
22 Posted,
23 /// Payroll run has been reversed
24 Reversed,
25}
26
27/// A payroll run representing a complete pay cycle for a company.
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct PayrollRun {
30 /// Company code
31 pub company_code: String,
32 /// Unique payroll run identifier
33 pub payroll_id: String,
34 /// Start of the pay period
35 pub pay_period_start: NaiveDate,
36 /// End of the pay period
37 pub pay_period_end: NaiveDate,
38 /// Date the payroll was run/processed
39 pub run_date: NaiveDate,
40 /// Current status of the payroll run
41 pub status: PayrollRunStatus,
42 /// Total gross pay across all employees
43 #[serde(with = "rust_decimal::serde::str")]
44 pub total_gross: Decimal,
45 /// Total deductions across all employees
46 #[serde(with = "rust_decimal::serde::str")]
47 pub total_deductions: Decimal,
48 /// Total net pay across all employees
49 #[serde(with = "rust_decimal::serde::str")]
50 pub total_net: Decimal,
51 /// Total employer cost (gross + employer-side taxes/benefits)
52 #[serde(with = "rust_decimal::serde::str")]
53 pub total_employer_cost: Decimal,
54 /// Number of employees included in this run
55 pub employee_count: u32,
56 /// Currency code (e.g., USD, EUR)
57 pub currency: String,
58 /// User who posted the payroll
59 pub posted_by: Option<String>,
60 /// User who approved the payroll
61 pub approved_by: Option<String>,
62}
63
64/// An individual employee's payroll line item within a payroll run.
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct PayrollLineItem {
67 /// Reference to the parent payroll run
68 pub payroll_id: String,
69 /// Employee identifier
70 pub employee_id: String,
71 /// Unique line item identifier
72 pub line_id: String,
73 /// Total gross pay for this employee
74 #[serde(with = "rust_decimal::serde::str")]
75 pub gross_pay: Decimal,
76 /// Base salary component
77 #[serde(with = "rust_decimal::serde::str")]
78 pub base_salary: Decimal,
79 /// Overtime pay component
80 #[serde(with = "rust_decimal::serde::str")]
81 pub overtime_pay: Decimal,
82 /// Bonus component
83 #[serde(with = "rust_decimal::serde::str")]
84 pub bonus: Decimal,
85 /// Federal/state tax withholding
86 #[serde(with = "rust_decimal::serde::str")]
87 pub tax_withholding: Decimal,
88 /// Social security / FICA deduction
89 #[serde(with = "rust_decimal::serde::str")]
90 pub social_security: Decimal,
91 /// Health insurance deduction
92 #[serde(with = "rust_decimal::serde::str")]
93 pub health_insurance: Decimal,
94 /// Retirement plan contribution (employee side)
95 #[serde(with = "rust_decimal::serde::str")]
96 pub retirement_contribution: Decimal,
97 /// Other deductions (garnishments, voluntary deductions, etc.)
98 #[serde(with = "rust_decimal::serde::str")]
99 pub other_deductions: Decimal,
100 /// Net pay after all deductions
101 #[serde(with = "rust_decimal::serde::str")]
102 pub net_pay: Decimal,
103 /// Regular hours worked in the period
104 pub hours_worked: f64,
105 /// Overtime hours worked in the period
106 pub overtime_hours: f64,
107 /// Date payment is issued
108 pub pay_date: NaiveDate,
109 /// Cost center allocation
110 pub cost_center: Option<String>,
111 /// Department allocation
112 pub department: Option<String>,
113
114 // -- Country-pack deduction labels ----------------------------------------
115 // When a country pack is available these carry the localized deduction names
116 // (e.g. "Lohnsteuer" instead of "Federal Income Tax"). When no pack is set
117 // the fields are `None` and the implicit US-centric names apply.
118 /// Localized label for the tax withholding deduction.
119 #[serde(default, skip_serializing_if = "Option::is_none")]
120 pub tax_withholding_label: Option<String>,
121 /// Localized label for the social security / FICA deduction.
122 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub social_security_label: Option<String>,
124 /// Localized label for the health insurance deduction.
125 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub health_insurance_label: Option<String>,
127 /// Localized label for the retirement / pension contribution.
128 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub retirement_contribution_label: Option<String>,
130 /// Localized label(s) for employer contributions (semicolon-separated).
131 #[serde(default, skip_serializing_if = "Option::is_none")]
132 pub employer_contribution_label: Option<String>,
133}