1use chrono::NaiveDate;
8use datasynth_core::models::{
9 CustomerPool, Employee, EmployeePool, EntityId, EntityRegistry, EntityType, FixedAsset,
10 FixedAssetPool, Material, MaterialPool, Vendor, VendorPool,
11};
12
13use super::{
14 AssetGenerator, AssetGeneratorConfig, CustomerGenerator, CustomerGeneratorConfig,
15 EmployeeGenerator, EmployeeGeneratorConfig, MaterialGenerator, MaterialGeneratorConfig,
16 VendorGenerator, VendorGeneratorConfig,
17};
18
19#[derive(Debug, Clone, Default)]
21pub struct EntityRegistryManagerConfig {
22 pub vendor_config: VendorGeneratorConfig,
24 pub customer_config: CustomerGeneratorConfig,
26 pub material_config: MaterialGeneratorConfig,
28 pub asset_config: AssetGeneratorConfig,
30 pub employee_config: EmployeeGeneratorConfig,
32}
33
34#[derive(Debug, Clone)]
36pub struct MasterDataCounts {
37 pub vendors: usize,
39 pub customers: usize,
41 pub materials: usize,
43 pub assets: usize,
45 pub employees: Option<usize>,
47}
48
49impl Default for MasterDataCounts {
50 fn default() -> Self {
51 Self {
52 vendors: 100,
53 customers: 200,
54 materials: 500,
55 assets: 150,
56 employees: None,
57 }
58 }
59}
60
61#[derive(Debug)]
63pub struct GeneratedMasterData {
64 pub vendors: VendorPool,
66 pub customers: CustomerPool,
68 pub materials: MaterialPool,
70 pub assets: FixedAssetPool,
72 pub employees: EmployeePool,
74 pub registry: EntityRegistry,
76}
77
78pub struct EntityRegistryManager {
80 seed: u64,
81 config: EntityRegistryManagerConfig,
82 vendor_generator: VendorGenerator,
83 customer_generator: CustomerGenerator,
84 material_generator: MaterialGenerator,
85 asset_generator: AssetGenerator,
86 employee_generator: EmployeeGenerator,
87 registry: EntityRegistry,
88}
89
90impl EntityRegistryManager {
91 pub fn new(seed: u64) -> Self {
93 Self::with_config(seed, EntityRegistryManagerConfig::default())
94 }
95
96 pub fn with_config(seed: u64, config: EntityRegistryManagerConfig) -> Self {
98 Self {
99 seed,
100 vendor_generator: VendorGenerator::with_config(seed, config.vendor_config.clone()),
101 customer_generator: CustomerGenerator::with_config(
102 seed + 1,
103 config.customer_config.clone(),
104 ),
105 material_generator: MaterialGenerator::with_config(
106 seed + 2,
107 config.material_config.clone(),
108 ),
109 asset_generator: AssetGenerator::with_config(seed + 3, config.asset_config.clone()),
110 employee_generator: EmployeeGenerator::with_config(
111 seed + 4,
112 config.employee_config.clone(),
113 ),
114 registry: EntityRegistry::new(),
115 config,
116 }
117 }
118
119 pub fn generate_master_data(
121 &mut self,
122 company_code: &str,
123 counts: &MasterDataCounts,
124 effective_date: NaiveDate,
125 date_range: (NaiveDate, NaiveDate),
126 ) -> GeneratedMasterData {
127 let vendors = self.generate_vendors(company_code, counts.vendors, effective_date);
129
130 let customers = self.generate_customers(company_code, counts.customers, effective_date);
132
133 let materials = self.generate_materials(company_code, counts.materials, effective_date);
135
136 let assets = self.generate_assets(company_code, counts.assets, date_range);
138
139 let employees = self.generate_employees(company_code, date_range);
141
142 GeneratedMasterData {
143 vendors,
144 customers,
145 materials,
146 assets,
147 employees,
148 registry: self.registry.clone(),
149 }
150 }
151
152 pub fn generate_multi_company_master_data(
154 &mut self,
155 company_codes: &[String],
156 counts: &MasterDataCounts,
157 effective_date: NaiveDate,
158 date_range: (NaiveDate, NaiveDate),
159 ) -> Vec<GeneratedMasterData> {
160 let mut results = Vec::new();
161
162 for company_code in company_codes {
163 let data = self.generate_master_data(company_code, counts, effective_date, date_range);
164 results.push(data);
165 }
166
167 results
168 }
169
170 pub fn generate_master_data_with_ic(
172 &mut self,
173 company_codes: &[String],
174 counts: &MasterDataCounts,
175 effective_date: NaiveDate,
176 date_range: (NaiveDate, NaiveDate),
177 ) -> Vec<GeneratedMasterData> {
178 let mut results = Vec::new();
179
180 for company_code in company_codes {
181 let partners: Vec<String> = company_codes
183 .iter()
184 .filter(|c| *c != company_code)
185 .cloned()
186 .collect();
187
188 let vendors = self.vendor_generator.generate_vendor_pool_with_ic(
190 counts.vendors,
191 company_code,
192 &partners,
193 effective_date,
194 );
195
196 for vendor in &vendors.vendors {
198 self.register_vendor(vendor, company_code, effective_date);
199 }
200
201 let customers = self.customer_generator.generate_customer_pool_with_ic(
203 counts.customers,
204 company_code,
205 &partners,
206 effective_date,
207 );
208
209 for customer in &customers.customers {
211 self.register_customer(customer, company_code, effective_date);
212 }
213
214 let materials = self.generate_materials(company_code, counts.materials, effective_date);
216
217 let assets = self.generate_assets(company_code, counts.assets, date_range);
219
220 let employees = self.generate_employees(company_code, date_range);
222
223 results.push(GeneratedMasterData {
224 vendors,
225 customers,
226 materials,
227 assets,
228 employees,
229 registry: self.registry.clone(),
230 });
231 }
232
233 results
234 }
235
236 fn generate_vendors(
238 &mut self,
239 company_code: &str,
240 count: usize,
241 effective_date: NaiveDate,
242 ) -> VendorPool {
243 let pool = self
244 .vendor_generator
245 .generate_vendor_pool(count, company_code, effective_date);
246
247 for vendor in &pool.vendors {
249 self.register_vendor(vendor, company_code, effective_date);
250 }
251
252 pool
253 }
254
255 fn generate_customers(
257 &mut self,
258 company_code: &str,
259 count: usize,
260 effective_date: NaiveDate,
261 ) -> CustomerPool {
262 let pool =
263 self.customer_generator
264 .generate_customer_pool(count, company_code, effective_date);
265
266 for customer in &pool.customers {
268 self.register_customer(customer, company_code, effective_date);
269 }
270
271 pool
272 }
273
274 fn generate_materials(
276 &mut self,
277 company_code: &str,
278 count: usize,
279 effective_date: NaiveDate,
280 ) -> MaterialPool {
281 let pool = self.material_generator.generate_material_pool_with_bom(
282 count,
283 0.25, company_code,
285 effective_date,
286 );
287
288 for material in &pool.materials {
290 self.register_material(material, company_code, effective_date);
291 }
292
293 pool
294 }
295
296 fn generate_assets(
298 &mut self,
299 company_code: &str,
300 count: usize,
301 date_range: (NaiveDate, NaiveDate),
302 ) -> FixedAssetPool {
303 let pool = self
304 .asset_generator
305 .generate_diverse_pool(count, company_code, date_range);
306
307 for asset in &pool.assets {
309 self.register_asset(asset, company_code, asset.acquisition_date);
310 }
311
312 pool
313 }
314
315 fn generate_employees(
317 &mut self,
318 company_code: &str,
319 date_range: (NaiveDate, NaiveDate),
320 ) -> EmployeePool {
321 let pool = self
322 .employee_generator
323 .generate_company_pool(company_code, date_range);
324
325 for employee in &pool.employees {
327 if let Some(hire_date) = employee.hire_date {
328 self.register_employee(employee, company_code, hire_date);
329 }
330 }
331
332 pool
333 }
334
335 fn register_vendor(&mut self, vendor: &Vendor, company_code: &str, effective_date: NaiveDate) {
337 let entity_id = EntityId::vendor(&vendor.vendor_id);
338 let record =
339 datasynth_core::models::EntityRecord::new(entity_id, &vendor.name, effective_date)
340 .with_company_code(company_code);
341 self.registry.register(record);
342 }
343
344 fn register_customer(
346 &mut self,
347 customer: &datasynth_core::models::Customer,
348 company_code: &str,
349 effective_date: NaiveDate,
350 ) {
351 let entity_id = EntityId::customer(&customer.customer_id);
352 let record =
353 datasynth_core::models::EntityRecord::new(entity_id, &customer.name, effective_date)
354 .with_company_code(company_code);
355 self.registry.register(record);
356 }
357
358 fn register_material(
360 &mut self,
361 material: &Material,
362 company_code: &str,
363 effective_date: NaiveDate,
364 ) {
365 let entity_id = EntityId::material(&material.material_id);
366 let record = datasynth_core::models::EntityRecord::new(
367 entity_id,
368 &material.description,
369 effective_date,
370 )
371 .with_company_code(company_code);
372 self.registry.register(record);
373 }
374
375 fn register_asset(
377 &mut self,
378 asset: &FixedAsset,
379 company_code: &str,
380 effective_date: NaiveDate,
381 ) {
382 let entity_id = EntityId::fixed_asset(&asset.asset_id);
383 let record = datasynth_core::models::EntityRecord::new(
384 entity_id,
385 &asset.description,
386 effective_date,
387 )
388 .with_company_code(company_code);
389 self.registry.register(record);
390 }
391
392 fn register_employee(
394 &mut self,
395 employee: &Employee,
396 company_code: &str,
397 effective_date: NaiveDate,
398 ) {
399 let entity_id = EntityId::employee(&employee.employee_id);
400 let record = datasynth_core::models::EntityRecord::new(
401 entity_id,
402 &employee.display_name,
403 effective_date,
404 )
405 .with_company_code(company_code);
406 self.registry.register(record);
407 }
408
409 pub fn registry(&self) -> &EntityRegistry {
411 &self.registry
412 }
413
414 pub fn validate_entity(
416 &self,
417 entity_type: EntityType,
418 entity_id: &str,
419 transaction_date: NaiveDate,
420 ) -> bool {
421 let id = EntityId {
422 entity_type,
423 id: entity_id.to_string(),
424 };
425 self.registry.is_valid_on(&id, transaction_date)
426 }
427
428 pub fn get_active_entities(&self, entity_type: EntityType, date: NaiveDate) -> Vec<EntityId> {
430 self.registry
431 .get_ids_by_type(entity_type)
432 .into_iter()
433 .filter(|id| self.registry.is_valid_on(id, date))
434 .cloned()
435 .collect()
436 }
437
438 pub fn get_random_vendor(
440 &self,
441 company_code: &str,
442 date: NaiveDate,
443 rng: &mut impl rand::Rng,
444 ) -> Option<String> {
445 let vendors = self
446 .registry
447 .get_by_company(company_code)
448 .into_iter()
449 .filter(|rec| rec.entity_id.entity_type == EntityType::Vendor)
450 .filter(|rec| self.registry.is_valid(&rec.entity_id, date))
451 .collect::<Vec<_>>();
452
453 if vendors.is_empty() {
454 None
455 } else {
456 use rand::seq::SliceRandom;
457 vendors.choose(rng).map(|rec| rec.entity_id.id.clone())
458 }
459 }
460
461 pub fn get_random_customer(
463 &self,
464 company_code: &str,
465 date: NaiveDate,
466 rng: &mut impl rand::Rng,
467 ) -> Option<String> {
468 let customers = self
469 .registry
470 .get_by_company(company_code)
471 .into_iter()
472 .filter(|rec| rec.entity_id.entity_type == EntityType::Customer)
473 .filter(|rec| self.registry.is_valid(&rec.entity_id, date))
474 .collect::<Vec<_>>();
475
476 if customers.is_empty() {
477 None
478 } else {
479 use rand::seq::SliceRandom;
480 customers.choose(rng).map(|rec| rec.entity_id.id.clone())
481 }
482 }
483
484 pub fn reset(&mut self) {
486 self.vendor_generator.reset();
487 self.customer_generator.reset();
488 self.material_generator.reset();
489 self.asset_generator.reset();
490 self.employee_generator.reset();
491 self.registry = EntityRegistry::new();
492 }
493}
494
495#[cfg(test)]
496mod tests {
497 use super::*;
498
499 #[test]
500 fn test_manager_creation() {
501 let manager = EntityRegistryManager::new(42);
502 assert_eq!(manager.registry().total_count(), 0);
503 }
504
505 #[test]
506 fn test_master_data_generation() {
507 let mut manager = EntityRegistryManager::new(42);
508 let counts = MasterDataCounts {
509 vendors: 10,
510 customers: 20,
511 materials: 50,
512 assets: 15,
513 employees: None,
514 };
515
516 let data = manager.generate_master_data(
517 "1000",
518 &counts,
519 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
520 (
521 NaiveDate::from_ymd_opt(2020, 1, 1).unwrap(),
522 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
523 ),
524 );
525
526 assert_eq!(data.vendors.vendors.len(), 10);
527 assert_eq!(data.customers.customers.len(), 20);
528 assert_eq!(data.materials.materials.len(), 50);
529 assert_eq!(data.assets.assets.len(), 15);
530 assert!(!data.employees.employees.is_empty());
531
532 assert!(data.registry.total_count() > 0);
534 }
535
536 #[test]
537 fn test_entity_validation() {
538 let mut manager = EntityRegistryManager::new(42);
539 let counts = MasterDataCounts {
540 vendors: 5,
541 ..Default::default()
542 };
543
544 let data = manager.generate_master_data(
545 "1000",
546 &counts,
547 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
548 (
549 NaiveDate::from_ymd_opt(2020, 1, 1).unwrap(),
550 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
551 ),
552 );
553
554 let vendor_id = &data.vendors.vendors[0].vendor_id;
556 assert!(manager.validate_entity(
557 EntityType::Vendor,
558 vendor_id,
559 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
560 ));
561
562 assert!(!manager.validate_entity(
564 EntityType::Vendor,
565 "V-NONEXISTENT",
566 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
567 ));
568 }
569
570 #[test]
571 fn test_multi_company_generation() {
572 let mut manager = EntityRegistryManager::new(42);
573 let counts = MasterDataCounts {
574 vendors: 5,
575 customers: 10,
576 materials: 20,
577 assets: 5,
578 employees: None,
579 };
580
581 let results = manager.generate_multi_company_master_data(
582 &["1000".to_string(), "2000".to_string()],
583 &counts,
584 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
585 (
586 NaiveDate::from_ymd_opt(2020, 1, 1).unwrap(),
587 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
588 ),
589 );
590
591 assert_eq!(results.len(), 2);
592 }
593
594 #[test]
595 fn test_intercompany_generation() {
596 let mut manager = EntityRegistryManager::new(42);
597 let counts = MasterDataCounts {
598 vendors: 10,
599 customers: 15,
600 materials: 20,
601 assets: 5,
602 employees: None,
603 };
604
605 let results = manager.generate_master_data_with_ic(
606 &["1000".to_string(), "2000".to_string()],
607 &counts,
608 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
609 (
610 NaiveDate::from_ymd_opt(2020, 1, 1).unwrap(),
611 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
612 ),
613 );
614
615 let ic_vendors: Vec<_> = results[0]
617 .vendors
618 .vendors
619 .iter()
620 .filter(|v| v.is_intercompany)
621 .collect();
622 assert!(!ic_vendors.is_empty());
623 }
624
625 #[test]
626 fn test_get_random_vendor() {
627 use rand::SeedableRng;
628 use rand_chacha::ChaCha8Rng;
629
630 let mut manager = EntityRegistryManager::new(42);
631 let counts = MasterDataCounts {
632 vendors: 10,
633 ..Default::default()
634 };
635
636 let _data = manager.generate_master_data(
637 "1000",
638 &counts,
639 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
640 (
641 NaiveDate::from_ymd_opt(2020, 1, 1).unwrap(),
642 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
643 ),
644 );
645
646 let mut rng = ChaCha8Rng::seed_from_u64(42);
647 let vendor = manager.get_random_vendor(
648 "1000",
649 NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
650 &mut rng,
651 );
652
653 assert!(vendor.is_some());
654 }
655}