datasynth_generators/master_data/
cost_center_generator.rs1use datasynth_core::models::{CostCenter, CostCenterCategory};
8use datasynth_core::utils::seeded_rng;
9use rand::prelude::*;
10use rand_chacha::ChaCha8Rng;
11use tracing::debug;
12
13const SEED_DISCRIMINATOR: u64 = 0x4343_434e; struct DeptTemplate {
18 code: &'static str,
19 name: &'static str,
20 category: CostCenterCategory,
21 sub_depts: &'static [(&'static str, &'static str)],
22}
23
24const DEPT_TEMPLATES: &[DeptTemplate] = &[
25 DeptTemplate {
26 code: "FIN",
27 name: "Finance",
28 category: CostCenterCategory::Administration,
29 sub_depts: &[
30 ("AP", "Accounts Payable"),
31 ("AR", "Accounts Receivable"),
32 ("GL", "General Ledger"),
33 ("TAX", "Tax"),
34 ],
35 },
36 DeptTemplate {
37 code: "PROD",
38 name: "Production",
39 category: CostCenterCategory::Production,
40 sub_depts: &[
41 ("ASSY", "Assembly"),
42 ("QC", "Quality Control"),
43 ("MAINT", "Maintenance"),
44 ],
45 },
46 DeptTemplate {
47 code: "SALES",
48 name: "Sales & Marketing",
49 category: CostCenterCategory::Sales,
50 sub_depts: &[
51 ("DOM", "Domestic Sales"),
52 ("INTL", "International Sales"),
53 ("MKT", "Marketing"),
54 ],
55 },
56 DeptTemplate {
57 code: "RD",
58 name: "Research & Development",
59 category: CostCenterCategory::RAndD,
60 sub_depts: &[("RSCH", "Research"), ("DEV", "Development")],
61 },
62 DeptTemplate {
63 code: "CORP",
64 name: "Corporate",
65 category: CostCenterCategory::Corporate,
66 sub_depts: &[
67 ("EXEC", "Executive"),
68 ("HR", "Human Resources"),
69 ("IT", "Information Technology"),
70 ("LEGAL", "Legal"),
71 ],
72 },
73];
74
75pub struct CostCenterGenerator {
77 rng: ChaCha8Rng,
78}
79
80impl CostCenterGenerator {
81 pub fn new(seed: u64) -> Self {
83 Self {
84 rng: seeded_rng(seed, SEED_DISCRIMINATOR),
85 }
86 }
87
88 pub fn generate_for_company(
95 &mut self,
96 company_code: &str,
97 employee_ids: &[String],
98 ) -> Vec<CostCenter> {
99 let mut cost_centers: Vec<CostCenter> = Vec::with_capacity(25);
100
101 for tmpl in DEPT_TEMPLATES {
102 let dept_id = format!("CC-{}-{}", company_code, tmpl.code);
103
104 let mut dept = CostCenter::department(
106 dept_id.clone(),
107 format!("{} — {}", company_code, tmpl.name),
108 company_code,
109 tmpl.category,
110 );
111 dept.responsible_person = self.pick_employee(employee_ids);
112 cost_centers.push(dept);
113
114 for (sub_code, sub_name) in tmpl.sub_depts {
116 let sub_id = format!("CC-{}-{}-{}", company_code, tmpl.code, sub_code);
117 let mut sub = CostCenter::sub_department(
118 sub_id,
119 format!("{} / {}", tmpl.name, sub_name),
120 dept_id.clone(),
121 company_code,
122 tmpl.category,
123 );
124 sub.responsible_person = self.pick_employee(employee_ids);
125 cost_centers.push(sub);
126 }
127 }
128
129 debug!(
130 company_code,
131 count = cost_centers.len(),
132 "Generated cost centers"
133 );
134 cost_centers
135 }
136
137 fn pick_employee(&mut self, employee_ids: &[String]) -> Option<String> {
139 if employee_ids.is_empty() || self.rng.random::<f64>() > 0.20 {
140 return None;
141 }
142 let idx = self.rng.random_range(0..employee_ids.len());
143 Some(employee_ids[idx].clone())
144 }
145}