Skip to main content

datasynth_config/
schema.rs

1//! Configuration schema for synthetic data generation.
2
3use datasynth_core::distributions::{
4    AmountDistributionConfig, DebitCreditDistributionConfig, EvenOddDistributionConfig,
5    LineItemDistributionConfig, SeasonalityConfig,
6};
7use datasynth_core::models::{CoAComplexity, IndustrySector};
8use serde::{Deserialize, Serialize};
9use std::path::PathBuf;
10
11/// Root configuration for the synthetic data generator.
12///
13/// # camelCase alias policy
14///
15/// Every multi-word field carries `#[serde(alias = "camelCaseName")]`
16/// so SDK clients that follow JSON conventions can submit configs
17/// without round-tripping through a snake_case transformer.
18///
19/// Before v4.4.1 several fields — `documentFlows`, `accountingStandards`,
20/// `complianceRegulations`, `analyticsMetadata` — had no alias, so SDK
21/// submissions silently fell through to defaults. The symptom was
22/// "enabling the 6 feature subsections together collapses the archive
23/// from 99 files to 19". Root cause: those four fields never parsed;
24/// the orchestrator produced far less data than requested, and
25/// `output.exportFormat` similarly fell through so journal_entries
26/// landed as the default Parquet/CSV rather than JSON.
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct GeneratorConfig {
29    /// Global settings
30    pub global: GlobalConfig,
31    /// Company configuration
32    pub companies: Vec<CompanyConfig>,
33    /// Chart of Accounts configuration
34    #[serde(alias = "chartOfAccounts")]
35    pub chart_of_accounts: ChartOfAccountsConfig,
36    /// Transaction generation settings
37    #[serde(default)]
38    pub transactions: TransactionConfig,
39    /// Output configuration
40    pub output: OutputConfig,
41    /// Fraud simulation settings
42    #[serde(default)]
43    pub fraud: FraudConfig,
44    /// Data quality variation settings
45    #[serde(default, alias = "dataQuality")]
46    pub data_quality: DataQualitySchemaConfig,
47    /// Internal Controls System settings
48    #[serde(default, alias = "internalControls")]
49    pub internal_controls: InternalControlsConfig,
50    /// Business process mix
51    #[serde(default, alias = "businessProcesses")]
52    pub business_processes: BusinessProcessConfig,
53    /// User persona distribution
54    #[serde(default, alias = "userPersonas")]
55    pub user_personas: UserPersonaConfig,
56    /// Template configuration for realistic data
57    #[serde(default)]
58    pub templates: TemplateConfig,
59    /// Approval workflow configuration
60    #[serde(default)]
61    pub approval: ApprovalConfig,
62    /// Department structure configuration
63    #[serde(default)]
64    pub departments: DepartmentConfig,
65    /// Master data generation settings
66    #[serde(default, alias = "masterData")]
67    pub master_data: MasterDataConfig,
68    /// Document flow generation settings
69    #[serde(default, alias = "documentFlows")]
70    pub document_flows: DocumentFlowConfig,
71    /// Intercompany transaction settings
72    #[serde(default)]
73    pub intercompany: IntercompanyConfig,
74    /// Balance and trial balance settings
75    #[serde(default)]
76    pub balance: BalanceConfig,
77    /// OCPM (Object-Centric Process Mining) settings
78    #[serde(default)]
79    pub ocpm: OcpmConfig,
80    /// Audit engagement and workpaper generation settings
81    #[serde(default)]
82    pub audit: AuditGenerationConfig,
83    /// Banking KYC/AML transaction generation settings
84    #[serde(default)]
85    pub banking: datasynth_banking::BankingConfig,
86    /// Scenario configuration for metadata and tagging (Phase 1.3)
87    #[serde(default)]
88    pub scenario: ScenarioConfig,
89    /// Temporal drift configuration for simulating distribution changes over time (Phase 2.2)
90    #[serde(default)]
91    pub temporal: TemporalDriftConfig,
92    /// Graph export configuration for accounting network export
93    #[serde(default, alias = "graphExport")]
94    pub graph_export: GraphExportConfig,
95    /// Streaming output API configuration
96    #[serde(default)]
97    pub streaming: StreamingSchemaConfig,
98    /// Rate limiting configuration
99    #[serde(default, alias = "rateLimit")]
100    pub rate_limit: RateLimitSchemaConfig,
101    /// Temporal attribute generation configuration
102    #[serde(default, alias = "temporalAttributes")]
103    pub temporal_attributes: TemporalAttributeSchemaConfig,
104    /// Relationship generation configuration
105    #[serde(default)]
106    pub relationships: RelationshipSchemaConfig,
107    /// Accounting standards framework configuration (IFRS, US GAAP)
108    #[serde(default, alias = "accountingStandards")]
109    pub accounting_standards: AccountingStandardsConfig,
110    /// Audit standards framework configuration (ISA, PCAOB)
111    #[serde(default, alias = "auditStandards")]
112    pub audit_standards: AuditStandardsConfig,
113    /// Advanced distribution configuration (mixture models, correlations, regime changes)
114    #[serde(default)]
115    pub distributions: AdvancedDistributionConfig,
116    /// Temporal patterns configuration (business days, period-end dynamics, processing lags)
117    #[serde(default, alias = "temporalPatterns")]
118    pub temporal_patterns: TemporalPatternsConfig,
119    /// Vendor network configuration (multi-tier supply chain modeling)
120    #[serde(default, alias = "vendorNetwork")]
121    pub vendor_network: VendorNetworkSchemaConfig,
122    /// Customer segmentation configuration (value segments, lifecycle stages)
123    #[serde(default, alias = "customerSegmentation")]
124    pub customer_segmentation: CustomerSegmentationSchemaConfig,
125    /// Relationship strength calculation configuration
126    #[serde(default, alias = "relationshipStrength")]
127    pub relationship_strength: RelationshipStrengthSchemaConfig,
128    /// Cross-process link configuration (P2P ↔ O2C via inventory)
129    #[serde(default, alias = "crossProcessLinks")]
130    pub cross_process_links: CrossProcessLinksSchemaConfig,
131    /// Organizational events configuration (acquisitions, divestitures, etc.)
132    #[serde(default, alias = "organizationalEvents")]
133    pub organizational_events: OrganizationalEventsSchemaConfig,
134    /// Behavioral drift configuration (vendor, customer, employee behavior)
135    #[serde(default, alias = "behavioralDrift")]
136    pub behavioral_drift: BehavioralDriftSchemaConfig,
137    /// Market drift configuration (economic cycles, commodities, price shocks)
138    #[serde(default, alias = "marketDrift")]
139    pub market_drift: MarketDriftSchemaConfig,
140    /// Drift labeling configuration for ground truth generation
141    #[serde(default, alias = "driftLabeling")]
142    pub drift_labeling: DriftLabelingSchemaConfig,
143    /// Enhanced anomaly injection configuration (multi-stage schemes, correlated injection, near-miss)
144    #[serde(default, alias = "anomalyInjection")]
145    pub anomaly_injection: EnhancedAnomalyConfig,
146    /// Industry-specific transaction and anomaly generation configuration
147    #[serde(default, alias = "industrySpecific")]
148    pub industry_specific: IndustrySpecificConfig,
149    /// Fingerprint privacy configuration for extraction/synthesis
150    #[serde(default, alias = "fingerprintPrivacy")]
151    pub fingerprint_privacy: FingerprintPrivacyConfig,
152    /// Quality gate configuration for pass/fail thresholds
153    #[serde(default, alias = "qualityGates")]
154    pub quality_gates: QualityGatesSchemaConfig,
155    /// Compliance configuration (EU AI Act, content marking)
156    #[serde(default)]
157    pub compliance: ComplianceSchemaConfig,
158    /// Webhook notification configuration
159    #[serde(default)]
160    pub webhooks: WebhookSchemaConfig,
161    /// LLM enrichment configuration (AI-augmented vendor names, descriptions, explanations)
162    #[serde(default)]
163    pub llm: LlmSchemaConfig,
164    /// Diffusion model configuration (statistical diffusion-based data enhancement)
165    #[serde(default)]
166    pub diffusion: DiffusionSchemaConfig,
167    /// Causal generation configuration (structural causal models, interventions)
168    #[serde(default)]
169    pub causal: CausalSchemaConfig,
170
171    // ===== Enterprise Process Chain Extensions =====
172    /// Source-to-Pay (S2C/S2P) configuration (sourcing, contracts, catalogs, scorecards)
173    #[serde(default, alias = "sourceToPay")]
174    pub source_to_pay: SourceToPayConfig,
175    /// Financial reporting configuration (financial statements, KPIs, budgets)
176    #[serde(default, alias = "financialReporting")]
177    pub financial_reporting: FinancialReportingConfig,
178    /// HR process configuration (payroll, time & attendance, expenses)
179    #[serde(default)]
180    pub hr: HrConfig,
181    /// Manufacturing configuration (production orders, WIP, routing)
182    #[serde(default)]
183    pub manufacturing: ManufacturingProcessConfig,
184    /// Sales quote configuration (quote-to-order pipeline)
185    #[serde(default, alias = "salesQuotes")]
186    pub sales_quotes: SalesQuoteConfig,
187    /// Tax accounting configuration (VAT/GST, sales tax, withholding, provisions, payroll tax)
188    #[serde(default)]
189    pub tax: TaxConfig,
190    /// Treasury and cash management configuration
191    #[serde(default)]
192    pub treasury: TreasuryConfig,
193    /// Project accounting configuration
194    #[serde(default, alias = "projectAccounting")]
195    pub project_accounting: ProjectAccountingConfig,
196    /// ESG / Sustainability reporting configuration
197    #[serde(default)]
198    pub esg: EsgConfig,
199    /// Country pack configuration (external packs directory, per-country overrides)
200    #[serde(default, alias = "countryPacks")]
201    pub country_packs: Option<CountryPacksSchemaConfig>,
202    /// Counterfactual simulation scenario configuration
203    #[serde(default)]
204    pub scenarios: ScenariosConfig,
205    /// Generation session configuration (period-by-period generation with balance carry-forward)
206    #[serde(default)]
207    pub session: SessionSchemaConfig,
208    /// Compliance regulations framework configuration (standards registry, jurisdictions, temporal versioning, audit templates, graph integration)
209    #[serde(default, alias = "complianceRegulations")]
210    pub compliance_regulations: ComplianceRegulationsConfig,
211    /// v3.3.0: analytics metadata phase — prior-year comparatives,
212    /// industry benchmarks, management reports, drift events. Off by
213    /// default so v3.2.1 archives are byte-identical.
214    #[serde(default, alias = "analyticsMetadata")]
215    pub analytics_metadata: AnalyticsMetadataConfig,
216}
217
218/// v3.3.0: analytics-metadata phase configuration.
219///
220/// Gates the `phase_analytics_metadata` pass that runs AFTER all
221/// JE-adding phases (including the fraud-bias sweep at Phase 20b).
222/// When enabled, the orchestrator calls `PriorYearGenerator`,
223/// `IndustryBenchmarkGenerator`, `ManagementReportGenerator`, and
224/// `DriftEventGenerator` in sequence; each sub-flag below controls
225/// whether that specific generator fires.
226#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct AnalyticsMetadataConfig {
228    /// Master switch for the whole analytics phase.
229    #[serde(default)]
230    pub enabled: bool,
231    /// Emit `PriorYearComparative` records derived from current
232    /// period's account balances.
233    #[serde(default = "default_true")]
234    pub prior_year: bool,
235    /// Emit `IndustryBenchmark` records for the configured industry.
236    #[serde(default = "default_true")]
237    pub industry_benchmark: bool,
238    /// Emit management-report artefacts.
239    #[serde(default = "default_true")]
240    pub management_reports: bool,
241    /// Emit `LabeledDriftEvent` records — post-generation sweep over
242    /// journal entries to label detected drift patterns.
243    #[serde(default = "default_true")]
244    pub drift_events: bool,
245}
246
247impl Default for AnalyticsMetadataConfig {
248    fn default() -> Self {
249        Self {
250            enabled: false,
251            prior_year: true,
252            industry_benchmark: true,
253            management_reports: true,
254            drift_events: true,
255        }
256    }
257}
258
259/// LLM enrichment configuration.
260///
261/// Controls AI-augmented metadata enrichment using LLM providers.
262/// When enabled, vendor names, transaction descriptions, and anomaly explanations
263/// are enriched using the configured provider (mock by default).
264#[derive(Debug, Clone, Serialize, Deserialize)]
265pub struct LlmSchemaConfig {
266    /// Whether LLM enrichment is enabled.
267    #[serde(default)]
268    pub enabled: bool,
269    /// Provider type: "mock", "openai", "anthropic", "custom".
270    #[serde(default = "default_llm_provider")]
271    pub provider: String,
272    /// Model name/ID for the provider.
273    #[serde(default = "default_llm_model_name")]
274    pub model: String,
275    /// Maximum number of vendor names to enrich per run.
276    #[serde(default = "default_llm_batch_size")]
277    pub max_vendor_enrichments: usize,
278
279    /// v4.1.1+: also enrich customer names at generate time.
280    /// Default `false` preserves v4.1.0 behaviour.
281    #[serde(default)]
282    pub enrich_customers: bool,
283
284    /// v4.1.1+: also enrich material descriptions at generate time.
285    /// Default `false`.
286    #[serde(default)]
287    pub enrich_materials: bool,
288
289    /// v4.1.1+: also enrich audit finding titles at generate time
290    /// (the finding narratives remain on their existing template path
291    /// because they're richer and locale-specific). Default `false`.
292    #[serde(default)]
293    pub enrich_findings: bool,
294
295    /// v4.1.1+: upper bound on customer enrichments per run. Matches
296    /// `max_vendor_enrichments` semantics.
297    #[serde(default = "default_llm_batch_size")]
298    pub max_customer_enrichments: usize,
299
300    /// v4.1.1+: upper bound on material enrichments per run.
301    #[serde(default = "default_llm_batch_size")]
302    pub max_material_enrichments: usize,
303
304    /// v4.1.1+: upper bound on finding enrichments per run.
305    #[serde(default = "default_llm_batch_size")]
306    pub max_finding_enrichments: usize,
307}
308
309fn default_llm_provider() -> String {
310    "mock".to_string()
311}
312
313fn default_llm_model_name() -> String {
314    "gpt-4o-mini".to_string()
315}
316
317fn default_llm_batch_size() -> usize {
318    50
319}
320
321impl Default for LlmSchemaConfig {
322    fn default() -> Self {
323        Self {
324            enabled: false,
325            provider: default_llm_provider(),
326            model: default_llm_model_name(),
327            max_vendor_enrichments: default_llm_batch_size(),
328            enrich_customers: false,
329            enrich_materials: false,
330            enrich_findings: false,
331            max_customer_enrichments: default_llm_batch_size(),
332            max_material_enrichments: default_llm_batch_size(),
333            max_finding_enrichments: default_llm_batch_size(),
334        }
335    }
336}
337
338/// Diffusion model configuration.
339///
340/// Controls statistical diffusion-based data enhancement that generates samples
341/// matching target distribution properties (means, standard deviations, correlations).
342#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct DiffusionSchemaConfig {
344    /// Whether diffusion enhancement is enabled.
345    #[serde(default)]
346    pub enabled: bool,
347    /// Number of diffusion steps (higher = better quality, slower).
348    #[serde(default = "default_diffusion_steps")]
349    pub n_steps: usize,
350    /// Noise schedule type: "linear", "cosine", "sigmoid".
351    #[serde(default = "default_diffusion_schedule")]
352    pub schedule: String,
353    /// Number of sample rows to generate for demonstration.
354    #[serde(default = "default_diffusion_sample_size")]
355    pub sample_size: usize,
356    /// Backend type: "statistical" (default), "neural", "hybrid".
357    #[serde(default = "default_diffusion_backend")]
358    pub backend: String,
359    /// Neural diffusion backend configuration (used when backend is "neural" or "hybrid").
360    #[serde(default)]
361    pub neural: NeuralDiffusionSchemaConfig,
362}
363
364fn default_diffusion_steps() -> usize {
365    100
366}
367
368fn default_diffusion_schedule() -> String {
369    "linear".to_string()
370}
371
372fn default_diffusion_sample_size() -> usize {
373    100
374}
375
376fn default_diffusion_backend() -> String {
377    "statistical".to_string()
378}
379
380impl Default for DiffusionSchemaConfig {
381    fn default() -> Self {
382        Self {
383            enabled: false,
384            n_steps: default_diffusion_steps(),
385            schedule: default_diffusion_schedule(),
386            sample_size: default_diffusion_sample_size(),
387            backend: default_diffusion_backend(),
388            neural: NeuralDiffusionSchemaConfig::default(),
389        }
390    }
391}
392
393/// Neural diffusion backend configuration.
394///
395/// Controls the `candle`-based neural score network that learns joint distributions
396/// from training data for the neural and hybrid diffusion backends.
397#[derive(Debug, Clone, Serialize, Deserialize)]
398pub struct NeuralDiffusionSchemaConfig {
399    /// Hidden layer dimensions for the score network MLP.
400    #[serde(default = "default_neural_hidden_dims")]
401    pub hidden_dims: Vec<usize>,
402    /// Dimensionality of the timestep embedding.
403    #[serde(default = "default_neural_timestep_embed_dim")]
404    pub timestep_embed_dim: usize,
405    /// Learning rate for training.
406    #[serde(default = "default_neural_learning_rate")]
407    pub learning_rate: f64,
408    /// Number of training epochs.
409    #[serde(default = "default_neural_training_epochs")]
410    pub training_epochs: usize,
411    /// Training batch size.
412    #[serde(default = "default_neural_batch_size")]
413    pub batch_size: usize,
414    /// Blend weight for hybrid mode (0.0 = all statistical, 1.0 = all neural).
415    #[serde(default = "default_neural_hybrid_weight")]
416    pub hybrid_weight: f64,
417    /// Hybrid blending strategy: "weighted_average", "column_select", "threshold".
418    #[serde(default = "default_neural_hybrid_strategy")]
419    pub hybrid_strategy: String,
420    /// Columns to apply neural generation to (empty = all numeric columns).
421    #[serde(default)]
422    pub neural_columns: Vec<String>,
423    /// v4.4.0+ Optional path to a pre-trained score-network checkpoint
424    /// (`.safetensors`). When set, the orchestrator loads the
425    /// checkpoint instead of training from the first batch — useful
426    /// for long-running production deployments where training cost
427    /// dominates per-run cost. When empty, the orchestrator trains
428    /// on the first generated JE amounts.
429    #[serde(default, skip_serializing_if = "Option::is_none")]
430    pub checkpoint_path: Option<String>,
431}
432
433fn default_neural_hidden_dims() -> Vec<usize> {
434    vec![256, 256, 128]
435}
436
437fn default_neural_timestep_embed_dim() -> usize {
438    64
439}
440
441fn default_neural_learning_rate() -> f64 {
442    0.001
443}
444
445fn default_neural_training_epochs() -> usize {
446    100
447}
448
449fn default_neural_batch_size() -> usize {
450    64
451}
452
453fn default_neural_hybrid_weight() -> f64 {
454    0.5
455}
456
457fn default_neural_hybrid_strategy() -> String {
458    "weighted_average".to_string()
459}
460
461impl Default for NeuralDiffusionSchemaConfig {
462    fn default() -> Self {
463        Self {
464            hidden_dims: default_neural_hidden_dims(),
465            timestep_embed_dim: default_neural_timestep_embed_dim(),
466            learning_rate: default_neural_learning_rate(),
467            training_epochs: default_neural_training_epochs(),
468            batch_size: default_neural_batch_size(),
469            hybrid_weight: default_neural_hybrid_weight(),
470            hybrid_strategy: default_neural_hybrid_strategy(),
471            neural_columns: Vec::new(),
472            checkpoint_path: None,
473        }
474    }
475}
476
477/// Causal generation configuration.
478///
479/// Controls structural causal model (SCM) based data generation that respects
480/// causal relationships between variables, supports do-calculus interventions,
481/// and enables counterfactual scenarios.
482#[derive(Debug, Clone, Serialize, Deserialize)]
483pub struct CausalSchemaConfig {
484    /// Whether causal generation is enabled.
485    #[serde(default)]
486    pub enabled: bool,
487    /// Built-in template to use: "fraud_detection", "revenue_cycle", or "custom".
488    #[serde(default = "default_causal_template")]
489    pub template: String,
490    /// Number of causal samples to generate.
491    #[serde(default = "default_causal_sample_size")]
492    pub sample_size: usize,
493    /// Whether to run causal validation on the output.
494    #[serde(default = "default_true")]
495    pub validate: bool,
496}
497
498fn default_causal_template() -> String {
499    "fraud_detection".to_string()
500}
501
502fn default_causal_sample_size() -> usize {
503    500
504}
505
506impl Default for CausalSchemaConfig {
507    fn default() -> Self {
508        Self {
509            enabled: false,
510            template: default_causal_template(),
511            sample_size: default_causal_sample_size(),
512            validate: true,
513        }
514    }
515}
516
517/// Graph export configuration for accounting network and ML training exports.
518///
519/// This section enables exporting generated data as graphs for:
520/// - Network reconstruction algorithms
521/// - Graph neural network training
522/// - Neo4j graph database import
523#[derive(Debug, Clone, Serialize, Deserialize)]
524pub struct GraphExportConfig {
525    /// Enable graph export.
526    #[serde(default)]
527    pub enabled: bool,
528
529    /// Graph types to generate.
530    #[serde(default = "default_graph_types")]
531    pub graph_types: Vec<GraphTypeConfig>,
532
533    /// Export formats to generate.
534    #[serde(default = "default_graph_formats")]
535    pub formats: Vec<GraphExportFormat>,
536
537    /// Train split ratio for ML datasets.
538    #[serde(default = "default_train_ratio")]
539    pub train_ratio: f64,
540
541    /// Validation split ratio for ML datasets.
542    #[serde(default = "default_val_ratio")]
543    pub validation_ratio: f64,
544
545    /// Random seed for train/val/test splits.
546    #[serde(default)]
547    pub split_seed: Option<u64>,
548
549    /// Output subdirectory for graph exports (relative to output directory).
550    #[serde(default = "default_graph_subdir")]
551    pub output_subdirectory: String,
552
553    /// Multi-layer hypergraph export settings for RustGraph integration.
554    #[serde(default)]
555    pub hypergraph: HypergraphExportSettings,
556
557    /// DGL-specific export settings.
558    #[serde(default)]
559    pub dgl: DglExportConfig,
560}
561
562fn default_graph_types() -> Vec<GraphTypeConfig> {
563    vec![GraphTypeConfig::default()]
564}
565
566fn default_graph_formats() -> Vec<GraphExportFormat> {
567    vec![GraphExportFormat::PytorchGeometric]
568}
569
570fn default_train_ratio() -> f64 {
571    0.7
572}
573
574fn default_val_ratio() -> f64 {
575    0.15
576}
577
578fn default_graph_subdir() -> String {
579    "graphs".to_string()
580}
581
582impl Default for GraphExportConfig {
583    fn default() -> Self {
584        Self {
585            enabled: false,
586            graph_types: default_graph_types(),
587            formats: default_graph_formats(),
588            train_ratio: 0.7,
589            validation_ratio: 0.15,
590            split_seed: None,
591            output_subdirectory: "graphs".to_string(),
592            hypergraph: HypergraphExportSettings::default(),
593            dgl: DglExportConfig::default(),
594        }
595    }
596}
597
598/// DGL-specific export settings.
599#[derive(Debug, Clone, Default, Serialize, Deserialize)]
600pub struct DglExportConfig {
601    /// Export as a heterogeneous graph (distinct node/edge types).
602    ///
603    /// When `true` the DGL exporter produces a `HeteroData` object with typed
604    /// node and edge stores rather than a single homogeneous graph.
605    /// Set to `true` in `graph_export.dgl.heterogeneous: true` in YAML.
606    #[serde(default)]
607    pub heterogeneous: bool,
608}
609
610// Default derived: heterogeneous = false (bool default)
611
612/// Settings for the multi-layer hypergraph export (RustGraph integration).
613///
614/// Produces a 3-layer hypergraph:
615/// - Layer 1: Governance & Controls (COSO, SOX, internal controls, organizational)
616/// - Layer 2: Process Events (P2P/O2C document flows, OCPM events)
617/// - Layer 3: Accounting Network (GL accounts, journal entries as hyperedges)
618#[derive(Debug, Clone, Serialize, Deserialize)]
619pub struct HypergraphExportSettings {
620    /// Enable hypergraph export.
621    #[serde(default)]
622    pub enabled: bool,
623
624    /// Maximum total nodes across all layers (default 50000).
625    #[serde(default = "default_hypergraph_max_nodes")]
626    pub max_nodes: usize,
627
628    /// Aggregation strategy when node budget is exceeded.
629    #[serde(default = "default_aggregation_strategy")]
630    pub aggregation_strategy: String,
631
632    /// Layer 1 (Governance & Controls) settings.
633    #[serde(default)]
634    pub governance_layer: GovernanceLayerSettings,
635
636    /// Layer 2 (Process Events) settings.
637    #[serde(default)]
638    pub process_layer: ProcessLayerSettings,
639
640    /// Layer 3 (Accounting Network) settings.
641    #[serde(default)]
642    pub accounting_layer: AccountingLayerSettings,
643
644    /// Cross-layer edge generation settings.
645    #[serde(default)]
646    pub cross_layer: CrossLayerSettings,
647
648    /// Output subdirectory for hypergraph files (relative to graph output directory).
649    #[serde(default = "default_hypergraph_subdir")]
650    pub output_subdirectory: String,
651
652    /// Output format: "native" (default) for internal field names, "unified" for RustGraph format.
653    #[serde(default = "default_hypergraph_format")]
654    pub output_format: String,
655
656    /// Optional URL for streaming unified JSONL to a RustGraph ingest endpoint.
657    #[serde(default)]
658    pub stream_target: Option<String>,
659
660    /// Batch size for streaming (number of JSONL lines per HTTP POST). Default: 1000.
661    #[serde(default = "default_stream_batch_size")]
662    pub stream_batch_size: usize,
663}
664
665fn default_hypergraph_max_nodes() -> usize {
666    50_000
667}
668
669fn default_aggregation_strategy() -> String {
670    "pool_by_counterparty".to_string()
671}
672
673fn default_hypergraph_subdir() -> String {
674    "hypergraph".to_string()
675}
676
677fn default_hypergraph_format() -> String {
678    "native".to_string()
679}
680
681fn default_stream_batch_size() -> usize {
682    1000
683}
684
685impl Default for HypergraphExportSettings {
686    fn default() -> Self {
687        Self {
688            enabled: false,
689            max_nodes: 50_000,
690            aggregation_strategy: "pool_by_counterparty".to_string(),
691            governance_layer: GovernanceLayerSettings::default(),
692            process_layer: ProcessLayerSettings::default(),
693            accounting_layer: AccountingLayerSettings::default(),
694            cross_layer: CrossLayerSettings::default(),
695            output_subdirectory: "hypergraph".to_string(),
696            output_format: "native".to_string(),
697            stream_target: None,
698            stream_batch_size: 1000,
699        }
700    }
701}
702
703/// Layer 1: Governance & Controls layer settings.
704#[derive(Debug, Clone, Serialize, Deserialize)]
705pub struct GovernanceLayerSettings {
706    /// Include COSO framework nodes (5 components + 17 principles).
707    #[serde(default = "default_true")]
708    pub include_coso: bool,
709    /// Include internal control nodes.
710    #[serde(default = "default_true")]
711    pub include_controls: bool,
712    /// Include SOX assertion nodes.
713    #[serde(default = "default_true")]
714    pub include_sox: bool,
715    /// Include vendor master data nodes.
716    #[serde(default = "default_true")]
717    pub include_vendors: bool,
718    /// Include customer master data nodes.
719    #[serde(default = "default_true")]
720    pub include_customers: bool,
721    /// Include employee/organizational nodes.
722    #[serde(default = "default_true")]
723    pub include_employees: bool,
724}
725
726impl Default for GovernanceLayerSettings {
727    fn default() -> Self {
728        Self {
729            include_coso: true,
730            include_controls: true,
731            include_sox: true,
732            include_vendors: true,
733            include_customers: true,
734            include_employees: true,
735        }
736    }
737}
738
739/// Layer 2: Process Events layer settings.
740#[derive(Debug, Clone, Serialize, Deserialize)]
741pub struct ProcessLayerSettings {
742    /// Include P2P (Procure-to-Pay) document flow nodes.
743    #[serde(default = "default_true")]
744    pub include_p2p: bool,
745    /// Include O2C (Order-to-Cash) document flow nodes.
746    #[serde(default = "default_true")]
747    pub include_o2c: bool,
748    /// Include S2C (Source-to-Contract) document flow nodes.
749    #[serde(default = "default_true")]
750    pub include_s2c: bool,
751    /// Include H2R (Hire-to-Retire) document flow nodes.
752    #[serde(default = "default_true")]
753    pub include_h2r: bool,
754    /// Include MFG (Manufacturing) document flow nodes.
755    #[serde(default = "default_true")]
756    pub include_mfg: bool,
757    /// Include BANK (Banking) document flow nodes.
758    #[serde(default = "default_true")]
759    pub include_bank: bool,
760    /// Include AUDIT document flow nodes.
761    #[serde(default = "default_true")]
762    pub include_audit: bool,
763    /// Include R2R (Record-to-Report) document flow nodes (bank recon + period close).
764    #[serde(default = "default_true")]
765    pub include_r2r: bool,
766    /// Export OCPM events as hyperedges.
767    #[serde(default = "default_true")]
768    pub events_as_hyperedges: bool,
769    /// Threshold: if a counterparty has more documents than this, aggregate into pool nodes.
770    #[serde(default = "default_docs_per_counterparty_threshold")]
771    pub docs_per_counterparty_threshold: usize,
772}
773
774fn default_docs_per_counterparty_threshold() -> usize {
775    20
776}
777
778impl Default for ProcessLayerSettings {
779    fn default() -> Self {
780        Self {
781            include_p2p: true,
782            include_o2c: true,
783            include_s2c: true,
784            include_h2r: true,
785            include_mfg: true,
786            include_bank: true,
787            include_audit: true,
788            include_r2r: true,
789            events_as_hyperedges: true,
790            docs_per_counterparty_threshold: 20,
791        }
792    }
793}
794
795/// Layer 3: Accounting Network layer settings.
796#[derive(Debug, Clone, Serialize, Deserialize)]
797pub struct AccountingLayerSettings {
798    /// Include GL account nodes.
799    #[serde(default = "default_true")]
800    pub include_accounts: bool,
801    /// Export journal entries as hyperedges (debit+credit accounts as participants).
802    #[serde(default = "default_true")]
803    pub je_as_hyperedges: bool,
804}
805
806impl Default for AccountingLayerSettings {
807    fn default() -> Self {
808        Self {
809            include_accounts: true,
810            je_as_hyperedges: true,
811        }
812    }
813}
814
815/// Cross-layer edge generation settings.
816#[derive(Debug, Clone, Serialize, Deserialize)]
817pub struct CrossLayerSettings {
818    /// Generate cross-layer edges (Control→Account, Vendor→PO, etc.).
819    #[serde(default = "default_true")]
820    pub enabled: bool,
821}
822
823impl Default for CrossLayerSettings {
824    fn default() -> Self {
825        Self { enabled: true }
826    }
827}
828
829/// Configuration for a specific graph type to export.
830#[derive(Debug, Clone, Serialize, Deserialize)]
831pub struct GraphTypeConfig {
832    /// Name identifier for this graph configuration.
833    #[serde(default = "default_graph_name")]
834    pub name: String,
835
836    /// Whether to aggregate parallel edges between the same nodes.
837    #[serde(default)]
838    pub aggregate_edges: bool,
839
840    /// Minimum edge weight to include (filters out small transactions).
841    #[serde(default)]
842    pub min_edge_weight: f64,
843
844    /// Whether to include document nodes (creates hub-and-spoke structure).
845    #[serde(default)]
846    pub include_document_nodes: bool,
847}
848
849fn default_graph_name() -> String {
850    "accounting_network".to_string()
851}
852
853impl Default for GraphTypeConfig {
854    fn default() -> Self {
855        Self {
856            name: "accounting_network".to_string(),
857            aggregate_edges: false,
858            min_edge_weight: 0.0,
859            include_document_nodes: false,
860        }
861    }
862}
863
864/// Export format for graph data.
865#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
866#[serde(rename_all = "snake_case")]
867pub enum GraphExportFormat {
868    /// PyTorch Geometric format (.npy files + metadata.json).
869    PytorchGeometric,
870    /// Neo4j format (CSV files + Cypher import scripts).
871    Neo4j,
872    /// Deep Graph Library format.
873    Dgl,
874    /// RustGraph/RustAssureTwin JSON format.
875    RustGraph,
876    /// RustGraph multi-layer hypergraph format (nodes.jsonl + edges.jsonl + hyperedges.jsonl).
877    RustGraphHypergraph,
878}
879
880/// Scenario configuration for metadata, tagging, and ML training setup.
881///
882/// This section enables tracking the purpose and characteristics of a generation run.
883#[derive(Debug, Clone, Default, Serialize, Deserialize)]
884pub struct ScenarioConfig {
885    /// Tags for categorizing and filtering datasets.
886    /// Examples: "fraud_detection", "retail", "month_end_stress", "ml_training"
887    #[serde(default)]
888    pub tags: Vec<String>,
889
890    /// Data quality profile preset.
891    /// - "clean": Minimal data quality issues (0.1% missing, 0.05% typos)
892    /// - "noisy": Moderate issues (5% missing, 2% typos, 1% duplicates)
893    /// - "legacy": Heavy issues simulating legacy system data (10% missing, 5% typos)
894    #[serde(default)]
895    pub profile: Option<String>,
896
897    /// Human-readable description of the scenario purpose.
898    #[serde(default)]
899    pub description: Option<String>,
900
901    /// Whether this run is for ML training (enables balanced labeling).
902    #[serde(default)]
903    pub ml_training: bool,
904
905    /// Target anomaly class balance for ML training.
906    /// If set, anomalies will be injected to achieve this ratio.
907    #[serde(default)]
908    pub target_anomaly_ratio: Option<f64>,
909
910    /// Custom metadata key-value pairs.
911    #[serde(default)]
912    pub metadata: std::collections::HashMap<String, String>,
913}
914
915/// Temporal drift configuration for simulating distribution changes over time.
916///
917/// This enables generation of data that shows realistic temporal evolution,
918/// useful for training drift detection models and testing temporal robustness.
919#[derive(Debug, Clone, Serialize, Deserialize)]
920pub struct TemporalDriftConfig {
921    /// Enable temporal drift simulation.
922    #[serde(default)]
923    pub enabled: bool,
924
925    /// Amount mean drift per period (e.g., 0.02 = 2% mean shift per month).
926    /// Simulates gradual inflation or business growth.
927    #[serde(default = "default_amount_drift")]
928    pub amount_mean_drift: f64,
929
930    /// Amount variance drift per period (e.g., 0.01 = 1% variance increase per month).
931    /// Simulates increasing volatility over time.
932    #[serde(default)]
933    pub amount_variance_drift: f64,
934
935    /// Anomaly rate drift per period (e.g., 0.001 = 0.1% increase per month).
936    /// Simulates increasing fraud attempts or degrading controls.
937    #[serde(default)]
938    pub anomaly_rate_drift: f64,
939
940    /// Concept drift rate - how quickly feature distributions change (0.0-1.0).
941    /// Higher values cause more rapid distribution shifts.
942    #[serde(default = "default_concept_drift")]
943    pub concept_drift_rate: f64,
944
945    /// Sudden drift events - probability of a sudden distribution shift in any period.
946    #[serde(default)]
947    pub sudden_drift_probability: f64,
948
949    /// Magnitude of sudden drift events when they occur (multiplier).
950    #[serde(default = "default_sudden_drift_magnitude")]
951    pub sudden_drift_magnitude: f64,
952
953    /// Seasonal drift - enable cyclic patterns that repeat annually.
954    #[serde(default)]
955    pub seasonal_drift: bool,
956
957    /// Drift start period (0 = from beginning). Use to simulate stable baseline before drift.
958    #[serde(default)]
959    pub drift_start_period: u32,
960
961    /// Drift type: "gradual", "sudden", "recurring", "mixed"
962    #[serde(default = "default_drift_type")]
963    pub drift_type: DriftType,
964}
965
966fn default_amount_drift() -> f64 {
967    0.02
968}
969
970fn default_concept_drift() -> f64 {
971    0.01
972}
973
974fn default_sudden_drift_magnitude() -> f64 {
975    2.0
976}
977
978fn default_drift_type() -> DriftType {
979    DriftType::Gradual
980}
981
982impl Default for TemporalDriftConfig {
983    fn default() -> Self {
984        Self {
985            enabled: false,
986            amount_mean_drift: 0.02,
987            amount_variance_drift: 0.0,
988            anomaly_rate_drift: 0.0,
989            concept_drift_rate: 0.01,
990            sudden_drift_probability: 0.0,
991            sudden_drift_magnitude: 2.0,
992            seasonal_drift: false,
993            drift_start_period: 0,
994            drift_type: DriftType::Gradual,
995        }
996    }
997}
998
999impl TemporalDriftConfig {
1000    /// Convert to core DriftConfig for use in generators.
1001    pub fn to_core_config(&self) -> datasynth_core::distributions::DriftConfig {
1002        datasynth_core::distributions::DriftConfig {
1003            enabled: self.enabled,
1004            amount_mean_drift: self.amount_mean_drift,
1005            amount_variance_drift: self.amount_variance_drift,
1006            anomaly_rate_drift: self.anomaly_rate_drift,
1007            concept_drift_rate: self.concept_drift_rate,
1008            sudden_drift_probability: self.sudden_drift_probability,
1009            sudden_drift_magnitude: self.sudden_drift_magnitude,
1010            seasonal_drift: self.seasonal_drift,
1011            drift_start_period: self.drift_start_period,
1012            drift_type: match self.drift_type {
1013                DriftType::Gradual => datasynth_core::distributions::DriftType::Gradual,
1014                DriftType::Sudden => datasynth_core::distributions::DriftType::Sudden,
1015                DriftType::Recurring => datasynth_core::distributions::DriftType::Recurring,
1016                DriftType::Mixed => datasynth_core::distributions::DriftType::Mixed,
1017            },
1018            regime_changes: Vec::new(),
1019            economic_cycle: Default::default(),
1020            parameter_drifts: Vec::new(),
1021        }
1022    }
1023}
1024
1025/// Types of temporal drift patterns.
1026#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1027#[serde(rename_all = "snake_case")]
1028pub enum DriftType {
1029    /// Gradual, continuous drift over time (like inflation).
1030    #[default]
1031    Gradual,
1032    /// Sudden, point-in-time shifts (like policy changes).
1033    Sudden,
1034    /// Recurring patterns that cycle (like seasonal variations).
1035    Recurring,
1036    /// Combination of gradual background drift with occasional sudden shifts.
1037    Mixed,
1038}
1039
1040// ============================================================================
1041// Streaming Output API Configuration (Phase 2)
1042// ============================================================================
1043
1044/// Configuration for streaming output API.
1045#[derive(Debug, Clone, Serialize, Deserialize)]
1046pub struct StreamingSchemaConfig {
1047    /// Enable streaming output.
1048    #[serde(default)]
1049    pub enabled: bool,
1050    /// Target events per second (0 = unlimited, default 0).
1051    #[serde(default)]
1052    pub events_per_second: f64,
1053    /// Token bucket burst size (default 100).
1054    #[serde(default = "default_burst_size")]
1055    pub burst_size: u32,
1056    /// Buffer size for streaming (number of items).
1057    #[serde(default = "default_buffer_size")]
1058    pub buffer_size: usize,
1059    /// Enable progress reporting.
1060    #[serde(default = "default_true")]
1061    pub enable_progress: bool,
1062    /// Progress reporting interval (number of items).
1063    #[serde(default = "default_progress_interval")]
1064    pub progress_interval: u64,
1065    /// Backpressure strategy.
1066    #[serde(default)]
1067    pub backpressure: BackpressureSchemaStrategy,
1068}
1069
1070fn default_buffer_size() -> usize {
1071    1000
1072}
1073
1074fn default_progress_interval() -> u64 {
1075    100
1076}
1077
1078impl Default for StreamingSchemaConfig {
1079    fn default() -> Self {
1080        Self {
1081            enabled: false,
1082            events_per_second: 0.0,
1083            burst_size: 100,
1084            buffer_size: 1000,
1085            enable_progress: true,
1086            progress_interval: 100,
1087            backpressure: BackpressureSchemaStrategy::Block,
1088        }
1089    }
1090}
1091
1092/// Backpressure strategy for streaming output.
1093#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1094#[serde(rename_all = "snake_case")]
1095pub enum BackpressureSchemaStrategy {
1096    /// Block until space is available in the buffer.
1097    #[default]
1098    Block,
1099    /// Drop oldest items when buffer is full.
1100    DropOldest,
1101    /// Drop newest items when buffer is full.
1102    DropNewest,
1103    /// Buffer overflow items up to a limit, then block.
1104    Buffer,
1105}
1106
1107// ============================================================================
1108// Rate Limiting Configuration (Phase 5)
1109// ============================================================================
1110
1111/// Configuration for rate limiting.
1112#[derive(Debug, Clone, Serialize, Deserialize)]
1113pub struct RateLimitSchemaConfig {
1114    /// Enable rate limiting.
1115    #[serde(default)]
1116    pub enabled: bool,
1117    /// Entities per second limit.
1118    #[serde(default = "default_entities_per_second")]
1119    pub entities_per_second: f64,
1120    /// Burst size (number of tokens in bucket).
1121    #[serde(default = "default_burst_size")]
1122    pub burst_size: u32,
1123    /// Backpressure strategy for rate limiting.
1124    #[serde(default)]
1125    pub backpressure: RateLimitBackpressureSchema,
1126}
1127
1128fn default_entities_per_second() -> f64 {
1129    1000.0
1130}
1131
1132fn default_burst_size() -> u32 {
1133    100
1134}
1135
1136impl Default for RateLimitSchemaConfig {
1137    fn default() -> Self {
1138        Self {
1139            enabled: false,
1140            entities_per_second: 1000.0,
1141            burst_size: 100,
1142            backpressure: RateLimitBackpressureSchema::Block,
1143        }
1144    }
1145}
1146
1147/// Backpressure strategy for rate limiting.
1148#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1149#[serde(rename_all = "snake_case")]
1150pub enum RateLimitBackpressureSchema {
1151    /// Block until rate allows.
1152    #[default]
1153    Block,
1154    /// Drop items that exceed rate.
1155    Drop,
1156    /// Buffer items and process when rate allows.
1157    Buffer,
1158}
1159
1160// ============================================================================
1161// Temporal Attribute Generation Configuration (Phase 3)
1162// ============================================================================
1163
1164/// Configuration for temporal attribute generation.
1165#[derive(Debug, Clone, Serialize, Deserialize)]
1166pub struct TemporalAttributeSchemaConfig {
1167    /// Enable temporal attribute generation.
1168    #[serde(default)]
1169    pub enabled: bool,
1170    /// Valid time configuration.
1171    #[serde(default)]
1172    pub valid_time: ValidTimeSchemaConfig,
1173    /// Transaction time configuration.
1174    #[serde(default)]
1175    pub transaction_time: TransactionTimeSchemaConfig,
1176    /// Generate version chains for entities.
1177    #[serde(default)]
1178    pub generate_version_chains: bool,
1179    /// Average number of versions per entity.
1180    #[serde(default = "default_avg_versions")]
1181    pub avg_versions_per_entity: f64,
1182}
1183
1184fn default_avg_versions() -> f64 {
1185    1.5
1186}
1187
1188impl Default for TemporalAttributeSchemaConfig {
1189    fn default() -> Self {
1190        Self {
1191            enabled: false,
1192            valid_time: ValidTimeSchemaConfig::default(),
1193            transaction_time: TransactionTimeSchemaConfig::default(),
1194            generate_version_chains: false,
1195            avg_versions_per_entity: 1.5,
1196        }
1197    }
1198}
1199
1200/// Configuration for valid time (business time) generation.
1201#[derive(Debug, Clone, Serialize, Deserialize)]
1202pub struct ValidTimeSchemaConfig {
1203    /// Probability that valid_to is set (entity has ended validity).
1204    #[serde(default = "default_closed_probability")]
1205    pub closed_probability: f64,
1206    /// Average validity duration in days.
1207    #[serde(default = "default_avg_validity_days")]
1208    pub avg_validity_days: u32,
1209    /// Standard deviation of validity duration in days.
1210    #[serde(default = "default_validity_stddev")]
1211    pub validity_stddev_days: u32,
1212}
1213
1214fn default_closed_probability() -> f64 {
1215    0.1
1216}
1217
1218fn default_avg_validity_days() -> u32 {
1219    365
1220}
1221
1222fn default_validity_stddev() -> u32 {
1223    90
1224}
1225
1226impl Default for ValidTimeSchemaConfig {
1227    fn default() -> Self {
1228        Self {
1229            closed_probability: 0.1,
1230            avg_validity_days: 365,
1231            validity_stddev_days: 90,
1232        }
1233    }
1234}
1235
1236/// Configuration for transaction time (system time) generation.
1237#[derive(Debug, Clone, Serialize, Deserialize)]
1238pub struct TransactionTimeSchemaConfig {
1239    /// Average recording delay in seconds (0 = immediate).
1240    #[serde(default)]
1241    pub avg_recording_delay_seconds: u32,
1242    /// Allow backdating (recording time before valid time).
1243    #[serde(default)]
1244    pub allow_backdating: bool,
1245    /// Probability of backdating if allowed.
1246    #[serde(default = "default_backdating_probability")]
1247    pub backdating_probability: f64,
1248    /// Maximum backdate days.
1249    #[serde(default = "default_max_backdate_days")]
1250    pub max_backdate_days: u32,
1251}
1252
1253fn default_backdating_probability() -> f64 {
1254    0.01
1255}
1256
1257fn default_max_backdate_days() -> u32 {
1258    30
1259}
1260
1261impl Default for TransactionTimeSchemaConfig {
1262    fn default() -> Self {
1263        Self {
1264            avg_recording_delay_seconds: 0,
1265            allow_backdating: false,
1266            backdating_probability: 0.01,
1267            max_backdate_days: 30,
1268        }
1269    }
1270}
1271
1272// ============================================================================
1273// Relationship Generation Configuration (Phase 4)
1274// ============================================================================
1275
1276/// Configuration for relationship generation.
1277#[derive(Debug, Clone, Serialize, Deserialize)]
1278pub struct RelationshipSchemaConfig {
1279    /// Relationship type definitions.
1280    #[serde(default)]
1281    pub relationship_types: Vec<RelationshipTypeSchemaConfig>,
1282    /// Allow orphan entities (entities with no relationships).
1283    #[serde(default = "default_true")]
1284    pub allow_orphans: bool,
1285    /// Probability of creating an orphan entity.
1286    #[serde(default = "default_orphan_probability")]
1287    pub orphan_probability: f64,
1288    /// Allow circular relationships.
1289    #[serde(default)]
1290    pub allow_circular: bool,
1291    /// Maximum depth for circular relationship detection.
1292    #[serde(default = "default_max_circular_depth")]
1293    pub max_circular_depth: u32,
1294}
1295
1296fn default_orphan_probability() -> f64 {
1297    0.01
1298}
1299
1300fn default_max_circular_depth() -> u32 {
1301    3
1302}
1303
1304impl Default for RelationshipSchemaConfig {
1305    fn default() -> Self {
1306        Self {
1307            relationship_types: Vec::new(),
1308            allow_orphans: true,
1309            orphan_probability: 0.01,
1310            allow_circular: false,
1311            max_circular_depth: 3,
1312        }
1313    }
1314}
1315
1316/// Configuration for a specific relationship type.
1317#[derive(Debug, Clone, Serialize, Deserialize)]
1318pub struct RelationshipTypeSchemaConfig {
1319    /// Name of the relationship type (e.g., "debits", "credits", "created").
1320    pub name: String,
1321    /// Source entity type (e.g., "journal_entry").
1322    pub source_type: String,
1323    /// Target entity type (e.g., "account").
1324    pub target_type: String,
1325    /// Cardinality rule for this relationship.
1326    #[serde(default)]
1327    pub cardinality: CardinalitySchemaRule,
1328    /// Weight for this relationship in random selection.
1329    #[serde(default = "default_relationship_weight")]
1330    pub weight: f64,
1331    /// Whether this relationship is required.
1332    #[serde(default)]
1333    pub required: bool,
1334    /// Whether this relationship is directed.
1335    #[serde(default = "default_true")]
1336    pub directed: bool,
1337}
1338
1339fn default_relationship_weight() -> f64 {
1340    1.0
1341}
1342
1343impl Default for RelationshipTypeSchemaConfig {
1344    fn default() -> Self {
1345        Self {
1346            name: String::new(),
1347            source_type: String::new(),
1348            target_type: String::new(),
1349            cardinality: CardinalitySchemaRule::default(),
1350            weight: 1.0,
1351            required: false,
1352            directed: true,
1353        }
1354    }
1355}
1356
1357/// Cardinality rule for relationships in schema config.
1358#[derive(Debug, Clone, Serialize, Deserialize)]
1359#[serde(rename_all = "snake_case")]
1360pub enum CardinalitySchemaRule {
1361    /// One source to one target.
1362    OneToOne,
1363    /// One source to many targets.
1364    OneToMany {
1365        /// Minimum number of targets.
1366        min: u32,
1367        /// Maximum number of targets.
1368        max: u32,
1369    },
1370    /// Many sources to one target.
1371    ManyToOne {
1372        /// Minimum number of sources.
1373        min: u32,
1374        /// Maximum number of sources.
1375        max: u32,
1376    },
1377    /// Many sources to many targets.
1378    ManyToMany {
1379        /// Minimum targets per source.
1380        min_per_source: u32,
1381        /// Maximum targets per source.
1382        max_per_source: u32,
1383    },
1384}
1385
1386impl Default for CardinalitySchemaRule {
1387    fn default() -> Self {
1388        Self::OneToMany { min: 1, max: 5 }
1389    }
1390}
1391
1392/// Global configuration settings.
1393#[derive(Debug, Clone, Serialize, Deserialize)]
1394pub struct GlobalConfig {
1395    /// Random seed for reproducibility
1396    pub seed: Option<u64>,
1397    /// Industry sector
1398    pub industry: IndustrySector,
1399    /// Simulation start date (YYYY-MM-DD)
1400    #[serde(alias = "startDate")]
1401    pub start_date: String,
1402    /// Simulation period in months
1403    #[serde(alias = "periodMonths")]
1404    pub period_months: u32,
1405    /// Base currency for group reporting
1406    #[serde(default = "default_currency", alias = "groupCurrency")]
1407    pub group_currency: String,
1408    /// Presentation currency for consolidated financial statements (ISO 4217).
1409    /// If not set, defaults to `group_currency`.
1410    #[serde(default, alias = "presentationCurrency")]
1411    pub presentation_currency: Option<String>,
1412    /// Enable parallel generation
1413    #[serde(default = "default_true")]
1414    pub parallel: bool,
1415    /// Number of worker threads (0 = auto-detect)
1416    #[serde(default, alias = "workerThreads")]
1417    pub worker_threads: usize,
1418    /// Memory limit in MB (0 = unlimited)
1419    #[serde(default, alias = "memoryLimitMb")]
1420    pub memory_limit_mb: usize,
1421    /// Fiscal year length in months (defaults to 12 if not set).
1422    /// Used by session-based generation to split the total period into fiscal years.
1423    #[serde(default, alias = "fiscalYearMonths")]
1424    pub fiscal_year_months: Option<u32>,
1425}
1426
1427fn default_currency() -> String {
1428    "USD".to_string()
1429}
1430fn default_true() -> bool {
1431    true
1432}
1433
1434/// Configuration for generation session behavior.
1435///
1436/// When enabled, the generation pipeline splits the total period into fiscal years
1437/// and generates data period-by-period, carrying forward balance state.
1438#[derive(Debug, Clone, Serialize, Deserialize)]
1439pub struct SessionSchemaConfig {
1440    /// Whether session-based (period-by-period) generation is enabled.
1441    #[serde(default)]
1442    pub enabled: bool,
1443    /// Optional path for saving/loading session checkpoint files.
1444    #[serde(default)]
1445    pub checkpoint_path: Option<String>,
1446    /// Whether to write output files per fiscal period (e.g., `period_01/`).
1447    #[serde(default = "default_true")]
1448    pub per_period_output: bool,
1449    /// Whether to also produce a single consolidated output across all periods.
1450    #[serde(default = "default_true")]
1451    pub consolidated_output: bool,
1452}
1453
1454impl Default for SessionSchemaConfig {
1455    fn default() -> Self {
1456        Self {
1457            enabled: false,
1458            checkpoint_path: None,
1459            per_period_output: true,
1460            consolidated_output: true,
1461        }
1462    }
1463}
1464
1465/// Company code configuration.
1466#[derive(Debug, Clone, Serialize, Deserialize)]
1467pub struct CompanyConfig {
1468    /// Company code identifier
1469    pub code: String,
1470    /// Company name
1471    pub name: String,
1472    /// Local currency (ISO 4217)
1473    pub currency: String,
1474    /// Functional currency for IAS 21 translation (ISO 4217).
1475    /// If not set, defaults to the `currency` field (i.e. local == functional).
1476    #[serde(default, alias = "functionalCurrency")]
1477    pub functional_currency: Option<String>,
1478    /// Country code (ISO 3166-1 alpha-2)
1479    pub country: String,
1480    /// Fiscal year variant
1481    #[serde(default = "default_fiscal_variant", alias = "fiscalYearVariant")]
1482    pub fiscal_year_variant: String,
1483    /// Transaction volume per year
1484    #[serde(alias = "annualTransactionVolume")]
1485    pub annual_transaction_volume: TransactionVolume,
1486    /// Company-specific transaction weight
1487    #[serde(default = "default_weight", alias = "volumeWeight")]
1488    pub volume_weight: f64,
1489}
1490
1491fn default_fiscal_variant() -> String {
1492    "K4".to_string()
1493}
1494fn default_weight() -> f64 {
1495    1.0
1496}
1497
1498/// Transaction volume presets.
1499#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1500#[serde(rename_all = "snake_case")]
1501pub enum TransactionVolume {
1502    /// 10,000 transactions per year
1503    TenK,
1504    /// 50,000 transactions per year
1505    FiftyK,
1506    /// 100,000 transactions per year
1507    HundredK,
1508    /// 1,000,000 transactions per year
1509    OneM,
1510    /// 10,000,000 transactions per year
1511    TenM,
1512    /// 100,000,000 transactions per year
1513    HundredM,
1514    /// Custom count
1515    Custom(u64),
1516}
1517
1518impl TransactionVolume {
1519    /// Get the transaction count.
1520    pub fn count(&self) -> u64 {
1521        match self {
1522            Self::TenK => 10_000,
1523            Self::FiftyK => 50_000,
1524            Self::HundredK => 100_000,
1525            Self::OneM => 1_000_000,
1526            Self::TenM => 10_000_000,
1527            Self::HundredM => 100_000_000,
1528            Self::Custom(n) => *n,
1529        }
1530    }
1531}
1532
1533/// Chart of Accounts configuration.
1534#[derive(Debug, Clone, Serialize, Deserialize)]
1535pub struct ChartOfAccountsConfig {
1536    /// CoA complexity level
1537    pub complexity: CoAComplexity,
1538    /// Use industry-specific accounts
1539    #[serde(default = "default_true")]
1540    pub industry_specific: bool,
1541    /// Custom account definitions file
1542    pub custom_accounts: Option<PathBuf>,
1543    /// Minimum hierarchy depth
1544    #[serde(default = "default_min_depth")]
1545    pub min_hierarchy_depth: u8,
1546    /// Maximum hierarchy depth
1547    #[serde(default = "default_max_depth")]
1548    pub max_hierarchy_depth: u8,
1549    /// **v5.7.0** — expand canonical accounts into industry-specific
1550    /// 6-digit sub-accounts using the embedded
1551    /// [`datasynth_core::industry_packs`] (manufacturing, retail,
1552    /// financial_services, healthcare, technology). When `true`:
1553    ///
1554    /// - Each canonical 4-digit account that has an expansion in the
1555    ///   pack becomes a non-postable control account (`is_postable =
1556    ///   false`).
1557    /// - 2–6 6-digit sub-accounts are added per parent, with
1558    ///   suffix-driven names (`"Product Revenue — Steel Products"`),
1559    ///   industry-realistic gaps, and inherited ISO 21378 codes.
1560    /// - Generators that currently target canonical accounts via
1561    ///   constants will pick a sub-account deterministically per
1562    ///   `document_id` (preserving seed-based reproducibility).
1563    ///
1564    /// Default: `false` (preserves v5.6.0 behaviour exactly — same
1565    /// account count, same numbering, same goldens).
1566    #[serde(default, alias = "expandIndustrySubaccounts")]
1567    pub expand_industry_subaccounts: bool,
1568}
1569
1570fn default_min_depth() -> u8 {
1571    2
1572}
1573fn default_max_depth() -> u8 {
1574    5
1575}
1576
1577impl Default for ChartOfAccountsConfig {
1578    fn default() -> Self {
1579        Self {
1580            complexity: CoAComplexity::Small,
1581            industry_specific: true,
1582            custom_accounts: None,
1583            min_hierarchy_depth: default_min_depth(),
1584            max_hierarchy_depth: default_max_depth(),
1585            expand_industry_subaccounts: false,
1586        }
1587    }
1588}
1589
1590/// Transaction generation configuration.
1591#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1592pub struct TransactionConfig {
1593    /// Line item distribution
1594    #[serde(default)]
1595    pub line_item_distribution: LineItemDistributionConfig,
1596    /// Debit/credit balance distribution
1597    #[serde(default)]
1598    pub debit_credit_distribution: DebitCreditDistributionConfig,
1599    /// Even/odd line count distribution
1600    #[serde(default)]
1601    pub even_odd_distribution: EvenOddDistributionConfig,
1602    /// Transaction source distribution
1603    #[serde(default)]
1604    pub source_distribution: SourceDistribution,
1605    /// Seasonality configuration
1606    #[serde(default)]
1607    pub seasonality: SeasonalityConfig,
1608    /// Amount distribution
1609    #[serde(default)]
1610    pub amounts: AmountDistributionConfig,
1611    /// Benford's Law compliance configuration
1612    #[serde(default)]
1613    pub benford: BenfordConfig,
1614}
1615
1616/// Benford's Law compliance configuration.
1617#[derive(Debug, Clone, Serialize, Deserialize)]
1618pub struct BenfordConfig {
1619    /// Enable Benford's Law compliance for amount generation
1620    #[serde(default = "default_true")]
1621    pub enabled: bool,
1622    /// Tolerance for deviation from ideal Benford distribution (0.0-1.0)
1623    #[serde(default = "default_benford_tolerance")]
1624    pub tolerance: f64,
1625    /// Transaction sources exempt from Benford's Law (fixed amounts)
1626    #[serde(default)]
1627    pub exempt_sources: Vec<BenfordExemption>,
1628}
1629
1630fn default_benford_tolerance() -> f64 {
1631    0.05
1632}
1633
1634impl Default for BenfordConfig {
1635    fn default() -> Self {
1636        Self {
1637            enabled: true,
1638            tolerance: default_benford_tolerance(),
1639            exempt_sources: vec![BenfordExemption::Recurring, BenfordExemption::Payroll],
1640        }
1641    }
1642}
1643
1644/// Types of transactions exempt from Benford's Law.
1645#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1646#[serde(rename_all = "snake_case")]
1647pub enum BenfordExemption {
1648    /// Recurring fixed amounts (rent, subscriptions)
1649    Recurring,
1650    /// Payroll (standardized salaries)
1651    Payroll,
1652    /// Fixed fees and charges
1653    FixedFees,
1654    /// Round number purchases (often legitimate)
1655    RoundAmounts,
1656}
1657
1658/// Distribution of transaction sources.
1659#[derive(Debug, Clone, Serialize, Deserialize)]
1660pub struct SourceDistribution {
1661    /// Manual entries percentage
1662    pub manual: f64,
1663    /// Automated system entries
1664    pub automated: f64,
1665    /// Recurring entries
1666    pub recurring: f64,
1667    /// Adjustment entries
1668    pub adjustment: f64,
1669}
1670
1671impl Default for SourceDistribution {
1672    fn default() -> Self {
1673        Self {
1674            manual: 0.20,
1675            automated: 0.70,
1676            recurring: 0.07,
1677            adjustment: 0.03,
1678        }
1679    }
1680}
1681
1682/// Output configuration.
1683#[derive(Debug, Clone, Serialize, Deserialize)]
1684pub struct OutputConfig {
1685    /// Output mode
1686    #[serde(default)]
1687    pub mode: OutputMode,
1688    /// Output directory
1689    #[serde(alias = "outputDirectory")]
1690    pub output_directory: PathBuf,
1691    /// File formats to generate. Accepts both `formats: [json, csv]`
1692    /// (canonical YAML) and `exportFormat: "json"` / `exportFormats:
1693    /// ["json", "csv"]` (SDK-style camelCase). The single-string
1694    /// `exportFormat` form is deserialised via `one_or_many_formats`
1695    /// so SDK clients submitting `exportFormat: "json"` hit the right
1696    /// code path instead of silently falling through to the Parquet
1697    /// default — the bug the SDK team flagged in v4.4.0.
1698    #[serde(
1699        default = "default_formats",
1700        alias = "exportFormats",
1701        alias = "exportFormat",
1702        deserialize_with = "one_or_many_formats"
1703    )]
1704    pub formats: Vec<FileFormat>,
1705    /// Compression settings
1706    #[serde(default)]
1707    pub compression: CompressionConfig,
1708    /// Batch size for writes
1709    #[serde(default = "default_batch_size", alias = "batchSize")]
1710    pub batch_size: usize,
1711    /// Include ACDOCA format
1712    #[serde(default = "default_true", alias = "includeAcdoca")]
1713    pub include_acdoca: bool,
1714    /// Include BSEG format
1715    #[serde(default, alias = "includeBseg")]
1716    pub include_bseg: bool,
1717    /// Partition by fiscal period
1718    #[serde(default = "default_true", alias = "partitionByPeriod")]
1719    pub partition_by_period: bool,
1720    /// Partition by company code
1721    #[serde(default, alias = "partitionByCompany")]
1722    pub partition_by_company: bool,
1723    /// Numeric serialization mode for JSON output.
1724    /// "string" (default): decimals as `"1729237.30"` — lossless precision.
1725    /// "native": decimals as `1729237.30` — friendlier for pandas/analytics.
1726    #[serde(default, alias = "numericMode")]
1727    pub numeric_mode: NumericMode,
1728    /// JSON export layout for journal entries and document flows.
1729    /// "nested" (default): `{"header": {...}, "lines": [...]}` — natural ERP structure.
1730    /// "flat": header fields repeated on every line — friendlier for analytics/ML.
1731    ///
1732    /// Accepts both `export_layout` (canonical / YAML) and `exportLayout`
1733    /// (camelCase / SDK JSON) so SDKs that follow camelCase conventions
1734    /// hit the flat path rather than silently getting the Nested default.
1735    /// Before v3.1.1 the missing camelCase alias meant SDK requests with
1736    /// `exportLayout: "flat"` were silently ignored, which SDK operators
1737    /// reported as "flat hangs generation" (the job completed with Nested
1738    /// layout, but manifests didn't match the expected flat shape).
1739    #[serde(default, alias = "exportLayout")]
1740    pub export_layout: ExportLayout,
1741    /// SAP / HANA export settings (only read when the CLI
1742    /// `--export-format sap` flag is passed). Empty by default so
1743    /// existing configs don't change behaviour; dialect defaults to
1744    /// `classic` for backward compatibility.
1745    #[serde(default, alias = "sapExport")]
1746    pub sap: SapExportSettings,
1747    /// SAF-T (Standard Audit File for Tax) export settings. Read when
1748    /// the CLI `--export-format saft` flag is passed. Defaults to
1749    /// Portugal (`pt`) because the PT variant is the most mature and
1750    /// cross-jurisdiction compatible. Override with
1751    /// `jurisdiction: pl|ro|no|lu` for the other supported countries.
1752    #[serde(default, alias = "saftExport")]
1753    pub saft: SaftExportSettings,
1754}
1755
1756/// Configuration for the SAP export writers (BKPF / BSEG / ACDOCA and
1757/// master-data tables).
1758///
1759/// Mirror of `datasynth_output::SapExportConfig` in YAML form — the CLI
1760/// translates this into the runtime struct before invoking the exporter,
1761/// replacing the v3.x hardcoded `SapExportConfig::default()`.
1762#[derive(Debug, Clone, Serialize, Deserialize)]
1763pub struct SapExportSettings {
1764    /// SAP client / MANDT column value on every table.
1765    #[serde(default = "default_sap_client")]
1766    pub client: String,
1767    /// Leading ledger for ACDOCA rows (0L for S/4HANA default).
1768    #[serde(default = "default_sap_ledger")]
1769    pub ledger: String,
1770    /// Source system identifier — written to ACDOCA.AWSYS so downstream
1771    /// consumers can distinguish synthetic rows from production ones.
1772    #[serde(default = "default_sap_source_system")]
1773    pub source_system: String,
1774    /// Local currency (WAERS / RWCUR).
1775    #[serde(default = "default_sap_currency")]
1776    pub local_currency: String,
1777    /// Optional group / consolidation currency (triggers the HSL / RHCUR columns).
1778    #[serde(default, skip_serializing_if = "Option::is_none")]
1779    pub group_currency: Option<String>,
1780    /// Which SAP tables to export. Empty = default set (bkpf, bseg, acdoca).
1781    #[serde(default)]
1782    pub tables: Vec<String>,
1783    /// Include ZSIM_* extension columns on ACDOCA rows.
1784    #[serde(default = "default_true")]
1785    pub include_extension_fields: bool,
1786    /// Export dialect — `classic` (R/3 / BODS) or `hana` (S/4HANA CDS).
1787    #[serde(default)]
1788    pub dialect: SapDialectSetting,
1789    /// Legacy flag, retained for backward compatibility. Has no effect
1790    /// when `dialect = hana`.
1791    #[serde(default = "default_true")]
1792    pub use_sap_date_format: bool,
1793}
1794
1795impl Default for SapExportSettings {
1796    fn default() -> Self {
1797        Self {
1798            client: default_sap_client(),
1799            ledger: default_sap_ledger(),
1800            source_system: default_sap_source_system(),
1801            local_currency: default_sap_currency(),
1802            group_currency: None,
1803            tables: Vec::new(),
1804            include_extension_fields: true,
1805            dialect: SapDialectSetting::default(),
1806            use_sap_date_format: true,
1807        }
1808    }
1809}
1810
1811fn default_sap_client() -> String {
1812    "100".to_string()
1813}
1814fn default_sap_ledger() -> String {
1815    "0L".to_string()
1816}
1817fn default_sap_source_system() -> String {
1818    "SYNTH".to_string()
1819}
1820fn default_sap_currency() -> String {
1821    "USD".to_string()
1822}
1823
1824/// SAP export dialect (wire form — `datasynth_output::SapDialect` is the
1825/// runtime form).
1826#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
1827#[serde(rename_all = "snake_case")]
1828pub enum SapDialectSetting {
1829    /// Legacy R/3 / BODS-compatible CSV (default).
1830    #[default]
1831    Classic,
1832    /// S/4HANA CDS dialect (semicolon + UTF-8 BOM + decimal comma + ISO dates).
1833    Hana,
1834}
1835
1836/// SAF-T export settings (v4.3.1).
1837#[derive(Debug, Clone, Serialize, Deserialize)]
1838pub struct SaftExportSettings {
1839    /// ISO-ish two-letter code: `pt` / `pl` / `ro` / `no` / `lu`.
1840    /// Defaults to `pt` (Portugal, most mature variant).
1841    #[serde(default = "default_saft_jurisdiction")]
1842    pub jurisdiction: String,
1843    /// Company tax registration number / VAT ID / TIN used in the
1844    /// `Header.TaxRegistrationNumber` element. Falls back to
1845    /// `"Desconhecido"` (Portuguese for "unknown") when empty.
1846    #[serde(default)]
1847    pub company_tax_id: String,
1848    /// Optional override for the company name used in the Header.
1849    /// When empty, the first configured company's `name` is used.
1850    #[serde(default)]
1851    pub company_name: String,
1852}
1853
1854impl Default for SaftExportSettings {
1855    fn default() -> Self {
1856        Self {
1857            jurisdiction: default_saft_jurisdiction(),
1858            company_tax_id: String::new(),
1859            company_name: String::new(),
1860        }
1861    }
1862}
1863
1864fn default_saft_jurisdiction() -> String {
1865    "pt".to_string()
1866}
1867
1868fn default_formats() -> Vec<FileFormat> {
1869    vec![FileFormat::Parquet]
1870}
1871fn default_batch_size() -> usize {
1872    100_000
1873}
1874
1875/// Custom deserializer for `formats` that accepts either a single
1876/// `FileFormat` (e.g. `"json"` for SDK `exportFormat: "json"`) or a
1877/// vector (e.g. `["json", "csv"]`). Without this shim an SDK config
1878/// with `exportFormat: "json"` would fail to parse (serde expects a
1879/// sequence for a `Vec` field) and silently fall through to defaults.
1880fn one_or_many_formats<'de, D>(deserializer: D) -> Result<Vec<FileFormat>, D::Error>
1881where
1882    D: serde::Deserializer<'de>,
1883{
1884    #[derive(Deserialize)]
1885    #[serde(untagged)]
1886    enum OneOrMany {
1887        One(FileFormat),
1888        Many(Vec<FileFormat>),
1889    }
1890    match OneOrMany::deserialize(deserializer)? {
1891        OneOrMany::One(f) => Ok(vec![f]),
1892        OneOrMany::Many(v) => Ok(v),
1893    }
1894}
1895
1896impl Default for OutputConfig {
1897    fn default() -> Self {
1898        Self {
1899            mode: OutputMode::FlatFile,
1900            output_directory: PathBuf::from("./output"),
1901            formats: default_formats(),
1902            compression: CompressionConfig::default(),
1903            batch_size: default_batch_size(),
1904            include_acdoca: true,
1905            include_bseg: false,
1906            partition_by_period: true,
1907            partition_by_company: false,
1908            numeric_mode: NumericMode::default(),
1909            export_layout: ExportLayout::default(),
1910            sap: SapExportSettings::default(),
1911            saft: SaftExportSettings::default(),
1912        }
1913    }
1914}
1915
1916/// Numeric serialization mode for JSON decimal fields.
1917#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
1918#[serde(rename_all = "snake_case")]
1919pub enum NumericMode {
1920    /// Decimals as JSON strings (e.g. `"1729237.30"`). Preserves full precision.
1921    #[default]
1922    String,
1923    /// Decimals as JSON numbers (e.g. `1729237.30`). Friendlier for pandas/analytics.
1924    Native,
1925}
1926
1927/// JSON export layout for nested structures (journal entries, document flows).
1928#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
1929#[serde(rename_all = "snake_case")]
1930pub enum ExportLayout {
1931    /// Nested structure: `{"header": {...}, "lines": [...]}`. Natural ERP format.
1932    #[default]
1933    Nested,
1934    /// Flat structure: header fields repeated on every line. Analytics-friendly.
1935    Flat,
1936}
1937
1938/// Output mode.
1939#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
1940#[serde(rename_all = "snake_case")]
1941pub enum OutputMode {
1942    /// Stream records as generated
1943    Streaming,
1944    /// Write to flat files
1945    #[default]
1946    FlatFile,
1947    /// Both streaming and flat file
1948    Both,
1949}
1950
1951/// Supported file formats.
1952#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1953#[serde(rename_all = "snake_case")]
1954pub enum FileFormat {
1955    Csv,
1956    Parquet,
1957    Json,
1958    JsonLines,
1959}
1960
1961/// Compression configuration.
1962#[derive(Debug, Clone, Serialize, Deserialize)]
1963pub struct CompressionConfig {
1964    /// Enable compression
1965    #[serde(default = "default_true")]
1966    pub enabled: bool,
1967    /// Compression algorithm
1968    #[serde(default)]
1969    pub algorithm: CompressionAlgorithm,
1970    /// Compression level (1-9)
1971    #[serde(default = "default_compression_level")]
1972    pub level: u8,
1973}
1974
1975fn default_compression_level() -> u8 {
1976    3
1977}
1978
1979impl Default for CompressionConfig {
1980    fn default() -> Self {
1981        Self {
1982            enabled: true,
1983            algorithm: CompressionAlgorithm::default(),
1984            level: default_compression_level(),
1985        }
1986    }
1987}
1988
1989/// Compression algorithms.
1990#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
1991#[serde(rename_all = "snake_case")]
1992pub enum CompressionAlgorithm {
1993    Gzip,
1994    #[default]
1995    Zstd,
1996    Lz4,
1997    Snappy,
1998}
1999
2000/// Fraud simulation configuration.
2001///
2002/// ## Document-level vs. line-level fraud
2003///
2004/// `fraud_rate` applies to individual journal-entry lines (line-level).
2005/// `document_fraud_rate` (optional) applies to source documents
2006/// (purchase orders, vendor invoices, customer invoices, payments), and when
2007/// `propagate_to_lines` is true, every JE derived from a fraudulent document
2008/// also gets `is_fraud = true`. This lets users express either:
2009///
2010///  * pure line-level fraud (`document_fraud_rate = None`): legacy behaviour;
2011///  * pure document-level fraud (`fraud_rate ≈ 0` and `document_fraud_rate` set):
2012///    fraud rings expressed at document granularity — realistic for PO/invoice
2013///    fraud schemes where one fraudulent document spawns multiple derived JEs;
2014///  * hybrid (both set): document-level scheme fraud plus unrelated line-level
2015///    slip-ups.
2016///
2017/// `propagate_to_document` does the inverse: when a JE is tagged as fraud by
2018/// the anomaly injector, its source document is also marked fraudulent.
2019#[derive(Debug, Clone, Serialize, Deserialize)]
2020pub struct FraudConfig {
2021    /// Enable fraud scenario generation
2022    #[serde(default)]
2023    pub enabled: bool,
2024    /// Line-level fraud rate: fraction of individual JE lines flagged as fraud (0.0 to 1.0).
2025    ///
2026    /// # Effective line-level prevalence
2027    ///
2028    /// If `document_fraud_rate = Some(d)` and `propagate_to_lines = true`,
2029    /// the observed line-level fraud prevalence is roughly:
2030    ///
2031    /// > `P(line is_fraud) ≈ fraud_rate + d × avg_lines_per_fraud_doc / total_lines`
2032    ///
2033    /// For a typical retail job (avg 3 lines per document, ~30 % of lines
2034    /// come from doc-flow-derived JEs) the combined rate lands near:
2035    ///
2036    /// > `fraud_rate + 0.3 × d`
2037    ///
2038    /// so setting `fraud_rate=0.02, document_fraud_rate=0.05, propagate_to_lines=true`
2039    /// produces ~3.5 % line-level fraud, not 2 %. To target a specific
2040    /// line-level prevalence X, choose `fraud_rate = X - 0.3 × d`.
2041    #[serde(default = "default_fraud_rate", alias = "fraudRate")]
2042    pub fraud_rate: f64,
2043    /// Document-level fraud rate: fraction of source documents (PO, vendor
2044    /// invoice, customer invoice, payment) flagged as fraud. `None` disables
2045    /// document-level injection; `Some(r)` marks ~r × document-count as fraud
2046    /// independently of the line-level rate.
2047    ///
2048    /// v4.4.2+ default: `Some(0.01)` — the SDK team reported
2049    /// `is_fraud_propagated: 0/72` regressed from `12/33` in 3.1.1 because
2050    /// the default had silently become None. A 1% document-fraud default
2051    /// restores the propagation signal (~0.3% of JE headers carry
2052    /// `is_fraud_propagated = true`) without meaningfully changing the
2053    /// line-level fraud prevalence. Set to `Some(0.0)` or `null` in your
2054    /// YAML to explicitly disable document-level injection.
2055    #[serde(default = "default_document_fraud_rate", alias = "documentFraudRate")]
2056    pub document_fraud_rate: Option<f64>,
2057    /// When true, flagging a document as fraudulent cascades `is_fraud = true`
2058    /// and `fraud_type` to every journal entry derived from that document,
2059    /// and records `fraud_source_document_id` on the JE header.
2060    /// Default: `true`.
2061    #[serde(default = "default_true", alias = "propagateToLines")]
2062    pub propagate_to_lines: bool,
2063    /// When true, tagging a JE as fraud via line-level anomaly injection also
2064    /// marks the JE's source document as fraudulent (if it can be resolved).
2065    /// Default: `true`.
2066    #[serde(default = "default_true", alias = "propagateToDocument")]
2067    pub propagate_to_document: bool,
2068    /// Fraud type distribution
2069    #[serde(default)]
2070    pub fraud_type_distribution: FraudTypeDistribution,
2071    /// Enable fraud clustering
2072    #[serde(default)]
2073    pub clustering_enabled: bool,
2074    /// Clustering factor
2075    #[serde(default = "default_clustering_factor")]
2076    pub clustering_factor: f64,
2077    /// Approval thresholds for threshold-adjacent fraud pattern
2078    #[serde(default = "default_approval_thresholds")]
2079    pub approval_thresholds: Vec<f64>,
2080}
2081
2082fn default_approval_thresholds() -> Vec<f64> {
2083    vec![1000.0, 5000.0, 10000.0, 25000.0, 50000.0, 100000.0]
2084}
2085
2086fn default_fraud_rate() -> f64 {
2087    0.005
2088}
2089fn default_document_fraud_rate() -> Option<f64> {
2090    // v5.0.1: bumped 0.01 → 0.05 to deliver meaningful scheme-level
2091    // fraud propagation at typical line-level rates. The 1 % default
2092    // (set in v4.4.2 to restore `is_fraud_propagated > 0`) was too
2093    // conservative — at `fraud_rate = 0.08` it produced ~3.6 % observed
2094    // propagation against a 26.7 % target. The new 5 % default + the
2095    // additive formula `P(line is_fraud) ≈ fraud_rate + 0.3 × d` yields
2096    // ~9.5 % combined at fraud_rate=0.08 (closer to the spec target).
2097    // Set explicitly to `Some(0.0)` or `null` in YAML to disable, or to
2098    // a higher value (e.g. 0.20) for scheme-heavy fraud workloads.
2099    Some(0.05)
2100}
2101fn default_clustering_factor() -> f64 {
2102    3.0
2103}
2104
2105impl Default for FraudConfig {
2106    fn default() -> Self {
2107        Self {
2108            enabled: false,
2109            fraud_rate: default_fraud_rate(),
2110            document_fraud_rate: default_document_fraud_rate(),
2111            propagate_to_lines: true,
2112            propagate_to_document: true,
2113            fraud_type_distribution: FraudTypeDistribution::default(),
2114            clustering_enabled: false,
2115            clustering_factor: default_clustering_factor(),
2116            approval_thresholds: default_approval_thresholds(),
2117        }
2118    }
2119}
2120
2121/// Distribution of fraud types.
2122///
2123/// All fields default to `0.0` if absent from the YAML, so partial
2124/// distributions are accepted; the validator (`validate_sum_to_one`)
2125/// then enforces that the populated weights sum to `1.0 ± 0.01`.
2126#[derive(Debug, Clone, Serialize, Deserialize)]
2127#[serde(deny_unknown_fields)]
2128pub struct FraudTypeDistribution {
2129    #[serde(default)]
2130    pub suspense_account_abuse: f64,
2131    #[serde(default)]
2132    pub fictitious_transaction: f64,
2133    #[serde(default)]
2134    pub revenue_manipulation: f64,
2135    #[serde(default)]
2136    pub expense_capitalization: f64,
2137    #[serde(default)]
2138    pub split_transaction: f64,
2139    #[serde(default)]
2140    pub timing_anomaly: f64,
2141    #[serde(default)]
2142    pub unauthorized_access: f64,
2143    #[serde(default)]
2144    pub duplicate_payment: f64,
2145    /// Vendor kickback scheme.
2146    #[serde(default)]
2147    pub kickback_scheme: f64,
2148    /// Round-tripping funds through multiple entities or accounts.
2149    #[serde(default)]
2150    pub round_tripping: f64,
2151    /// Unauthorized customer/vendor discounts (sweethearting, side deals).
2152    #[serde(default)]
2153    pub unauthorized_discount: f64,
2154}
2155
2156impl Default for FraudTypeDistribution {
2157    fn default() -> Self {
2158        // Preserves the pre-extension default sum=1.0 over the original
2159        // eight fields.  The three additional fields (kickback_scheme,
2160        // round_tripping, unauthorized_discount) default to 0.0 so that
2161        // existing fraud packs / templates that explicitly enumerate the
2162        // original eight fields continue to merge to a 1.0 sum without
2163        // modification.  Users who want those fraud types must set them
2164        // explicitly (and rebalance the others).
2165        Self {
2166            suspense_account_abuse: 0.25,
2167            fictitious_transaction: 0.15,
2168            revenue_manipulation: 0.10,
2169            expense_capitalization: 0.10,
2170            split_transaction: 0.15,
2171            timing_anomaly: 0.10,
2172            unauthorized_access: 0.10,
2173            duplicate_payment: 0.05,
2174            kickback_scheme: 0.0,
2175            round_tripping: 0.0,
2176            unauthorized_discount: 0.0,
2177        }
2178    }
2179}
2180
2181/// Internal Controls System (ICS) configuration.
2182#[derive(Debug, Clone, Serialize, Deserialize)]
2183pub struct InternalControlsConfig {
2184    /// Enable internal controls system
2185    #[serde(default)]
2186    pub enabled: bool,
2187    /// Rate at which controls result in exceptions (0.0 - 1.0)
2188    #[serde(default = "default_exception_rate")]
2189    pub exception_rate: f64,
2190    /// Rate at which SoD violations occur (0.0 - 1.0)
2191    #[serde(default = "default_sod_violation_rate")]
2192    pub sod_violation_rate: f64,
2193    /// Export control master data to separate files
2194    #[serde(default = "default_true")]
2195    pub export_control_master_data: bool,
2196    /// SOX materiality threshold for marking transactions as SOX-relevant
2197    #[serde(default = "default_sox_materiality_threshold")]
2198    pub sox_materiality_threshold: f64,
2199    /// Enable COSO 2013 framework integration
2200    #[serde(default = "default_true")]
2201    pub coso_enabled: bool,
2202    /// Include entity-level controls in generation
2203    #[serde(default)]
2204    pub include_entity_level_controls: bool,
2205    /// Target maturity level for controls
2206    /// Valid values: "ad_hoc", "repeatable", "defined", "managed", "optimized", "mixed"
2207    #[serde(default = "default_target_maturity_level")]
2208    pub target_maturity_level: String,
2209}
2210
2211fn default_exception_rate() -> f64 {
2212    0.02
2213}
2214
2215fn default_sod_violation_rate() -> f64 {
2216    0.01
2217}
2218
2219fn default_sox_materiality_threshold() -> f64 {
2220    10000.0
2221}
2222
2223fn default_target_maturity_level() -> String {
2224    "mixed".to_string()
2225}
2226
2227impl Default for InternalControlsConfig {
2228    fn default() -> Self {
2229        Self {
2230            enabled: false,
2231            exception_rate: default_exception_rate(),
2232            sod_violation_rate: default_sod_violation_rate(),
2233            export_control_master_data: true,
2234            sox_materiality_threshold: default_sox_materiality_threshold(),
2235            coso_enabled: true,
2236            include_entity_level_controls: false,
2237            target_maturity_level: default_target_maturity_level(),
2238        }
2239    }
2240}
2241
2242/// Business process configuration.
2243#[derive(Debug, Clone, Serialize, Deserialize)]
2244pub struct BusinessProcessConfig {
2245    /// Order-to-Cash weight
2246    #[serde(default = "default_o2c")]
2247    pub o2c_weight: f64,
2248    /// Procure-to-Pay weight
2249    #[serde(default = "default_p2p")]
2250    pub p2p_weight: f64,
2251    /// Record-to-Report weight
2252    #[serde(default = "default_r2r")]
2253    pub r2r_weight: f64,
2254    /// Hire-to-Retire weight
2255    #[serde(default = "default_h2r")]
2256    pub h2r_weight: f64,
2257    /// Acquire-to-Retire weight
2258    #[serde(default = "default_a2r")]
2259    pub a2r_weight: f64,
2260}
2261
2262fn default_o2c() -> f64 {
2263    0.35
2264}
2265fn default_p2p() -> f64 {
2266    0.30
2267}
2268fn default_r2r() -> f64 {
2269    0.20
2270}
2271fn default_h2r() -> f64 {
2272    0.10
2273}
2274fn default_a2r() -> f64 {
2275    0.05
2276}
2277
2278impl Default for BusinessProcessConfig {
2279    fn default() -> Self {
2280        Self {
2281            o2c_weight: default_o2c(),
2282            p2p_weight: default_p2p(),
2283            r2r_weight: default_r2r(),
2284            h2r_weight: default_h2r(),
2285            a2r_weight: default_a2r(),
2286        }
2287    }
2288}
2289
2290/// User persona configuration.
2291#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2292pub struct UserPersonaConfig {
2293    /// Distribution of user personas
2294    #[serde(default)]
2295    pub persona_distribution: PersonaDistribution,
2296    /// Users per persona type
2297    #[serde(default)]
2298    pub users_per_persona: UsersPerPersona,
2299}
2300
2301/// Distribution of user personas for transaction generation.
2302#[derive(Debug, Clone, Serialize, Deserialize)]
2303pub struct PersonaDistribution {
2304    pub junior_accountant: f64,
2305    pub senior_accountant: f64,
2306    pub controller: f64,
2307    pub manager: f64,
2308    pub automated_system: f64,
2309}
2310
2311impl Default for PersonaDistribution {
2312    fn default() -> Self {
2313        Self {
2314            junior_accountant: 0.15,
2315            senior_accountant: 0.15,
2316            controller: 0.05,
2317            manager: 0.05,
2318            automated_system: 0.60,
2319        }
2320    }
2321}
2322
2323/// Number of users per persona type.
2324#[derive(Debug, Clone, Serialize, Deserialize)]
2325pub struct UsersPerPersona {
2326    pub junior_accountant: usize,
2327    pub senior_accountant: usize,
2328    pub controller: usize,
2329    pub manager: usize,
2330    pub automated_system: usize,
2331}
2332
2333impl Default for UsersPerPersona {
2334    fn default() -> Self {
2335        Self {
2336            junior_accountant: 10,
2337            senior_accountant: 5,
2338            controller: 2,
2339            manager: 3,
2340            automated_system: 20,
2341        }
2342    }
2343}
2344
2345/// Template configuration for realistic data generation.
2346///
2347/// # User-supplied template packs (v3.2.0+)
2348///
2349/// Set `path` to a directory (or single YAML/JSON file) to override or
2350/// extend the embedded default pools for vendor names, customer names,
2351/// material/asset descriptions, audit findings, bank names, and
2352/// department names. When `path` is `None` (the default), generators
2353/// use the compiled-in pools and output is byte-identical to v3.1.2.
2354///
2355/// See `crates/datasynth-core/src/templates/loader.rs::TemplateData`
2356/// for the full YAML schema. Use `datasynth-data templates export` to
2357/// dump the defaults as a starter pack.
2358#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2359pub struct TemplateConfig {
2360    /// Name generation settings
2361    #[serde(default)]
2362    pub names: NameTemplateConfig,
2363    /// Description generation settings
2364    #[serde(default)]
2365    pub descriptions: DescriptionTemplateConfig,
2366    /// Reference number settings
2367    #[serde(default)]
2368    pub references: ReferenceTemplateConfig,
2369    /// Optional path to a user-supplied template file or directory.
2370    /// When set, entries from the file(s) augment or replace the
2371    /// embedded defaults according to `merge_strategy`.
2372    ///
2373    /// `None` (default) = use embedded pools only (byte-identical to v3.1.2).
2374    #[serde(default, alias = "templatesPath")]
2375    pub path: Option<std::path::PathBuf>,
2376    /// How file-based entries combine with embedded defaults.
2377    ///
2378    /// - `extend` (default): append file entries to embedded pools,
2379    ///   de-duplicating. Safe for incremental overlays.
2380    /// - `replace`: discard embedded pools entirely and use only file
2381    ///   entries. Requires a fully-populated template file.
2382    /// - `merge_prefer_file`: replace individual categories when present
2383    ///   in the file; keep embedded for absent categories.
2384    #[serde(default, alias = "mergeStrategy")]
2385    pub merge_strategy: TemplateMergeStrategy,
2386}
2387
2388/// Strategy for combining user-supplied template files with embedded defaults.
2389#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
2390#[serde(rename_all = "snake_case")]
2391pub enum TemplateMergeStrategy {
2392    /// Append file entries to embedded pools (default).
2393    #[default]
2394    Extend,
2395    /// Replace embedded pools entirely with file entries.
2396    Replace,
2397    /// Replace individual categories when present in file; keep embedded for absent ones.
2398    MergePreferFile,
2399}
2400
2401/// Name template configuration.
2402#[derive(Debug, Clone, Serialize, Deserialize)]
2403pub struct NameTemplateConfig {
2404    /// Distribution of name cultures
2405    #[serde(default)]
2406    pub culture_distribution: CultureDistribution,
2407    /// Email domain for generated users
2408    #[serde(default = "default_email_domain")]
2409    pub email_domain: String,
2410    /// Generate realistic display names
2411    #[serde(default = "default_true")]
2412    pub generate_realistic_names: bool,
2413}
2414
2415fn default_email_domain() -> String {
2416    "company.com".to_string()
2417}
2418
2419impl Default for NameTemplateConfig {
2420    fn default() -> Self {
2421        Self {
2422            culture_distribution: CultureDistribution::default(),
2423            email_domain: default_email_domain(),
2424            generate_realistic_names: true,
2425        }
2426    }
2427}
2428
2429/// Distribution of name cultures for generation.
2430#[derive(Debug, Clone, Serialize, Deserialize)]
2431pub struct CultureDistribution {
2432    pub western_us: f64,
2433    pub hispanic: f64,
2434    pub german: f64,
2435    pub french: f64,
2436    pub chinese: f64,
2437    pub japanese: f64,
2438    pub indian: f64,
2439}
2440
2441impl Default for CultureDistribution {
2442    fn default() -> Self {
2443        Self {
2444            western_us: 0.40,
2445            hispanic: 0.20,
2446            german: 0.10,
2447            french: 0.05,
2448            chinese: 0.10,
2449            japanese: 0.05,
2450            indian: 0.10,
2451        }
2452    }
2453}
2454
2455/// Description template configuration.
2456#[derive(Debug, Clone, Serialize, Deserialize)]
2457pub struct DescriptionTemplateConfig {
2458    /// Generate header text for journal entries
2459    #[serde(default = "default_true")]
2460    pub generate_header_text: bool,
2461    /// Generate line text for journal entry lines
2462    #[serde(default = "default_true")]
2463    pub generate_line_text: bool,
2464}
2465
2466impl Default for DescriptionTemplateConfig {
2467    fn default() -> Self {
2468        Self {
2469            generate_header_text: true,
2470            generate_line_text: true,
2471        }
2472    }
2473}
2474
2475/// Reference number template configuration.
2476#[derive(Debug, Clone, Serialize, Deserialize)]
2477pub struct ReferenceTemplateConfig {
2478    /// Generate reference numbers
2479    #[serde(default = "default_true")]
2480    pub generate_references: bool,
2481    /// Invoice prefix
2482    #[serde(default = "default_invoice_prefix")]
2483    pub invoice_prefix: String,
2484    /// Purchase order prefix
2485    #[serde(default = "default_po_prefix")]
2486    pub po_prefix: String,
2487    /// Sales order prefix
2488    #[serde(default = "default_so_prefix")]
2489    pub so_prefix: String,
2490}
2491
2492fn default_invoice_prefix() -> String {
2493    "INV".to_string()
2494}
2495fn default_po_prefix() -> String {
2496    "PO".to_string()
2497}
2498fn default_so_prefix() -> String {
2499    "SO".to_string()
2500}
2501
2502impl Default for ReferenceTemplateConfig {
2503    fn default() -> Self {
2504        Self {
2505            generate_references: true,
2506            invoice_prefix: default_invoice_prefix(),
2507            po_prefix: default_po_prefix(),
2508            so_prefix: default_so_prefix(),
2509        }
2510    }
2511}
2512
2513/// Approval workflow configuration.
2514#[derive(Debug, Clone, Serialize, Deserialize)]
2515pub struct ApprovalConfig {
2516    /// Enable approval workflow generation
2517    #[serde(default)]
2518    pub enabled: bool,
2519    /// Threshold below which transactions are auto-approved
2520    #[serde(default = "default_auto_approve_threshold")]
2521    pub auto_approve_threshold: f64,
2522    /// Rate at which approvals are rejected (0.0 to 1.0)
2523    #[serde(default = "default_rejection_rate")]
2524    pub rejection_rate: f64,
2525    /// Rate at which approvals require revision (0.0 to 1.0)
2526    #[serde(default = "default_revision_rate")]
2527    pub revision_rate: f64,
2528    /// Average delay in hours for approval processing
2529    #[serde(default = "default_approval_delay_hours")]
2530    pub average_approval_delay_hours: f64,
2531    /// Approval chain thresholds
2532    #[serde(default)]
2533    pub thresholds: Vec<ApprovalThresholdConfig>,
2534}
2535
2536fn default_auto_approve_threshold() -> f64 {
2537    1000.0
2538}
2539fn default_rejection_rate() -> f64 {
2540    0.02
2541}
2542fn default_revision_rate() -> f64 {
2543    0.05
2544}
2545fn default_approval_delay_hours() -> f64 {
2546    4.0
2547}
2548
2549impl Default for ApprovalConfig {
2550    fn default() -> Self {
2551        Self {
2552            enabled: false,
2553            auto_approve_threshold: default_auto_approve_threshold(),
2554            rejection_rate: default_rejection_rate(),
2555            revision_rate: default_revision_rate(),
2556            average_approval_delay_hours: default_approval_delay_hours(),
2557            thresholds: vec![
2558                ApprovalThresholdConfig {
2559                    amount: 1000.0,
2560                    level: 1,
2561                    roles: vec!["senior_accountant".to_string()],
2562                },
2563                ApprovalThresholdConfig {
2564                    amount: 10000.0,
2565                    level: 2,
2566                    roles: vec!["senior_accountant".to_string(), "controller".to_string()],
2567                },
2568                ApprovalThresholdConfig {
2569                    amount: 100000.0,
2570                    level: 3,
2571                    roles: vec![
2572                        "senior_accountant".to_string(),
2573                        "controller".to_string(),
2574                        "manager".to_string(),
2575                    ],
2576                },
2577                ApprovalThresholdConfig {
2578                    amount: 500000.0,
2579                    level: 4,
2580                    roles: vec![
2581                        "senior_accountant".to_string(),
2582                        "controller".to_string(),
2583                        "manager".to_string(),
2584                        "executive".to_string(),
2585                    ],
2586                },
2587            ],
2588        }
2589    }
2590}
2591
2592/// Configuration for a single approval threshold.
2593#[derive(Debug, Clone, Serialize, Deserialize)]
2594pub struct ApprovalThresholdConfig {
2595    /// Amount threshold
2596    pub amount: f64,
2597    /// Approval level required
2598    pub level: u8,
2599    /// Roles that can approve at this level
2600    pub roles: Vec<String>,
2601}
2602
2603/// Department configuration.
2604#[derive(Debug, Clone, Serialize, Deserialize)]
2605pub struct DepartmentConfig {
2606    /// Enable department assignment
2607    #[serde(default)]
2608    pub enabled: bool,
2609    /// Multiplier for department headcounts
2610    #[serde(default = "default_headcount_multiplier")]
2611    pub headcount_multiplier: f64,
2612    /// Custom department definitions (optional)
2613    #[serde(default)]
2614    pub custom_departments: Vec<CustomDepartmentConfig>,
2615}
2616
2617fn default_headcount_multiplier() -> f64 {
2618    1.0
2619}
2620
2621impl Default for DepartmentConfig {
2622    fn default() -> Self {
2623        Self {
2624            enabled: false,
2625            headcount_multiplier: default_headcount_multiplier(),
2626            custom_departments: Vec::new(),
2627        }
2628    }
2629}
2630
2631/// Custom department definition.
2632#[derive(Debug, Clone, Serialize, Deserialize)]
2633pub struct CustomDepartmentConfig {
2634    /// Department code
2635    pub code: String,
2636    /// Department name
2637    pub name: String,
2638    /// Associated cost center
2639    #[serde(default)]
2640    pub cost_center: Option<String>,
2641    /// Primary business processes
2642    #[serde(default)]
2643    pub primary_processes: Vec<String>,
2644    /// Parent department code
2645    #[serde(default)]
2646    pub parent_code: Option<String>,
2647}
2648
2649// ============================================================================
2650// Master Data Configuration
2651// ============================================================================
2652
2653/// Master data generation configuration.
2654#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2655pub struct MasterDataConfig {
2656    /// Vendor master data settings
2657    #[serde(default)]
2658    pub vendors: VendorMasterConfig,
2659    /// Customer master data settings
2660    #[serde(default)]
2661    pub customers: CustomerMasterConfig,
2662    /// Material master data settings
2663    #[serde(default)]
2664    pub materials: MaterialMasterConfig,
2665    /// Fixed asset master data settings
2666    #[serde(default)]
2667    pub fixed_assets: FixedAssetMasterConfig,
2668    /// Employee master data settings
2669    #[serde(default)]
2670    pub employees: EmployeeMasterConfig,
2671    /// Cost center master data settings
2672    #[serde(default)]
2673    pub cost_centers: CostCenterMasterConfig,
2674}
2675
2676/// Vendor master data configuration.
2677#[derive(Debug, Clone, Serialize, Deserialize)]
2678pub struct VendorMasterConfig {
2679    /// Number of vendors to generate
2680    #[serde(default = "default_vendor_count")]
2681    pub count: usize,
2682    /// Percentage of vendors that are intercompany (0.0 to 1.0)
2683    #[serde(default = "default_intercompany_percent")]
2684    pub intercompany_percent: f64,
2685    /// Payment terms distribution
2686    #[serde(default)]
2687    pub payment_terms_distribution: PaymentTermsDistribution,
2688    /// Vendor behavior distribution
2689    #[serde(default)]
2690    pub behavior_distribution: VendorBehaviorDistribution,
2691    /// Generate bank account details
2692    #[serde(default = "default_true")]
2693    pub generate_bank_accounts: bool,
2694    /// Generate tax IDs
2695    #[serde(default = "default_true")]
2696    pub generate_tax_ids: bool,
2697}
2698
2699fn default_vendor_count() -> usize {
2700    500
2701}
2702
2703fn default_intercompany_percent() -> f64 {
2704    0.05
2705}
2706
2707impl Default for VendorMasterConfig {
2708    fn default() -> Self {
2709        Self {
2710            count: default_vendor_count(),
2711            intercompany_percent: default_intercompany_percent(),
2712            payment_terms_distribution: PaymentTermsDistribution::default(),
2713            behavior_distribution: VendorBehaviorDistribution::default(),
2714            generate_bank_accounts: true,
2715            generate_tax_ids: true,
2716        }
2717    }
2718}
2719
2720/// Payment terms distribution for vendors.
2721#[derive(Debug, Clone, Serialize, Deserialize)]
2722pub struct PaymentTermsDistribution {
2723    /// Net 30 days
2724    pub net_30: f64,
2725    /// Net 60 days
2726    pub net_60: f64,
2727    /// Net 90 days
2728    pub net_90: f64,
2729    /// 2% 10 Net 30 (early payment discount)
2730    pub two_ten_net_30: f64,
2731    /// Due on receipt
2732    pub due_on_receipt: f64,
2733    /// End of month
2734    pub end_of_month: f64,
2735}
2736
2737impl Default for PaymentTermsDistribution {
2738    fn default() -> Self {
2739        Self {
2740            net_30: 0.40,
2741            net_60: 0.20,
2742            net_90: 0.10,
2743            two_ten_net_30: 0.15,
2744            due_on_receipt: 0.05,
2745            end_of_month: 0.10,
2746        }
2747    }
2748}
2749
2750/// Vendor behavior distribution.
2751///
2752/// All fields default to `0.0` if absent from the YAML, so partial
2753/// distributions are accepted; the validator (`validate_sum_to_one`)
2754/// then enforces that the populated weights sum to `1.0 ± 0.01`.
2755#[derive(Debug, Clone, Serialize, Deserialize)]
2756#[serde(deny_unknown_fields)]
2757pub struct VendorBehaviorDistribution {
2758    /// Reliable vendors (consistent delivery, quality)
2759    #[serde(default)]
2760    pub reliable: f64,
2761    /// Sometimes late vendors
2762    #[serde(default)]
2763    pub sometimes_late: f64,
2764    /// Inconsistent quality vendors
2765    #[serde(default)]
2766    pub inconsistent_quality: f64,
2767    /// Premium vendors (high quality, premium pricing)
2768    #[serde(default)]
2769    pub premium: f64,
2770    /// Budget vendors (lower quality, lower pricing)
2771    #[serde(default)]
2772    pub budget: f64,
2773    /// Erratic vendors (variable behavior, unpredictable performance)
2774    #[serde(default)]
2775    pub erratic: f64,
2776    /// Problematic vendors (frequent issues, high risk for fraud scenarios)
2777    #[serde(default)]
2778    pub problematic: f64,
2779}
2780
2781impl Default for VendorBehaviorDistribution {
2782    fn default() -> Self {
2783        // Preserves the pre-extension default sum=1.0 over the original
2784        // five fields.  `erratic` and `problematic` default to 0.0 so
2785        // that existing configs/packs continue to merge to a 1.0 sum
2786        // without modification.
2787        Self {
2788            reliable: 0.50,
2789            sometimes_late: 0.20,
2790            inconsistent_quality: 0.10,
2791            premium: 0.10,
2792            budget: 0.10,
2793            erratic: 0.0,
2794            problematic: 0.0,
2795        }
2796    }
2797}
2798
2799/// Customer master data configuration.
2800#[derive(Debug, Clone, Serialize, Deserialize)]
2801pub struct CustomerMasterConfig {
2802    /// Number of customers to generate
2803    #[serde(default = "default_customer_count")]
2804    pub count: usize,
2805    /// Percentage of customers that are intercompany (0.0 to 1.0)
2806    #[serde(default = "default_intercompany_percent")]
2807    pub intercompany_percent: f64,
2808    /// Credit rating distribution
2809    #[serde(default)]
2810    pub credit_rating_distribution: CreditRatingDistribution,
2811    /// Payment behavior distribution
2812    #[serde(default)]
2813    pub payment_behavior_distribution: PaymentBehaviorDistribution,
2814    /// Generate credit limits based on rating
2815    #[serde(default = "default_true")]
2816    pub generate_credit_limits: bool,
2817}
2818
2819fn default_customer_count() -> usize {
2820    2000
2821}
2822
2823impl Default for CustomerMasterConfig {
2824    fn default() -> Self {
2825        Self {
2826            count: default_customer_count(),
2827            intercompany_percent: default_intercompany_percent(),
2828            credit_rating_distribution: CreditRatingDistribution::default(),
2829            payment_behavior_distribution: PaymentBehaviorDistribution::default(),
2830            generate_credit_limits: true,
2831        }
2832    }
2833}
2834
2835/// Credit rating distribution for customers.
2836///
2837/// Two parallel vocabularies are accepted:
2838///   * Bond-grade tiers: `aaa`, `aa`, `a`, `bbb`, `bb`, `b`, `below_b`
2839///   * Plain-English tiers: `excellent`, `good`, `fair`, `poor`
2840///
2841/// All fields default to `0.0` if absent; mix and match as needed.
2842/// The validator enforces that the populated weights sum to `1.0`.
2843#[derive(Debug, Clone, Serialize, Deserialize)]
2844#[serde(deny_unknown_fields)]
2845pub struct CreditRatingDistribution {
2846    /// AAA rating
2847    #[serde(default)]
2848    pub aaa: f64,
2849    /// AA rating
2850    #[serde(default)]
2851    pub aa: f64,
2852    /// A rating
2853    #[serde(default)]
2854    pub a: f64,
2855    /// BBB rating
2856    #[serde(default)]
2857    pub bbb: f64,
2858    /// BB rating
2859    #[serde(default)]
2860    pub bb: f64,
2861    /// B rating
2862    #[serde(default)]
2863    pub b: f64,
2864    /// Below B rating
2865    #[serde(default)]
2866    pub below_b: f64,
2867    /// Plain-English: excellent credit (≈ AAA/AA tier)
2868    #[serde(default)]
2869    pub excellent: f64,
2870    /// Plain-English: good credit (≈ A tier)
2871    #[serde(default)]
2872    pub good: f64,
2873    /// Plain-English: fair credit (≈ BBB/BB tier)
2874    #[serde(default)]
2875    pub fair: f64,
2876    /// Plain-English: poor credit (≈ B/below tier)
2877    #[serde(default)]
2878    pub poor: f64,
2879}
2880
2881impl Default for CreditRatingDistribution {
2882    fn default() -> Self {
2883        Self {
2884            aaa: 0.05,
2885            aa: 0.10,
2886            a: 0.20,
2887            bbb: 0.30,
2888            bb: 0.20,
2889            b: 0.10,
2890            below_b: 0.05,
2891            excellent: 0.0,
2892            good: 0.0,
2893            fair: 0.0,
2894            poor: 0.0,
2895        }
2896    }
2897}
2898
2899/// Payment behavior distribution for customers.
2900///
2901/// All fields default to `0.0` if absent from the YAML.  Validator
2902/// enforces that populated weights sum to `1.0 ± 0.01`.
2903#[derive(Debug, Clone, Serialize, Deserialize)]
2904#[serde(deny_unknown_fields)]
2905pub struct PaymentBehaviorDistribution {
2906    /// Always pays early
2907    #[serde(default)]
2908    pub early_payer: f64,
2909    /// Pays on time
2910    #[serde(default)]
2911    pub on_time: f64,
2912    /// Occasionally late
2913    #[serde(default)]
2914    pub occasional_late: f64,
2915    /// Frequently late
2916    #[serde(default)]
2917    pub frequent_late: f64,
2918    /// Takes early payment discounts
2919    #[serde(default)]
2920    pub discount_taker: f64,
2921}
2922
2923impl Default for PaymentBehaviorDistribution {
2924    fn default() -> Self {
2925        Self {
2926            early_payer: 0.10,
2927            on_time: 0.50,
2928            occasional_late: 0.25,
2929            frequent_late: 0.10,
2930            discount_taker: 0.05,
2931        }
2932    }
2933}
2934
2935/// Material master data configuration.
2936#[derive(Debug, Clone, Serialize, Deserialize)]
2937pub struct MaterialMasterConfig {
2938    /// Number of materials to generate
2939    #[serde(default = "default_material_count")]
2940    pub count: usize,
2941    /// Material type distribution
2942    #[serde(default)]
2943    pub type_distribution: MaterialTypeDistribution,
2944    /// Valuation method distribution
2945    #[serde(default)]
2946    pub valuation_distribution: ValuationMethodDistribution,
2947    /// Percentage of materials with BOM (bill of materials)
2948    #[serde(default = "default_bom_percent")]
2949    pub bom_percent: f64,
2950    /// Maximum BOM depth
2951    #[serde(default = "default_max_bom_depth")]
2952    pub max_bom_depth: u8,
2953}
2954
2955fn default_material_count() -> usize {
2956    5000
2957}
2958
2959fn default_bom_percent() -> f64 {
2960    0.20
2961}
2962
2963fn default_max_bom_depth() -> u8 {
2964    3
2965}
2966
2967impl Default for MaterialMasterConfig {
2968    fn default() -> Self {
2969        Self {
2970            count: default_material_count(),
2971            type_distribution: MaterialTypeDistribution::default(),
2972            valuation_distribution: ValuationMethodDistribution::default(),
2973            bom_percent: default_bom_percent(),
2974            max_bom_depth: default_max_bom_depth(),
2975        }
2976    }
2977}
2978
2979/// Material type distribution.
2980#[derive(Debug, Clone, Serialize, Deserialize)]
2981pub struct MaterialTypeDistribution {
2982    /// Raw materials
2983    pub raw_material: f64,
2984    /// Semi-finished goods
2985    pub semi_finished: f64,
2986    /// Finished goods
2987    pub finished_good: f64,
2988    /// Trading goods (purchased for resale)
2989    pub trading_good: f64,
2990    /// Operating supplies
2991    pub operating_supply: f64,
2992    /// Services
2993    pub service: f64,
2994}
2995
2996impl Default for MaterialTypeDistribution {
2997    fn default() -> Self {
2998        Self {
2999            raw_material: 0.30,
3000            semi_finished: 0.15,
3001            finished_good: 0.25,
3002            trading_good: 0.15,
3003            operating_supply: 0.10,
3004            service: 0.05,
3005        }
3006    }
3007}
3008
3009/// Valuation method distribution for materials.
3010#[derive(Debug, Clone, Serialize, Deserialize)]
3011pub struct ValuationMethodDistribution {
3012    /// Standard cost
3013    pub standard_cost: f64,
3014    /// Moving average
3015    pub moving_average: f64,
3016    /// FIFO (First In, First Out)
3017    pub fifo: f64,
3018    /// LIFO (Last In, First Out)
3019    pub lifo: f64,
3020}
3021
3022impl Default for ValuationMethodDistribution {
3023    fn default() -> Self {
3024        Self {
3025            standard_cost: 0.50,
3026            moving_average: 0.30,
3027            fifo: 0.15,
3028            lifo: 0.05,
3029        }
3030    }
3031}
3032
3033/// Fixed asset master data configuration.
3034#[derive(Debug, Clone, Serialize, Deserialize)]
3035pub struct FixedAssetMasterConfig {
3036    /// Number of fixed assets to generate
3037    #[serde(default = "default_asset_count")]
3038    pub count: usize,
3039    /// Asset class distribution
3040    #[serde(default)]
3041    pub class_distribution: AssetClassDistribution,
3042    /// Depreciation method distribution
3043    #[serde(default)]
3044    pub depreciation_distribution: DepreciationMethodDistribution,
3045    /// Percentage of assets that are fully depreciated
3046    #[serde(default = "default_fully_depreciated_percent")]
3047    pub fully_depreciated_percent: f64,
3048    /// Generate acquisition history
3049    #[serde(default = "default_true")]
3050    pub generate_acquisition_history: bool,
3051}
3052
3053fn default_asset_count() -> usize {
3054    800
3055}
3056
3057fn default_fully_depreciated_percent() -> f64 {
3058    0.15
3059}
3060
3061impl Default for FixedAssetMasterConfig {
3062    fn default() -> Self {
3063        Self {
3064            count: default_asset_count(),
3065            class_distribution: AssetClassDistribution::default(),
3066            depreciation_distribution: DepreciationMethodDistribution::default(),
3067            fully_depreciated_percent: default_fully_depreciated_percent(),
3068            generate_acquisition_history: true,
3069        }
3070    }
3071}
3072
3073/// Asset class distribution.
3074#[derive(Debug, Clone, Serialize, Deserialize)]
3075pub struct AssetClassDistribution {
3076    /// Buildings and structures
3077    pub buildings: f64,
3078    /// Machinery and equipment
3079    pub machinery: f64,
3080    /// Vehicles
3081    pub vehicles: f64,
3082    /// IT equipment
3083    pub it_equipment: f64,
3084    /// Furniture and fixtures
3085    pub furniture: f64,
3086    /// Land (non-depreciable)
3087    pub land: f64,
3088    /// Leasehold improvements
3089    pub leasehold: f64,
3090}
3091
3092impl Default for AssetClassDistribution {
3093    fn default() -> Self {
3094        Self {
3095            buildings: 0.15,
3096            machinery: 0.30,
3097            vehicles: 0.15,
3098            it_equipment: 0.20,
3099            furniture: 0.10,
3100            land: 0.05,
3101            leasehold: 0.05,
3102        }
3103    }
3104}
3105
3106/// Depreciation method distribution.
3107#[derive(Debug, Clone, Serialize, Deserialize)]
3108pub struct DepreciationMethodDistribution {
3109    /// Straight line
3110    pub straight_line: f64,
3111    /// Declining balance
3112    pub declining_balance: f64,
3113    /// Double declining balance
3114    pub double_declining: f64,
3115    /// Sum of years' digits
3116    pub sum_of_years: f64,
3117    /// Units of production
3118    pub units_of_production: f64,
3119}
3120
3121impl Default for DepreciationMethodDistribution {
3122    fn default() -> Self {
3123        Self {
3124            straight_line: 0.60,
3125            declining_balance: 0.20,
3126            double_declining: 0.10,
3127            sum_of_years: 0.05,
3128            units_of_production: 0.05,
3129        }
3130    }
3131}
3132
3133/// Employee master data configuration.
3134#[derive(Debug, Clone, Serialize, Deserialize)]
3135pub struct EmployeeMasterConfig {
3136    /// Number of employees to generate
3137    #[serde(default = "default_employee_count")]
3138    pub count: usize,
3139    /// Generate organizational hierarchy
3140    #[serde(default = "default_true")]
3141    pub generate_hierarchy: bool,
3142    /// Maximum hierarchy depth
3143    #[serde(default = "default_hierarchy_depth")]
3144    pub max_hierarchy_depth: u8,
3145    /// Average span of control (direct reports per manager)
3146    #[serde(default = "default_span_of_control")]
3147    pub average_span_of_control: f64,
3148    /// Approval limit distribution by job level
3149    #[serde(default)]
3150    pub approval_limits: ApprovalLimitDistribution,
3151    /// Department distribution
3152    #[serde(default)]
3153    pub department_distribution: EmployeeDepartmentDistribution,
3154}
3155
3156fn default_employee_count() -> usize {
3157    1500
3158}
3159
3160fn default_hierarchy_depth() -> u8 {
3161    6
3162}
3163
3164fn default_span_of_control() -> f64 {
3165    5.0
3166}
3167
3168impl Default for EmployeeMasterConfig {
3169    fn default() -> Self {
3170        Self {
3171            count: default_employee_count(),
3172            generate_hierarchy: true,
3173            max_hierarchy_depth: default_hierarchy_depth(),
3174            average_span_of_control: default_span_of_control(),
3175            approval_limits: ApprovalLimitDistribution::default(),
3176            department_distribution: EmployeeDepartmentDistribution::default(),
3177        }
3178    }
3179}
3180
3181/// Approval limit distribution by job level.
3182#[derive(Debug, Clone, Serialize, Deserialize)]
3183pub struct ApprovalLimitDistribution {
3184    /// Staff level approval limit
3185    #[serde(default = "default_staff_limit")]
3186    pub staff: f64,
3187    /// Senior staff approval limit
3188    #[serde(default = "default_senior_limit")]
3189    pub senior: f64,
3190    /// Manager approval limit
3191    #[serde(default = "default_manager_limit")]
3192    pub manager: f64,
3193    /// Director approval limit
3194    #[serde(default = "default_director_limit")]
3195    pub director: f64,
3196    /// VP approval limit
3197    #[serde(default = "default_vp_limit")]
3198    pub vp: f64,
3199    /// Executive approval limit
3200    #[serde(default = "default_executive_limit")]
3201    pub executive: f64,
3202}
3203
3204fn default_staff_limit() -> f64 {
3205    1000.0
3206}
3207fn default_senior_limit() -> f64 {
3208    5000.0
3209}
3210fn default_manager_limit() -> f64 {
3211    25000.0
3212}
3213fn default_director_limit() -> f64 {
3214    100000.0
3215}
3216fn default_vp_limit() -> f64 {
3217    500000.0
3218}
3219fn default_executive_limit() -> f64 {
3220    f64::INFINITY
3221}
3222
3223impl Default for ApprovalLimitDistribution {
3224    fn default() -> Self {
3225        Self {
3226            staff: default_staff_limit(),
3227            senior: default_senior_limit(),
3228            manager: default_manager_limit(),
3229            director: default_director_limit(),
3230            vp: default_vp_limit(),
3231            executive: default_executive_limit(),
3232        }
3233    }
3234}
3235
3236/// Employee distribution across departments.
3237#[derive(Debug, Clone, Serialize, Deserialize)]
3238pub struct EmployeeDepartmentDistribution {
3239    /// Finance and Accounting
3240    pub finance: f64,
3241    /// Procurement
3242    pub procurement: f64,
3243    /// Sales
3244    pub sales: f64,
3245    /// Warehouse and Logistics
3246    pub warehouse: f64,
3247    /// IT
3248    pub it: f64,
3249    /// Human Resources
3250    pub hr: f64,
3251    /// Operations
3252    pub operations: f64,
3253    /// Executive
3254    pub executive: f64,
3255}
3256
3257impl Default for EmployeeDepartmentDistribution {
3258    fn default() -> Self {
3259        Self {
3260            finance: 0.12,
3261            procurement: 0.10,
3262            sales: 0.25,
3263            warehouse: 0.15,
3264            it: 0.10,
3265            hr: 0.05,
3266            operations: 0.20,
3267            executive: 0.03,
3268        }
3269    }
3270}
3271
3272/// Cost center master data configuration.
3273#[derive(Debug, Clone, Serialize, Deserialize)]
3274pub struct CostCenterMasterConfig {
3275    /// Number of cost centers to generate
3276    #[serde(default = "default_cost_center_count")]
3277    pub count: usize,
3278    /// Generate cost center hierarchy
3279    #[serde(default = "default_true")]
3280    pub generate_hierarchy: bool,
3281    /// Maximum hierarchy depth
3282    #[serde(default = "default_cc_hierarchy_depth")]
3283    pub max_hierarchy_depth: u8,
3284}
3285
3286fn default_cost_center_count() -> usize {
3287    50
3288}
3289
3290fn default_cc_hierarchy_depth() -> u8 {
3291    3
3292}
3293
3294impl Default for CostCenterMasterConfig {
3295    fn default() -> Self {
3296        Self {
3297            count: default_cost_center_count(),
3298            generate_hierarchy: true,
3299            max_hierarchy_depth: default_cc_hierarchy_depth(),
3300        }
3301    }
3302}
3303
3304// ============================================================================
3305// Document Flow Configuration
3306// ============================================================================
3307
3308/// Document flow generation configuration.
3309#[derive(Debug, Clone, Serialize, Deserialize)]
3310pub struct DocumentFlowConfig {
3311    /// P2P (Procure-to-Pay) flow configuration
3312    #[serde(default)]
3313    pub p2p: P2PFlowConfig,
3314    /// O2C (Order-to-Cash) flow configuration
3315    #[serde(default)]
3316    pub o2c: O2CFlowConfig,
3317    /// Generate document reference chains
3318    #[serde(default = "default_true")]
3319    pub generate_document_references: bool,
3320    /// Export document flow graph
3321    #[serde(default)]
3322    pub export_flow_graph: bool,
3323}
3324
3325impl Default for DocumentFlowConfig {
3326    fn default() -> Self {
3327        Self {
3328            p2p: P2PFlowConfig::default(),
3329            o2c: O2CFlowConfig::default(),
3330            generate_document_references: true,
3331            export_flow_graph: false,
3332        }
3333    }
3334}
3335
3336/// P2P (Procure-to-Pay) flow configuration.
3337#[derive(Debug, Clone, Serialize, Deserialize)]
3338pub struct P2PFlowConfig {
3339    /// Enable P2P document flow generation
3340    #[serde(default = "default_true")]
3341    pub enabled: bool,
3342    /// Three-way match success rate (PO-GR-Invoice)
3343    #[serde(default = "default_three_way_match_rate")]
3344    pub three_way_match_rate: f64,
3345    /// Rate of partial deliveries
3346    #[serde(default = "default_partial_delivery_rate")]
3347    pub partial_delivery_rate: f64,
3348    /// Rate of price variances between PO and Invoice
3349    #[serde(default = "default_price_variance_rate")]
3350    pub price_variance_rate: f64,
3351    /// Maximum price variance percentage
3352    #[serde(default = "default_max_price_variance")]
3353    pub max_price_variance_percent: f64,
3354    /// Rate of quantity variances between PO/GR and Invoice
3355    #[serde(default = "default_quantity_variance_rate")]
3356    pub quantity_variance_rate: f64,
3357    /// Average days from PO to goods receipt
3358    #[serde(default = "default_po_to_gr_days")]
3359    pub average_po_to_gr_days: u32,
3360    /// Average days from GR to invoice
3361    #[serde(default = "default_gr_to_invoice_days")]
3362    pub average_gr_to_invoice_days: u32,
3363    /// Average days from invoice to payment
3364    #[serde(default = "default_invoice_to_payment_days")]
3365    pub average_invoice_to_payment_days: u32,
3366    /// PO line count distribution
3367    #[serde(default)]
3368    pub line_count_distribution: DocumentLineCountDistribution,
3369    /// Payment behavior configuration
3370    #[serde(default)]
3371    pub payment_behavior: P2PPaymentBehaviorConfig,
3372    /// Rate of over-deliveries (quantity received exceeds PO quantity)
3373    #[serde(default)]
3374    pub over_delivery_rate: Option<f64>,
3375    /// Rate of early payment discounts being taken
3376    #[serde(default)]
3377    pub early_payment_discount_rate: Option<f64>,
3378}
3379
3380fn default_three_way_match_rate() -> f64 {
3381    0.95
3382}
3383
3384fn default_partial_delivery_rate() -> f64 {
3385    0.15
3386}
3387
3388fn default_price_variance_rate() -> f64 {
3389    0.08
3390}
3391
3392fn default_max_price_variance() -> f64 {
3393    0.05
3394}
3395
3396fn default_quantity_variance_rate() -> f64 {
3397    0.05
3398}
3399
3400fn default_po_to_gr_days() -> u32 {
3401    14
3402}
3403
3404fn default_gr_to_invoice_days() -> u32 {
3405    5
3406}
3407
3408fn default_invoice_to_payment_days() -> u32 {
3409    30
3410}
3411
3412impl Default for P2PFlowConfig {
3413    fn default() -> Self {
3414        Self {
3415            enabled: true,
3416            three_way_match_rate: default_three_way_match_rate(),
3417            partial_delivery_rate: default_partial_delivery_rate(),
3418            price_variance_rate: default_price_variance_rate(),
3419            max_price_variance_percent: default_max_price_variance(),
3420            quantity_variance_rate: default_quantity_variance_rate(),
3421            average_po_to_gr_days: default_po_to_gr_days(),
3422            average_gr_to_invoice_days: default_gr_to_invoice_days(),
3423            average_invoice_to_payment_days: default_invoice_to_payment_days(),
3424            line_count_distribution: DocumentLineCountDistribution::default(),
3425            payment_behavior: P2PPaymentBehaviorConfig::default(),
3426            over_delivery_rate: None,
3427            early_payment_discount_rate: None,
3428        }
3429    }
3430}
3431
3432// ============================================================================
3433// P2P Payment Behavior Configuration
3434// ============================================================================
3435
3436/// P2P payment behavior configuration.
3437#[derive(Debug, Clone, Serialize, Deserialize)]
3438pub struct P2PPaymentBehaviorConfig {
3439    /// Rate of late payments (beyond due date)
3440    #[serde(default = "default_p2p_late_payment_rate")]
3441    pub late_payment_rate: f64,
3442    /// Distribution of late payment days
3443    #[serde(default)]
3444    pub late_payment_days_distribution: LatePaymentDaysDistribution,
3445    /// Rate of partial payments
3446    #[serde(default = "default_p2p_partial_payment_rate")]
3447    pub partial_payment_rate: f64,
3448    /// Rate of payment corrections (NSF, chargebacks, reversals)
3449    #[serde(default = "default_p2p_payment_correction_rate")]
3450    pub payment_correction_rate: f64,
3451    /// Average days until partial payment remainder is paid
3452    #[serde(default = "default_p2p_avg_days_until_remainder")]
3453    pub avg_days_until_remainder: u32,
3454}
3455
3456fn default_p2p_late_payment_rate() -> f64 {
3457    0.15
3458}
3459
3460fn default_p2p_partial_payment_rate() -> f64 {
3461    0.05
3462}
3463
3464fn default_p2p_payment_correction_rate() -> f64 {
3465    0.02
3466}
3467
3468fn default_p2p_avg_days_until_remainder() -> u32 {
3469    30
3470}
3471
3472impl Default for P2PPaymentBehaviorConfig {
3473    fn default() -> Self {
3474        Self {
3475            late_payment_rate: default_p2p_late_payment_rate(),
3476            late_payment_days_distribution: LatePaymentDaysDistribution::default(),
3477            partial_payment_rate: default_p2p_partial_payment_rate(),
3478            payment_correction_rate: default_p2p_payment_correction_rate(),
3479            avg_days_until_remainder: default_p2p_avg_days_until_remainder(),
3480        }
3481    }
3482}
3483
3484/// Distribution of late payment days for P2P.
3485#[derive(Debug, Clone, Serialize, Deserialize)]
3486pub struct LatePaymentDaysDistribution {
3487    /// 1-7 days late (slightly late)
3488    #[serde(default = "default_slightly_late")]
3489    pub slightly_late_1_to_7: f64,
3490    /// 8-14 days late
3491    #[serde(default = "default_late_8_14")]
3492    pub late_8_to_14: f64,
3493    /// 15-30 days late (very late)
3494    #[serde(default = "default_very_late")]
3495    pub very_late_15_to_30: f64,
3496    /// 31-60 days late (severely late)
3497    #[serde(default = "default_severely_late")]
3498    pub severely_late_31_to_60: f64,
3499    /// Over 60 days late (extremely late)
3500    #[serde(default = "default_extremely_late")]
3501    pub extremely_late_over_60: f64,
3502}
3503
3504fn default_slightly_late() -> f64 {
3505    0.50
3506}
3507
3508fn default_late_8_14() -> f64 {
3509    0.25
3510}
3511
3512fn default_very_late() -> f64 {
3513    0.15
3514}
3515
3516fn default_severely_late() -> f64 {
3517    0.07
3518}
3519
3520fn default_extremely_late() -> f64 {
3521    0.03
3522}
3523
3524impl Default for LatePaymentDaysDistribution {
3525    fn default() -> Self {
3526        Self {
3527            slightly_late_1_to_7: default_slightly_late(),
3528            late_8_to_14: default_late_8_14(),
3529            very_late_15_to_30: default_very_late(),
3530            severely_late_31_to_60: default_severely_late(),
3531            extremely_late_over_60: default_extremely_late(),
3532        }
3533    }
3534}
3535
3536/// O2C (Order-to-Cash) flow configuration.
3537#[derive(Debug, Clone, Serialize, Deserialize)]
3538pub struct O2CFlowConfig {
3539    /// Enable O2C document flow generation
3540    #[serde(default = "default_true")]
3541    pub enabled: bool,
3542    /// Credit check failure rate
3543    #[serde(default = "default_credit_check_failure_rate")]
3544    pub credit_check_failure_rate: f64,
3545    /// Rate of partial shipments
3546    #[serde(default = "default_partial_shipment_rate")]
3547    pub partial_shipment_rate: f64,
3548    /// Rate of returns
3549    #[serde(default = "default_return_rate")]
3550    pub return_rate: f64,
3551    /// Bad debt write-off rate
3552    #[serde(default = "default_bad_debt_rate")]
3553    pub bad_debt_rate: f64,
3554    /// Average days from SO to delivery
3555    #[serde(default = "default_so_to_delivery_days")]
3556    pub average_so_to_delivery_days: u32,
3557    /// Average days from delivery to invoice
3558    #[serde(default = "default_delivery_to_invoice_days")]
3559    pub average_delivery_to_invoice_days: u32,
3560    /// Average days from invoice to receipt
3561    #[serde(default = "default_invoice_to_receipt_days")]
3562    pub average_invoice_to_receipt_days: u32,
3563    /// SO line count distribution
3564    #[serde(default)]
3565    pub line_count_distribution: DocumentLineCountDistribution,
3566    /// Cash discount configuration
3567    #[serde(default)]
3568    pub cash_discount: CashDiscountConfig,
3569    /// Payment behavior configuration
3570    #[serde(default)]
3571    pub payment_behavior: O2CPaymentBehaviorConfig,
3572    /// Rate of late payments
3573    #[serde(default)]
3574    pub late_payment_rate: Option<f64>,
3575}
3576
3577fn default_credit_check_failure_rate() -> f64 {
3578    0.02
3579}
3580
3581fn default_partial_shipment_rate() -> f64 {
3582    0.10
3583}
3584
3585fn default_return_rate() -> f64 {
3586    0.03
3587}
3588
3589fn default_bad_debt_rate() -> f64 {
3590    0.01
3591}
3592
3593fn default_so_to_delivery_days() -> u32 {
3594    7
3595}
3596
3597fn default_delivery_to_invoice_days() -> u32 {
3598    1
3599}
3600
3601fn default_invoice_to_receipt_days() -> u32 {
3602    45
3603}
3604
3605impl Default for O2CFlowConfig {
3606    fn default() -> Self {
3607        Self {
3608            enabled: true,
3609            credit_check_failure_rate: default_credit_check_failure_rate(),
3610            partial_shipment_rate: default_partial_shipment_rate(),
3611            return_rate: default_return_rate(),
3612            bad_debt_rate: default_bad_debt_rate(),
3613            average_so_to_delivery_days: default_so_to_delivery_days(),
3614            average_delivery_to_invoice_days: default_delivery_to_invoice_days(),
3615            average_invoice_to_receipt_days: default_invoice_to_receipt_days(),
3616            line_count_distribution: DocumentLineCountDistribution::default(),
3617            cash_discount: CashDiscountConfig::default(),
3618            payment_behavior: O2CPaymentBehaviorConfig::default(),
3619            late_payment_rate: None,
3620        }
3621    }
3622}
3623
3624// ============================================================================
3625// O2C Payment Behavior Configuration
3626// ============================================================================
3627
3628/// O2C payment behavior configuration.
3629#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3630pub struct O2CPaymentBehaviorConfig {
3631    /// Dunning (Mahnung) configuration
3632    #[serde(default)]
3633    pub dunning: DunningConfig,
3634    /// Partial payment configuration
3635    #[serde(default)]
3636    pub partial_payments: PartialPaymentConfig,
3637    /// Short payment configuration (unauthorized deductions)
3638    #[serde(default)]
3639    pub short_payments: ShortPaymentConfig,
3640    /// On-account payment configuration (unapplied payments)
3641    #[serde(default)]
3642    pub on_account_payments: OnAccountPaymentConfig,
3643    /// Payment correction configuration (NSF, chargebacks)
3644    #[serde(default)]
3645    pub payment_corrections: PaymentCorrectionConfig,
3646}
3647
3648/// Dunning (Mahnungen) configuration for AR collections.
3649#[derive(Debug, Clone, Serialize, Deserialize)]
3650pub struct DunningConfig {
3651    /// Enable dunning process
3652    #[serde(default)]
3653    pub enabled: bool,
3654    /// Days overdue for level 1 dunning (1st reminder)
3655    #[serde(default = "default_dunning_level_1_days")]
3656    pub level_1_days_overdue: u32,
3657    /// Days overdue for level 2 dunning (2nd reminder)
3658    #[serde(default = "default_dunning_level_2_days")]
3659    pub level_2_days_overdue: u32,
3660    /// Days overdue for level 3 dunning (final notice)
3661    #[serde(default = "default_dunning_level_3_days")]
3662    pub level_3_days_overdue: u32,
3663    /// Days overdue for collection handover
3664    #[serde(default = "default_collection_days")]
3665    pub collection_days_overdue: u32,
3666    /// Payment rates after each dunning level
3667    #[serde(default)]
3668    pub payment_after_dunning_rates: DunningPaymentRates,
3669    /// Rate of invoices blocked from dunning (disputes)
3670    #[serde(default = "default_dunning_block_rate")]
3671    pub dunning_block_rate: f64,
3672    /// Interest rate per year for overdue amounts
3673    #[serde(default = "default_dunning_interest_rate")]
3674    pub interest_rate_per_year: f64,
3675    /// Fixed dunning charge per letter
3676    #[serde(default = "default_dunning_charge")]
3677    pub dunning_charge: f64,
3678}
3679
3680fn default_dunning_level_1_days() -> u32 {
3681    14
3682}
3683
3684fn default_dunning_level_2_days() -> u32 {
3685    28
3686}
3687
3688fn default_dunning_level_3_days() -> u32 {
3689    42
3690}
3691
3692fn default_collection_days() -> u32 {
3693    60
3694}
3695
3696fn default_dunning_block_rate() -> f64 {
3697    0.05
3698}
3699
3700fn default_dunning_interest_rate() -> f64 {
3701    0.09
3702}
3703
3704fn default_dunning_charge() -> f64 {
3705    25.0
3706}
3707
3708impl Default for DunningConfig {
3709    fn default() -> Self {
3710        Self {
3711            enabled: false,
3712            level_1_days_overdue: default_dunning_level_1_days(),
3713            level_2_days_overdue: default_dunning_level_2_days(),
3714            level_3_days_overdue: default_dunning_level_3_days(),
3715            collection_days_overdue: default_collection_days(),
3716            payment_after_dunning_rates: DunningPaymentRates::default(),
3717            dunning_block_rate: default_dunning_block_rate(),
3718            interest_rate_per_year: default_dunning_interest_rate(),
3719            dunning_charge: default_dunning_charge(),
3720        }
3721    }
3722}
3723
3724/// Payment rates after each dunning level.
3725#[derive(Debug, Clone, Serialize, Deserialize)]
3726pub struct DunningPaymentRates {
3727    /// Rate that pays after level 1 reminder
3728    #[serde(default = "default_after_level_1")]
3729    pub after_level_1: f64,
3730    /// Rate that pays after level 2 reminder
3731    #[serde(default = "default_after_level_2")]
3732    pub after_level_2: f64,
3733    /// Rate that pays after level 3 final notice
3734    #[serde(default = "default_after_level_3")]
3735    pub after_level_3: f64,
3736    /// Rate that pays during collection
3737    #[serde(default = "default_during_collection")]
3738    pub during_collection: f64,
3739    /// Rate that never pays (becomes bad debt)
3740    #[serde(default = "default_never_pay")]
3741    pub never_pay: f64,
3742}
3743
3744fn default_after_level_1() -> f64 {
3745    0.40
3746}
3747
3748fn default_after_level_2() -> f64 {
3749    0.30
3750}
3751
3752fn default_after_level_3() -> f64 {
3753    0.15
3754}
3755
3756fn default_during_collection() -> f64 {
3757    0.05
3758}
3759
3760fn default_never_pay() -> f64 {
3761    0.10
3762}
3763
3764impl Default for DunningPaymentRates {
3765    fn default() -> Self {
3766        Self {
3767            after_level_1: default_after_level_1(),
3768            after_level_2: default_after_level_2(),
3769            after_level_3: default_after_level_3(),
3770            during_collection: default_during_collection(),
3771            never_pay: default_never_pay(),
3772        }
3773    }
3774}
3775
3776/// Partial payment configuration.
3777#[derive(Debug, Clone, Serialize, Deserialize)]
3778pub struct PartialPaymentConfig {
3779    /// Rate of invoices paid partially
3780    #[serde(default = "default_partial_payment_rate")]
3781    pub rate: f64,
3782    /// Distribution of partial payment percentages
3783    #[serde(default)]
3784    pub percentage_distribution: PartialPaymentPercentageDistribution,
3785    /// Average days until remainder is paid
3786    #[serde(default = "default_avg_days_until_remainder")]
3787    pub avg_days_until_remainder: u32,
3788}
3789
3790fn default_partial_payment_rate() -> f64 {
3791    0.08
3792}
3793
3794fn default_avg_days_until_remainder() -> u32 {
3795    30
3796}
3797
3798impl Default for PartialPaymentConfig {
3799    fn default() -> Self {
3800        Self {
3801            rate: default_partial_payment_rate(),
3802            percentage_distribution: PartialPaymentPercentageDistribution::default(),
3803            avg_days_until_remainder: default_avg_days_until_remainder(),
3804        }
3805    }
3806}
3807
3808/// Distribution of partial payment percentages.
3809#[derive(Debug, Clone, Serialize, Deserialize)]
3810pub struct PartialPaymentPercentageDistribution {
3811    /// Pay 25% of invoice
3812    #[serde(default = "default_partial_25")]
3813    pub pay_25_percent: f64,
3814    /// Pay 50% of invoice
3815    #[serde(default = "default_partial_50")]
3816    pub pay_50_percent: f64,
3817    /// Pay 75% of invoice
3818    #[serde(default = "default_partial_75")]
3819    pub pay_75_percent: f64,
3820    /// Pay random percentage
3821    #[serde(default = "default_partial_random")]
3822    pub pay_random_percent: f64,
3823}
3824
3825fn default_partial_25() -> f64 {
3826    0.15
3827}
3828
3829fn default_partial_50() -> f64 {
3830    0.50
3831}
3832
3833fn default_partial_75() -> f64 {
3834    0.25
3835}
3836
3837fn default_partial_random() -> f64 {
3838    0.10
3839}
3840
3841impl Default for PartialPaymentPercentageDistribution {
3842    fn default() -> Self {
3843        Self {
3844            pay_25_percent: default_partial_25(),
3845            pay_50_percent: default_partial_50(),
3846            pay_75_percent: default_partial_75(),
3847            pay_random_percent: default_partial_random(),
3848        }
3849    }
3850}
3851
3852/// Short payment configuration (unauthorized deductions).
3853#[derive(Debug, Clone, Serialize, Deserialize)]
3854pub struct ShortPaymentConfig {
3855    /// Rate of payments that are short
3856    #[serde(default = "default_short_payment_rate")]
3857    pub rate: f64,
3858    /// Distribution of short payment reasons
3859    #[serde(default)]
3860    pub reason_distribution: ShortPaymentReasonDistribution,
3861    /// Maximum percentage that can be short
3862    #[serde(default = "default_max_short_percent")]
3863    pub max_short_percent: f64,
3864}
3865
3866fn default_short_payment_rate() -> f64 {
3867    0.03
3868}
3869
3870fn default_max_short_percent() -> f64 {
3871    0.10
3872}
3873
3874impl Default for ShortPaymentConfig {
3875    fn default() -> Self {
3876        Self {
3877            rate: default_short_payment_rate(),
3878            reason_distribution: ShortPaymentReasonDistribution::default(),
3879            max_short_percent: default_max_short_percent(),
3880        }
3881    }
3882}
3883
3884/// Distribution of short payment reasons.
3885#[derive(Debug, Clone, Serialize, Deserialize)]
3886pub struct ShortPaymentReasonDistribution {
3887    /// Pricing dispute
3888    #[serde(default = "default_pricing_dispute")]
3889    pub pricing_dispute: f64,
3890    /// Quality issue
3891    #[serde(default = "default_quality_issue")]
3892    pub quality_issue: f64,
3893    /// Quantity discrepancy
3894    #[serde(default = "default_quantity_discrepancy")]
3895    pub quantity_discrepancy: f64,
3896    /// Unauthorized deduction
3897    #[serde(default = "default_unauthorized_deduction")]
3898    pub unauthorized_deduction: f64,
3899    /// Early payment discount taken incorrectly
3900    #[serde(default = "default_incorrect_discount")]
3901    pub incorrect_discount: f64,
3902}
3903
3904fn default_pricing_dispute() -> f64 {
3905    0.30
3906}
3907
3908fn default_quality_issue() -> f64 {
3909    0.20
3910}
3911
3912fn default_quantity_discrepancy() -> f64 {
3913    0.20
3914}
3915
3916fn default_unauthorized_deduction() -> f64 {
3917    0.15
3918}
3919
3920fn default_incorrect_discount() -> f64 {
3921    0.15
3922}
3923
3924impl Default for ShortPaymentReasonDistribution {
3925    fn default() -> Self {
3926        Self {
3927            pricing_dispute: default_pricing_dispute(),
3928            quality_issue: default_quality_issue(),
3929            quantity_discrepancy: default_quantity_discrepancy(),
3930            unauthorized_deduction: default_unauthorized_deduction(),
3931            incorrect_discount: default_incorrect_discount(),
3932        }
3933    }
3934}
3935
3936/// On-account payment configuration (unapplied payments).
3937#[derive(Debug, Clone, Serialize, Deserialize)]
3938pub struct OnAccountPaymentConfig {
3939    /// Rate of payments that are on-account (unapplied)
3940    #[serde(default = "default_on_account_rate")]
3941    pub rate: f64,
3942    /// Average days until on-account payments are applied
3943    #[serde(default = "default_avg_days_until_applied")]
3944    pub avg_days_until_applied: u32,
3945}
3946
3947fn default_on_account_rate() -> f64 {
3948    0.02
3949}
3950
3951fn default_avg_days_until_applied() -> u32 {
3952    14
3953}
3954
3955impl Default for OnAccountPaymentConfig {
3956    fn default() -> Self {
3957        Self {
3958            rate: default_on_account_rate(),
3959            avg_days_until_applied: default_avg_days_until_applied(),
3960        }
3961    }
3962}
3963
3964/// Payment correction configuration.
3965#[derive(Debug, Clone, Serialize, Deserialize)]
3966pub struct PaymentCorrectionConfig {
3967    /// Rate of payments requiring correction
3968    #[serde(default = "default_payment_correction_rate")]
3969    pub rate: f64,
3970    /// Distribution of correction types
3971    #[serde(default)]
3972    pub type_distribution: PaymentCorrectionTypeDistribution,
3973}
3974
3975fn default_payment_correction_rate() -> f64 {
3976    0.02
3977}
3978
3979impl Default for PaymentCorrectionConfig {
3980    fn default() -> Self {
3981        Self {
3982            rate: default_payment_correction_rate(),
3983            type_distribution: PaymentCorrectionTypeDistribution::default(),
3984        }
3985    }
3986}
3987
3988/// Distribution of payment correction types.
3989#[derive(Debug, Clone, Serialize, Deserialize)]
3990pub struct PaymentCorrectionTypeDistribution {
3991    /// NSF (Non-sufficient funds) / bounced check
3992    #[serde(default = "default_nsf_rate")]
3993    pub nsf: f64,
3994    /// Chargeback
3995    #[serde(default = "default_chargeback_rate")]
3996    pub chargeback: f64,
3997    /// Wrong amount applied
3998    #[serde(default = "default_wrong_amount_rate")]
3999    pub wrong_amount: f64,
4000    /// Wrong customer applied
4001    #[serde(default = "default_wrong_customer_rate")]
4002    pub wrong_customer: f64,
4003    /// Duplicate payment
4004    #[serde(default = "default_duplicate_payment_rate")]
4005    pub duplicate_payment: f64,
4006}
4007
4008fn default_nsf_rate() -> f64 {
4009    0.30
4010}
4011
4012fn default_chargeback_rate() -> f64 {
4013    0.20
4014}
4015
4016fn default_wrong_amount_rate() -> f64 {
4017    0.20
4018}
4019
4020fn default_wrong_customer_rate() -> f64 {
4021    0.15
4022}
4023
4024fn default_duplicate_payment_rate() -> f64 {
4025    0.15
4026}
4027
4028impl Default for PaymentCorrectionTypeDistribution {
4029    fn default() -> Self {
4030        Self {
4031            nsf: default_nsf_rate(),
4032            chargeback: default_chargeback_rate(),
4033            wrong_amount: default_wrong_amount_rate(),
4034            wrong_customer: default_wrong_customer_rate(),
4035            duplicate_payment: default_duplicate_payment_rate(),
4036        }
4037    }
4038}
4039
4040/// Document line count distribution.
4041#[derive(Debug, Clone, Serialize, Deserialize)]
4042pub struct DocumentLineCountDistribution {
4043    /// Minimum number of lines
4044    #[serde(default = "default_min_lines")]
4045    pub min_lines: u32,
4046    /// Maximum number of lines
4047    #[serde(default = "default_max_lines")]
4048    pub max_lines: u32,
4049    /// Most common line count (mode)
4050    #[serde(default = "default_mode_lines")]
4051    pub mode_lines: u32,
4052}
4053
4054fn default_min_lines() -> u32 {
4055    1
4056}
4057
4058fn default_max_lines() -> u32 {
4059    20
4060}
4061
4062fn default_mode_lines() -> u32 {
4063    3
4064}
4065
4066impl Default for DocumentLineCountDistribution {
4067    fn default() -> Self {
4068        Self {
4069            min_lines: default_min_lines(),
4070            max_lines: default_max_lines(),
4071            mode_lines: default_mode_lines(),
4072        }
4073    }
4074}
4075
4076/// Cash discount configuration.
4077#[derive(Debug, Clone, Serialize, Deserialize)]
4078pub struct CashDiscountConfig {
4079    /// Percentage of invoices eligible for cash discount
4080    #[serde(default = "default_discount_eligible_rate")]
4081    pub eligible_rate: f64,
4082    /// Rate at which customers take the discount
4083    #[serde(default = "default_discount_taken_rate")]
4084    pub taken_rate: f64,
4085    /// Standard discount percentage
4086    #[serde(default = "default_discount_percent")]
4087    pub discount_percent: f64,
4088    /// Days within which discount must be taken
4089    #[serde(default = "default_discount_days")]
4090    pub discount_days: u32,
4091}
4092
4093fn default_discount_eligible_rate() -> f64 {
4094    0.30
4095}
4096
4097fn default_discount_taken_rate() -> f64 {
4098    0.60
4099}
4100
4101fn default_discount_percent() -> f64 {
4102    0.02
4103}
4104
4105fn default_discount_days() -> u32 {
4106    10
4107}
4108
4109impl Default for CashDiscountConfig {
4110    fn default() -> Self {
4111        Self {
4112            eligible_rate: default_discount_eligible_rate(),
4113            taken_rate: default_discount_taken_rate(),
4114            discount_percent: default_discount_percent(),
4115            discount_days: default_discount_days(),
4116        }
4117    }
4118}
4119
4120// ============================================================================
4121// Intercompany Configuration
4122// ============================================================================
4123
4124/// Intercompany transaction configuration.
4125#[derive(Debug, Clone, Serialize, Deserialize)]
4126pub struct IntercompanyConfig {
4127    /// Enable intercompany transaction generation
4128    #[serde(default)]
4129    pub enabled: bool,
4130    /// Rate of transactions that are intercompany
4131    #[serde(default = "default_ic_transaction_rate")]
4132    pub ic_transaction_rate: f64,
4133    /// Transfer pricing method
4134    #[serde(default)]
4135    pub transfer_pricing_method: TransferPricingMethod,
4136    /// Transfer pricing markup percentage (for cost-plus)
4137    #[serde(default = "default_markup_percent")]
4138    pub markup_percent: f64,
4139    /// Generate matched IC pairs (offsetting entries)
4140    #[serde(default = "default_true")]
4141    pub generate_matched_pairs: bool,
4142    /// IC transaction type distribution
4143    #[serde(default)]
4144    pub transaction_type_distribution: ICTransactionTypeDistribution,
4145    /// Generate elimination entries for consolidation
4146    #[serde(default)]
4147    pub generate_eliminations: bool,
4148}
4149
4150fn default_ic_transaction_rate() -> f64 {
4151    0.15
4152}
4153
4154fn default_markup_percent() -> f64 {
4155    0.05
4156}
4157
4158impl Default for IntercompanyConfig {
4159    fn default() -> Self {
4160        Self {
4161            enabled: false,
4162            ic_transaction_rate: default_ic_transaction_rate(),
4163            transfer_pricing_method: TransferPricingMethod::default(),
4164            markup_percent: default_markup_percent(),
4165            generate_matched_pairs: true,
4166            transaction_type_distribution: ICTransactionTypeDistribution::default(),
4167            generate_eliminations: false,
4168        }
4169    }
4170}
4171
4172/// Transfer pricing method.
4173#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
4174#[serde(rename_all = "snake_case")]
4175pub enum TransferPricingMethod {
4176    /// Cost plus a markup
4177    #[default]
4178    CostPlus,
4179    /// Comparable uncontrolled price
4180    ComparableUncontrolled,
4181    /// Resale price method
4182    ResalePrice,
4183    /// Transactional net margin method
4184    TransactionalNetMargin,
4185    /// Profit split method
4186    ProfitSplit,
4187}
4188
4189/// IC transaction type distribution.
4190#[derive(Debug, Clone, Serialize, Deserialize)]
4191pub struct ICTransactionTypeDistribution {
4192    /// Goods sales between entities
4193    pub goods_sale: f64,
4194    /// Services provided
4195    pub service_provided: f64,
4196    /// Intercompany loans
4197    pub loan: f64,
4198    /// Dividends
4199    pub dividend: f64,
4200    /// Management fees
4201    pub management_fee: f64,
4202    /// Royalties
4203    pub royalty: f64,
4204    /// Cost sharing
4205    pub cost_sharing: f64,
4206}
4207
4208impl Default for ICTransactionTypeDistribution {
4209    fn default() -> Self {
4210        Self {
4211            goods_sale: 0.35,
4212            service_provided: 0.20,
4213            loan: 0.10,
4214            dividend: 0.05,
4215            management_fee: 0.15,
4216            royalty: 0.10,
4217            cost_sharing: 0.05,
4218        }
4219    }
4220}
4221
4222// ============================================================================
4223// Balance Configuration
4224// ============================================================================
4225
4226/// Balance and trial balance configuration.
4227#[derive(Debug, Clone, Serialize, Deserialize)]
4228pub struct BalanceConfig {
4229    /// Generate opening balances
4230    #[serde(default)]
4231    pub generate_opening_balances: bool,
4232    /// Generate trial balances
4233    #[serde(default = "default_true")]
4234    pub generate_trial_balances: bool,
4235    /// Target gross margin (for revenue/COGS coherence)
4236    #[serde(default = "default_gross_margin")]
4237    pub target_gross_margin: f64,
4238    /// Target DSO (Days Sales Outstanding)
4239    #[serde(default = "default_dso")]
4240    pub target_dso_days: u32,
4241    /// Target DPO (Days Payable Outstanding)
4242    #[serde(default = "default_dpo")]
4243    pub target_dpo_days: u32,
4244    /// Target current ratio
4245    #[serde(default = "default_current_ratio")]
4246    pub target_current_ratio: f64,
4247    /// Target debt-to-equity ratio
4248    #[serde(default = "default_debt_equity")]
4249    pub target_debt_to_equity: f64,
4250    /// Validate balance sheet equation (A = L + E)
4251    #[serde(default = "default_true")]
4252    pub validate_balance_equation: bool,
4253    /// Reconcile subledgers to GL control accounts
4254    #[serde(default = "default_true")]
4255    pub reconcile_subledgers: bool,
4256}
4257
4258fn default_gross_margin() -> f64 {
4259    0.35
4260}
4261
4262fn default_dso() -> u32 {
4263    45
4264}
4265
4266fn default_dpo() -> u32 {
4267    30
4268}
4269
4270fn default_current_ratio() -> f64 {
4271    1.5
4272}
4273
4274fn default_debt_equity() -> f64 {
4275    0.5
4276}
4277
4278impl Default for BalanceConfig {
4279    fn default() -> Self {
4280        Self {
4281            generate_opening_balances: false,
4282            generate_trial_balances: true,
4283            target_gross_margin: default_gross_margin(),
4284            target_dso_days: default_dso(),
4285            target_dpo_days: default_dpo(),
4286            target_current_ratio: default_current_ratio(),
4287            target_debt_to_equity: default_debt_equity(),
4288            validate_balance_equation: true,
4289            reconcile_subledgers: true,
4290        }
4291    }
4292}
4293
4294// ==========================================================================
4295// OCPM (Object-Centric Process Mining) Configuration
4296// ==========================================================================
4297
4298/// OCPM (Object-Centric Process Mining) configuration.
4299///
4300/// Controls generation of OCEL 2.0 compatible event logs with
4301/// many-to-many event-to-object relationships.
4302#[derive(Debug, Clone, Serialize, Deserialize)]
4303pub struct OcpmConfig {
4304    /// Enable OCPM event log generation
4305    #[serde(default)]
4306    pub enabled: bool,
4307
4308    /// Generate lifecycle events (Start/Complete pairs vs atomic events)
4309    #[serde(default = "default_true")]
4310    pub generate_lifecycle_events: bool,
4311
4312    /// Include object-to-object relationships in output
4313    #[serde(default = "default_true")]
4314    pub include_object_relationships: bool,
4315
4316    /// Compute and export process variants
4317    #[serde(default = "default_true")]
4318    pub compute_variants: bool,
4319
4320    /// Maximum variants to track (0 = unlimited)
4321    #[serde(default)]
4322    pub max_variants: usize,
4323
4324    /// P2P process configuration
4325    #[serde(default)]
4326    pub p2p_process: OcpmProcessConfig,
4327
4328    /// O2C process configuration
4329    #[serde(default)]
4330    pub o2c_process: OcpmProcessConfig,
4331
4332    /// Output format configuration
4333    #[serde(default)]
4334    pub output: OcpmOutputConfig,
4335}
4336
4337impl Default for OcpmConfig {
4338    fn default() -> Self {
4339        Self {
4340            enabled: false,
4341            generate_lifecycle_events: true,
4342            include_object_relationships: true,
4343            compute_variants: true,
4344            max_variants: 0,
4345            p2p_process: OcpmProcessConfig::default(),
4346            o2c_process: OcpmProcessConfig::default(),
4347            output: OcpmOutputConfig::default(),
4348        }
4349    }
4350}
4351
4352/// Process-specific OCPM configuration.
4353#[derive(Debug, Clone, Serialize, Deserialize)]
4354pub struct OcpmProcessConfig {
4355    /// Rework probability (0.0-1.0)
4356    #[serde(default = "default_rework_probability")]
4357    pub rework_probability: f64,
4358
4359    /// Skip step probability (0.0-1.0)
4360    #[serde(default = "default_skip_probability")]
4361    pub skip_step_probability: f64,
4362
4363    /// Out-of-order step probability (0.0-1.0)
4364    #[serde(default = "default_out_of_order_probability")]
4365    pub out_of_order_probability: f64,
4366}
4367
4368// Defaults deliberately produce variant counts and Inductive-Miner fitness
4369// in the range seen in real ERP data (dozens of variants, ~0.7–0.9 fitness).
4370// Lowering them all to 0 yields a single-variant happy-path log.
4371fn default_rework_probability() -> f64 {
4372    0.15
4373}
4374
4375fn default_skip_probability() -> f64 {
4376    0.10
4377}
4378
4379fn default_out_of_order_probability() -> f64 {
4380    0.08
4381}
4382
4383impl Default for OcpmProcessConfig {
4384    fn default() -> Self {
4385        Self {
4386            rework_probability: default_rework_probability(),
4387            skip_step_probability: default_skip_probability(),
4388            out_of_order_probability: default_out_of_order_probability(),
4389        }
4390    }
4391}
4392
4393/// OCPM output format configuration.
4394#[derive(Debug, Clone, Serialize, Deserialize)]
4395pub struct OcpmOutputConfig {
4396    /// Export OCEL 2.0 JSON format
4397    #[serde(default = "default_true")]
4398    pub ocel_json: bool,
4399
4400    /// Export OCEL 2.0 XML format
4401    #[serde(default)]
4402    pub ocel_xml: bool,
4403
4404    /// Export XES 2.0 XML format (IEEE standard for process mining tools)
4405    #[serde(default)]
4406    pub xes: bool,
4407
4408    /// Include lifecycle transitions in XES output (start/complete pairs)
4409    #[serde(default = "default_true")]
4410    pub xes_include_lifecycle: bool,
4411
4412    /// Include resource attributes in XES output
4413    #[serde(default = "default_true")]
4414    pub xes_include_resources: bool,
4415
4416    /// Export flattened CSV for each object type
4417    #[serde(default = "default_true")]
4418    pub flattened_csv: bool,
4419
4420    /// Export event-object relationship table
4421    #[serde(default = "default_true")]
4422    pub event_object_csv: bool,
4423
4424    /// Export object-object relationship table
4425    #[serde(default = "default_true")]
4426    pub object_relationship_csv: bool,
4427
4428    /// Export process variants summary
4429    #[serde(default = "default_true")]
4430    pub variants_csv: bool,
4431
4432    /// Export reference process models (canonical P2P, O2C, R2R)
4433    #[serde(default)]
4434    pub export_reference_models: bool,
4435}
4436
4437impl Default for OcpmOutputConfig {
4438    fn default() -> Self {
4439        Self {
4440            ocel_json: true,
4441            ocel_xml: false,
4442            xes: false,
4443            xes_include_lifecycle: true,
4444            xes_include_resources: true,
4445            flattened_csv: true,
4446            event_object_csv: true,
4447            object_relationship_csv: true,
4448            variants_csv: true,
4449            export_reference_models: false,
4450        }
4451    }
4452}
4453
4454/// Audit engagement and workpaper generation configuration.
4455#[derive(Debug, Clone, Serialize, Deserialize)]
4456pub struct AuditGenerationConfig {
4457    /// Enable audit engagement generation
4458    #[serde(default)]
4459    pub enabled: bool,
4460
4461    /// Gate for workpaper generation (v3.3.2+).
4462    /// When `false`, workpapers and dependent evidence are skipped
4463    /// while engagements / risk assessments / findings still generate.
4464    #[serde(default = "default_true")]
4465    pub generate_workpapers: bool,
4466
4467    /// Engagement type distribution (v3.3.2+). Drives per-engagement
4468    /// type draw via `AuditEngagementGenerator::draw_engagement_type`.
4469    #[serde(default)]
4470    pub engagement_types: AuditEngagementTypesConfig,
4471
4472    /// Workpaper configuration (v3.3.2+). `average_per_phase` maps onto
4473    /// `WorkpaperGenerator.workpapers_per_section` as a ±50% band
4474    /// around the average. Sampling / ISA / cross-reference flags are
4475    /// surfaced for downstream formatting overlays.
4476    #[serde(default)]
4477    pub workpapers: WorkpaperConfig,
4478
4479    /// Audit team configuration (v3.3.2+). `min_team_size` /
4480    /// `max_team_size` map directly onto
4481    /// `AuditEngagementGenerator.team_size_range`.
4482    /// `specialist_probability` is reserved for v3.4 (explicit
4483    /// specialist-role support).
4484    #[serde(default)]
4485    pub team: AuditTeamConfig,
4486
4487    /// Review workflow configuration (v3.3.2+).
4488    /// `average_review_delay_days` drives both
4489    /// `first_review_delay_range` and `second_review_delay_range` as
4490    /// a ±1-day band around the average. `rework_probability` and
4491    /// `require_partner_signoff` are reserved for v3.4 workflow
4492    /// modeling.
4493    #[serde(default)]
4494    pub review: ReviewWorkflowConfig,
4495
4496    /// FSM-driven audit generation configuration.
4497    #[serde(default)]
4498    pub fsm: Option<AuditFsmConfig>,
4499
4500    /// v3.3.0: IT general controls (access logs, change management
4501    /// records) emitted alongside audit engagements. Requires both
4502    /// `audit.enabled = true` and `audit.it_controls.enabled = true`
4503    /// to take effect — the latter defaults to `false` so current
4504    /// archives are byte-identical to v3.2.1.
4505    #[serde(default)]
4506    pub it_controls: ItControlsConfig,
4507}
4508
4509/// IT general controls config (v3.3.0+).
4510#[derive(Debug, Clone, Serialize, Deserialize)]
4511pub struct ItControlsConfig {
4512    /// Master switch — when `false`, no access logs or change records
4513    /// are generated.
4514    #[serde(default)]
4515    pub enabled: bool,
4516    /// Number of access-log entries per engagement (approximate — the
4517    /// generator may round or scale based on company size).
4518    #[serde(default = "default_access_log_count")]
4519    pub access_logs_per_engagement: usize,
4520    /// Number of change-management records per engagement.
4521    #[serde(default = "default_change_record_count")]
4522    pub change_records_per_engagement: usize,
4523}
4524
4525fn default_access_log_count() -> usize {
4526    500
4527}
4528fn default_change_record_count() -> usize {
4529    50
4530}
4531
4532impl Default for ItControlsConfig {
4533    fn default() -> Self {
4534        Self {
4535            enabled: false,
4536            access_logs_per_engagement: default_access_log_count(),
4537            change_records_per_engagement: default_change_record_count(),
4538        }
4539    }
4540}
4541
4542impl Default for AuditGenerationConfig {
4543    fn default() -> Self {
4544        Self {
4545            enabled: false,
4546            generate_workpapers: true,
4547            engagement_types: AuditEngagementTypesConfig::default(),
4548            workpapers: WorkpaperConfig::default(),
4549            team: AuditTeamConfig::default(),
4550            review: ReviewWorkflowConfig::default(),
4551            fsm: None,
4552            it_controls: ItControlsConfig::default(),
4553        }
4554    }
4555}
4556
4557/// FSM-driven audit generation configuration.
4558#[derive(Debug, Clone, Serialize, Deserialize)]
4559pub struct AuditFsmConfig {
4560    /// Enable FSM-driven audit generation.
4561    #[serde(default)]
4562    pub enabled: bool,
4563
4564    /// Blueprint source: "builtin:fsa", "builtin:ia", or a file path.
4565    #[serde(default = "default_audit_fsm_blueprint")]
4566    pub blueprint: String,
4567
4568    /// Overlay source: "builtin:default", "builtin:thorough", "builtin:rushed", or a file path.
4569    #[serde(default = "default_audit_fsm_overlay")]
4570    pub overlay: String,
4571
4572    /// Depth level override.
4573    #[serde(default)]
4574    pub depth: Option<String>,
4575
4576    /// Discriminator filter.
4577    #[serde(default)]
4578    pub discriminators: std::collections::HashMap<String, Vec<String>>,
4579
4580    /// Event trail output config.
4581    #[serde(default)]
4582    pub event_trail: AuditEventTrailConfig,
4583
4584    /// RNG seed override.
4585    #[serde(default)]
4586    pub seed: Option<u64>,
4587}
4588
4589impl Default for AuditFsmConfig {
4590    fn default() -> Self {
4591        Self {
4592            enabled: false,
4593            blueprint: default_audit_fsm_blueprint(),
4594            overlay: default_audit_fsm_overlay(),
4595            depth: None,
4596            discriminators: std::collections::HashMap::new(),
4597            event_trail: AuditEventTrailConfig::default(),
4598            seed: None,
4599        }
4600    }
4601}
4602
4603fn default_audit_fsm_blueprint() -> String {
4604    "builtin:fsa".to_string()
4605}
4606
4607fn default_audit_fsm_overlay() -> String {
4608    "builtin:default".to_string()
4609}
4610
4611/// Event trail output configuration for FSM-driven audit generation.
4612#[derive(Debug, Clone, Serialize, Deserialize)]
4613pub struct AuditEventTrailConfig {
4614    /// Emit a flat event log.
4615    #[serde(default = "default_true")]
4616    pub flat_log: bool,
4617    /// Project events to OCEL 2.0 format.
4618    #[serde(default)]
4619    pub ocel_projection: bool,
4620}
4621
4622impl Default for AuditEventTrailConfig {
4623    fn default() -> Self {
4624        Self {
4625            flat_log: true,
4626            ocel_projection: false,
4627        }
4628    }
4629}
4630
4631/// Engagement type distribution configuration.
4632#[derive(Debug, Clone, Serialize, Deserialize)]
4633pub struct AuditEngagementTypesConfig {
4634    /// Financial statement audit probability
4635    #[serde(default = "default_financial_audit_prob")]
4636    pub financial_statement: f64,
4637    /// SOX/ICFR audit probability
4638    #[serde(default = "default_sox_audit_prob")]
4639    pub sox_icfr: f64,
4640    /// Integrated audit probability
4641    #[serde(default = "default_integrated_audit_prob")]
4642    pub integrated: f64,
4643    /// Review engagement probability
4644    #[serde(default = "default_review_prob")]
4645    pub review: f64,
4646    /// Agreed-upon procedures probability
4647    #[serde(default = "default_aup_prob")]
4648    pub agreed_upon_procedures: f64,
4649}
4650
4651fn default_financial_audit_prob() -> f64 {
4652    0.40
4653}
4654fn default_sox_audit_prob() -> f64 {
4655    0.20
4656}
4657fn default_integrated_audit_prob() -> f64 {
4658    0.25
4659}
4660fn default_review_prob() -> f64 {
4661    0.10
4662}
4663fn default_aup_prob() -> f64 {
4664    0.05
4665}
4666
4667impl Default for AuditEngagementTypesConfig {
4668    fn default() -> Self {
4669        Self {
4670            financial_statement: default_financial_audit_prob(),
4671            sox_icfr: default_sox_audit_prob(),
4672            integrated: default_integrated_audit_prob(),
4673            review: default_review_prob(),
4674            agreed_upon_procedures: default_aup_prob(),
4675        }
4676    }
4677}
4678
4679/// Workpaper generation configuration.
4680#[derive(Debug, Clone, Serialize, Deserialize)]
4681pub struct WorkpaperConfig {
4682    /// Average workpapers per engagement phase
4683    #[serde(default = "default_workpapers_per_phase")]
4684    pub average_per_phase: usize,
4685
4686    /// Include ISA compliance references
4687    #[serde(default = "default_true")]
4688    pub include_isa_references: bool,
4689
4690    /// Generate sample details
4691    #[serde(default = "default_true")]
4692    pub include_sample_details: bool,
4693
4694    /// Include cross-references between workpapers
4695    #[serde(default = "default_true")]
4696    pub include_cross_references: bool,
4697
4698    /// Sampling configuration
4699    #[serde(default)]
4700    pub sampling: SamplingConfig,
4701}
4702
4703fn default_workpapers_per_phase() -> usize {
4704    5
4705}
4706
4707impl Default for WorkpaperConfig {
4708    fn default() -> Self {
4709        Self {
4710            average_per_phase: default_workpapers_per_phase(),
4711            include_isa_references: true,
4712            include_sample_details: true,
4713            include_cross_references: true,
4714            sampling: SamplingConfig::default(),
4715        }
4716    }
4717}
4718
4719/// Sampling method configuration.
4720#[derive(Debug, Clone, Serialize, Deserialize)]
4721pub struct SamplingConfig {
4722    /// Statistical sampling rate (0.0-1.0)
4723    #[serde(default = "default_statistical_rate")]
4724    pub statistical_rate: f64,
4725    /// Judgmental sampling rate (0.0-1.0)
4726    #[serde(default = "default_judgmental_rate")]
4727    pub judgmental_rate: f64,
4728    /// Haphazard sampling rate (0.0-1.0)
4729    #[serde(default = "default_haphazard_rate")]
4730    pub haphazard_rate: f64,
4731    /// 100% examination rate (0.0-1.0)
4732    #[serde(default = "default_complete_examination_rate")]
4733    pub complete_examination_rate: f64,
4734}
4735
4736fn default_statistical_rate() -> f64 {
4737    0.40
4738}
4739fn default_judgmental_rate() -> f64 {
4740    0.30
4741}
4742fn default_haphazard_rate() -> f64 {
4743    0.20
4744}
4745fn default_complete_examination_rate() -> f64 {
4746    0.10
4747}
4748
4749impl Default for SamplingConfig {
4750    fn default() -> Self {
4751        Self {
4752            statistical_rate: default_statistical_rate(),
4753            judgmental_rate: default_judgmental_rate(),
4754            haphazard_rate: default_haphazard_rate(),
4755            complete_examination_rate: default_complete_examination_rate(),
4756        }
4757    }
4758}
4759
4760/// Audit team configuration.
4761#[derive(Debug, Clone, Serialize, Deserialize)]
4762pub struct AuditTeamConfig {
4763    /// Minimum team size
4764    #[serde(default = "default_min_team_size")]
4765    pub min_team_size: usize,
4766    /// Maximum team size
4767    #[serde(default = "default_max_team_size")]
4768    pub max_team_size: usize,
4769    /// Probability of having a specialist on the team
4770    #[serde(default = "default_specialist_probability")]
4771    pub specialist_probability: f64,
4772}
4773
4774fn default_min_team_size() -> usize {
4775    3
4776}
4777fn default_max_team_size() -> usize {
4778    8
4779}
4780fn default_specialist_probability() -> f64 {
4781    0.30
4782}
4783
4784impl Default for AuditTeamConfig {
4785    fn default() -> Self {
4786        Self {
4787            min_team_size: default_min_team_size(),
4788            max_team_size: default_max_team_size(),
4789            specialist_probability: default_specialist_probability(),
4790        }
4791    }
4792}
4793
4794/// Review workflow configuration.
4795#[derive(Debug, Clone, Serialize, Deserialize)]
4796pub struct ReviewWorkflowConfig {
4797    /// Average days between preparer completion and first review
4798    #[serde(default = "default_review_delay_days")]
4799    pub average_review_delay_days: u32,
4800    /// Probability of review notes requiring rework
4801    #[serde(default = "default_rework_probability_review")]
4802    pub rework_probability: f64,
4803    /// Require partner sign-off for all workpapers
4804    #[serde(default = "default_true")]
4805    pub require_partner_signoff: bool,
4806}
4807
4808fn default_review_delay_days() -> u32 {
4809    2
4810}
4811fn default_rework_probability_review() -> f64 {
4812    0.15
4813}
4814
4815impl Default for ReviewWorkflowConfig {
4816    fn default() -> Self {
4817        Self {
4818            average_review_delay_days: default_review_delay_days(),
4819            rework_probability: default_rework_probability_review(),
4820            require_partner_signoff: true,
4821        }
4822    }
4823}
4824
4825// =============================================================================
4826// Data Quality Configuration
4827// =============================================================================
4828
4829/// Data quality variation settings for realistic flakiness injection.
4830#[derive(Debug, Clone, Serialize, Deserialize)]
4831pub struct DataQualitySchemaConfig {
4832    /// Enable data quality variations
4833    #[serde(default)]
4834    pub enabled: bool,
4835    /// Preset to use (overrides individual settings if set)
4836    #[serde(default)]
4837    pub preset: DataQualityPreset,
4838    /// Missing value injection settings
4839    #[serde(default)]
4840    pub missing_values: MissingValuesSchemaConfig,
4841    /// Typo injection settings
4842    #[serde(default)]
4843    pub typos: TypoSchemaConfig,
4844    /// Format variation settings
4845    #[serde(default)]
4846    pub format_variations: FormatVariationSchemaConfig,
4847    /// Duplicate injection settings
4848    #[serde(default)]
4849    pub duplicates: DuplicateSchemaConfig,
4850    /// Encoding issue settings
4851    #[serde(default)]
4852    pub encoding_issues: EncodingIssueSchemaConfig,
4853    /// Generate quality issue labels for ML training
4854    #[serde(default)]
4855    pub generate_labels: bool,
4856    /// Per-sink quality profiles (different settings for CSV vs JSON etc.)
4857    #[serde(default)]
4858    pub sink_profiles: SinkQualityProfiles,
4859}
4860
4861impl Default for DataQualitySchemaConfig {
4862    fn default() -> Self {
4863        Self {
4864            enabled: false,
4865            preset: DataQualityPreset::None,
4866            missing_values: MissingValuesSchemaConfig::default(),
4867            typos: TypoSchemaConfig::default(),
4868            format_variations: FormatVariationSchemaConfig::default(),
4869            duplicates: DuplicateSchemaConfig::default(),
4870            encoding_issues: EncodingIssueSchemaConfig::default(),
4871            generate_labels: true,
4872            sink_profiles: SinkQualityProfiles::default(),
4873        }
4874    }
4875}
4876
4877impl DataQualitySchemaConfig {
4878    /// Creates a config for a specific preset profile.
4879    pub fn with_preset(preset: DataQualityPreset) -> Self {
4880        let mut config = Self {
4881            preset,
4882            ..Default::default()
4883        };
4884        config.apply_preset();
4885        config
4886    }
4887
4888    /// Applies the preset settings to the individual configuration fields.
4889    /// Call this after deserializing if preset is not Custom or None.
4890    pub fn apply_preset(&mut self) {
4891        if !self.preset.overrides_settings() {
4892            return;
4893        }
4894
4895        self.enabled = true;
4896
4897        // Missing values
4898        self.missing_values.enabled = self.preset.missing_rate() > 0.0;
4899        self.missing_values.rate = self.preset.missing_rate();
4900
4901        // Typos
4902        self.typos.enabled = self.preset.typo_rate() > 0.0;
4903        self.typos.char_error_rate = self.preset.typo_rate();
4904
4905        // Duplicates
4906        self.duplicates.enabled = self.preset.duplicate_rate() > 0.0;
4907        self.duplicates.exact_duplicate_ratio = self.preset.duplicate_rate() * 0.4;
4908        self.duplicates.near_duplicate_ratio = self.preset.duplicate_rate() * 0.4;
4909        self.duplicates.fuzzy_duplicate_ratio = self.preset.duplicate_rate() * 0.2;
4910
4911        // Format variations
4912        self.format_variations.enabled = self.preset.format_variations_enabled();
4913
4914        // Encoding issues
4915        self.encoding_issues.enabled = self.preset.encoding_issues_enabled();
4916        self.encoding_issues.rate = self.preset.encoding_issue_rate();
4917
4918        // OCR errors for typos in legacy preset
4919        if self.preset.ocr_errors_enabled() {
4920            self.typos.type_weights.ocr_errors = 0.3;
4921        }
4922    }
4923
4924    /// Returns the effective missing value rate (considering preset).
4925    pub fn effective_missing_rate(&self) -> f64 {
4926        if self.preset.overrides_settings() {
4927            self.preset.missing_rate()
4928        } else {
4929            self.missing_values.rate
4930        }
4931    }
4932
4933    /// Returns the effective typo rate (considering preset).
4934    pub fn effective_typo_rate(&self) -> f64 {
4935        if self.preset.overrides_settings() {
4936            self.preset.typo_rate()
4937        } else {
4938            self.typos.char_error_rate
4939        }
4940    }
4941
4942    /// Returns the effective duplicate rate (considering preset).
4943    pub fn effective_duplicate_rate(&self) -> f64 {
4944        if self.preset.overrides_settings() {
4945            self.preset.duplicate_rate()
4946        } else {
4947            self.duplicates.exact_duplicate_ratio
4948                + self.duplicates.near_duplicate_ratio
4949                + self.duplicates.fuzzy_duplicate_ratio
4950        }
4951    }
4952
4953    /// Creates a clean profile config.
4954    pub fn clean() -> Self {
4955        Self::with_preset(DataQualityPreset::Clean)
4956    }
4957
4958    /// Creates a noisy profile config.
4959    pub fn noisy() -> Self {
4960        Self::with_preset(DataQualityPreset::Noisy)
4961    }
4962
4963    /// Creates a legacy profile config.
4964    pub fn legacy() -> Self {
4965        Self::with_preset(DataQualityPreset::Legacy)
4966    }
4967}
4968
4969/// Preset configurations for common data quality scenarios.
4970#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
4971#[serde(rename_all = "snake_case")]
4972pub enum DataQualityPreset {
4973    /// No data quality variations (clean data)
4974    #[default]
4975    None,
4976    /// Minimal variations (very clean data with rare issues)
4977    Minimal,
4978    /// Normal variations (realistic enterprise data quality)
4979    Normal,
4980    /// High variations (messy data for stress testing)
4981    High,
4982    /// Custom (use individual settings)
4983    Custom,
4984
4985    // ========================================
4986    // ML-Oriented Profiles (Phase 2.1)
4987    // ========================================
4988    /// Clean profile for ML training - minimal data quality issues
4989    /// Missing: 0.1%, Typos: 0.05%, Duplicates: 0%, Format: None
4990    Clean,
4991    /// Noisy profile simulating typical production data issues
4992    /// Missing: 5%, Typos: 2%, Duplicates: 1%, Format: Medium
4993    Noisy,
4994    /// Legacy profile simulating migrated/OCR'd historical data
4995    /// Missing: 10%, Typos: 5%, Duplicates: 3%, Format: Heavy + OCR
4996    Legacy,
4997}
4998
4999impl DataQualityPreset {
5000    /// Returns the missing value rate for this preset.
5001    pub fn missing_rate(&self) -> f64 {
5002        match self {
5003            DataQualityPreset::None => 0.0,
5004            DataQualityPreset::Minimal => 0.005,
5005            DataQualityPreset::Normal => 0.02,
5006            DataQualityPreset::High => 0.08,
5007            DataQualityPreset::Custom => 0.01, // Use config value
5008            DataQualityPreset::Clean => 0.001,
5009            DataQualityPreset::Noisy => 0.05,
5010            DataQualityPreset::Legacy => 0.10,
5011        }
5012    }
5013
5014    /// Returns the typo rate for this preset.
5015    pub fn typo_rate(&self) -> f64 {
5016        match self {
5017            DataQualityPreset::None => 0.0,
5018            DataQualityPreset::Minimal => 0.0005,
5019            DataQualityPreset::Normal => 0.002,
5020            DataQualityPreset::High => 0.01,
5021            DataQualityPreset::Custom => 0.001, // Use config value
5022            DataQualityPreset::Clean => 0.0005,
5023            DataQualityPreset::Noisy => 0.02,
5024            DataQualityPreset::Legacy => 0.05,
5025        }
5026    }
5027
5028    /// Returns the duplicate rate for this preset.
5029    pub fn duplicate_rate(&self) -> f64 {
5030        match self {
5031            DataQualityPreset::None => 0.0,
5032            DataQualityPreset::Minimal => 0.001,
5033            DataQualityPreset::Normal => 0.005,
5034            DataQualityPreset::High => 0.02,
5035            DataQualityPreset::Custom => 0.0, // Use config value
5036            DataQualityPreset::Clean => 0.0,
5037            DataQualityPreset::Noisy => 0.01,
5038            DataQualityPreset::Legacy => 0.03,
5039        }
5040    }
5041
5042    /// Returns whether format variations are enabled for this preset.
5043    pub fn format_variations_enabled(&self) -> bool {
5044        match self {
5045            DataQualityPreset::None | DataQualityPreset::Clean => false,
5046            DataQualityPreset::Minimal => true,
5047            DataQualityPreset::Normal => true,
5048            DataQualityPreset::High => true,
5049            DataQualityPreset::Custom => true,
5050            DataQualityPreset::Noisy => true,
5051            DataQualityPreset::Legacy => true,
5052        }
5053    }
5054
5055    /// Returns whether OCR-style errors are enabled for this preset.
5056    pub fn ocr_errors_enabled(&self) -> bool {
5057        matches!(self, DataQualityPreset::Legacy | DataQualityPreset::High)
5058    }
5059
5060    /// Returns whether encoding issues are enabled for this preset.
5061    pub fn encoding_issues_enabled(&self) -> bool {
5062        matches!(
5063            self,
5064            DataQualityPreset::Legacy | DataQualityPreset::High | DataQualityPreset::Noisy
5065        )
5066    }
5067
5068    /// Returns the encoding issue rate for this preset.
5069    pub fn encoding_issue_rate(&self) -> f64 {
5070        match self {
5071            DataQualityPreset::None | DataQualityPreset::Clean | DataQualityPreset::Minimal => 0.0,
5072            DataQualityPreset::Normal => 0.002,
5073            DataQualityPreset::High => 0.01,
5074            DataQualityPreset::Custom => 0.0,
5075            DataQualityPreset::Noisy => 0.005,
5076            DataQualityPreset::Legacy => 0.02,
5077        }
5078    }
5079
5080    /// Returns true if this preset overrides individual settings.
5081    pub fn overrides_settings(&self) -> bool {
5082        !matches!(self, DataQualityPreset::Custom | DataQualityPreset::None)
5083    }
5084
5085    /// Returns a human-readable description of this preset.
5086    pub fn description(&self) -> &'static str {
5087        match self {
5088            DataQualityPreset::None => "No data quality issues (pristine data)",
5089            DataQualityPreset::Minimal => "Very rare data quality issues",
5090            DataQualityPreset::Normal => "Realistic enterprise data quality",
5091            DataQualityPreset::High => "Messy data for stress testing",
5092            DataQualityPreset::Custom => "Custom settings from configuration",
5093            DataQualityPreset::Clean => "ML-ready clean data with minimal issues",
5094            DataQualityPreset::Noisy => "Typical production data with moderate issues",
5095            DataQualityPreset::Legacy => "Legacy/migrated data with heavy issues and OCR errors",
5096        }
5097    }
5098}
5099
5100/// Missing value injection configuration.
5101#[derive(Debug, Clone, Serialize, Deserialize)]
5102pub struct MissingValuesSchemaConfig {
5103    /// Enable missing value injection
5104    #[serde(default)]
5105    pub enabled: bool,
5106    /// Global missing rate (0.0 to 1.0)
5107    #[serde(default = "default_missing_rate")]
5108    pub rate: f64,
5109    /// Missing value strategy
5110    #[serde(default)]
5111    pub strategy: MissingValueStrategy,
5112    /// Field-specific rates (field name -> rate)
5113    #[serde(default)]
5114    pub field_rates: std::collections::HashMap<String, f64>,
5115    /// Fields that should never have missing values
5116    #[serde(default)]
5117    pub protected_fields: Vec<String>,
5118}
5119
5120fn default_missing_rate() -> f64 {
5121    0.01
5122}
5123
5124impl Default for MissingValuesSchemaConfig {
5125    fn default() -> Self {
5126        Self {
5127            enabled: false,
5128            rate: default_missing_rate(),
5129            strategy: MissingValueStrategy::Mcar,
5130            field_rates: std::collections::HashMap::new(),
5131            protected_fields: vec![
5132                "document_id".to_string(),
5133                "company_code".to_string(),
5134                "posting_date".to_string(),
5135            ],
5136        }
5137    }
5138}
5139
5140/// Missing value strategy types.
5141#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
5142#[serde(rename_all = "snake_case")]
5143pub enum MissingValueStrategy {
5144    /// Missing Completely At Random - equal probability for all values
5145    #[default]
5146    Mcar,
5147    /// Missing At Random - depends on other observed values
5148    Mar,
5149    /// Missing Not At Random - depends on the value itself
5150    Mnar,
5151    /// Systematic - entire field groups missing together
5152    Systematic,
5153}
5154
5155/// Typo injection configuration.
5156#[derive(Debug, Clone, Serialize, Deserialize)]
5157pub struct TypoSchemaConfig {
5158    /// Enable typo injection
5159    #[serde(default)]
5160    pub enabled: bool,
5161    /// Character error rate (per character, not per field)
5162    #[serde(default = "default_typo_rate")]
5163    pub char_error_rate: f64,
5164    /// Typo type weights
5165    #[serde(default)]
5166    pub type_weights: TypoTypeWeights,
5167    /// Fields that should never have typos
5168    #[serde(default)]
5169    pub protected_fields: Vec<String>,
5170}
5171
5172fn default_typo_rate() -> f64 {
5173    0.001
5174}
5175
5176impl Default for TypoSchemaConfig {
5177    fn default() -> Self {
5178        Self {
5179            enabled: false,
5180            char_error_rate: default_typo_rate(),
5181            type_weights: TypoTypeWeights::default(),
5182            protected_fields: vec![
5183                "document_id".to_string(),
5184                "gl_account".to_string(),
5185                "company_code".to_string(),
5186            ],
5187        }
5188    }
5189}
5190
5191/// Weights for different typo types.
5192#[derive(Debug, Clone, Serialize, Deserialize)]
5193pub struct TypoTypeWeights {
5194    /// Keyboard-adjacent substitution (e.g., 'a' -> 's')
5195    #[serde(default = "default_substitution_weight")]
5196    pub substitution: f64,
5197    /// Adjacent character transposition (e.g., 'ab' -> 'ba')
5198    #[serde(default = "default_transposition_weight")]
5199    pub transposition: f64,
5200    /// Character insertion
5201    #[serde(default = "default_insertion_weight")]
5202    pub insertion: f64,
5203    /// Character deletion
5204    #[serde(default = "default_deletion_weight")]
5205    pub deletion: f64,
5206    /// OCR-style errors (e.g., '0' -> 'O')
5207    #[serde(default = "default_ocr_weight")]
5208    pub ocr_errors: f64,
5209    /// Homophone substitution (e.g., 'their' -> 'there')
5210    #[serde(default = "default_homophone_weight")]
5211    pub homophones: f64,
5212}
5213
5214fn default_substitution_weight() -> f64 {
5215    0.35
5216}
5217fn default_transposition_weight() -> f64 {
5218    0.25
5219}
5220fn default_insertion_weight() -> f64 {
5221    0.10
5222}
5223fn default_deletion_weight() -> f64 {
5224    0.15
5225}
5226fn default_ocr_weight() -> f64 {
5227    0.10
5228}
5229fn default_homophone_weight() -> f64 {
5230    0.05
5231}
5232
5233impl Default for TypoTypeWeights {
5234    fn default() -> Self {
5235        Self {
5236            substitution: default_substitution_weight(),
5237            transposition: default_transposition_weight(),
5238            insertion: default_insertion_weight(),
5239            deletion: default_deletion_weight(),
5240            ocr_errors: default_ocr_weight(),
5241            homophones: default_homophone_weight(),
5242        }
5243    }
5244}
5245
5246/// Format variation configuration.
5247#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5248pub struct FormatVariationSchemaConfig {
5249    /// Enable format variations
5250    #[serde(default)]
5251    pub enabled: bool,
5252    /// Date format variation settings
5253    #[serde(default)]
5254    pub dates: DateFormatVariationConfig,
5255    /// Amount format variation settings
5256    #[serde(default)]
5257    pub amounts: AmountFormatVariationConfig,
5258    /// Identifier format variation settings
5259    #[serde(default)]
5260    pub identifiers: IdentifierFormatVariationConfig,
5261}
5262
5263/// Date format variation configuration.
5264#[derive(Debug, Clone, Serialize, Deserialize)]
5265pub struct DateFormatVariationConfig {
5266    /// Enable date format variations
5267    #[serde(default)]
5268    pub enabled: bool,
5269    /// Overall variation rate
5270    #[serde(default = "default_date_variation_rate")]
5271    pub rate: f64,
5272    /// Include ISO format (2024-01-15)
5273    #[serde(default = "default_true")]
5274    pub iso_format: bool,
5275    /// Include US format (01/15/2024)
5276    #[serde(default)]
5277    pub us_format: bool,
5278    /// Include EU format (15.01.2024)
5279    #[serde(default)]
5280    pub eu_format: bool,
5281    /// Include long format (January 15, 2024)
5282    #[serde(default)]
5283    pub long_format: bool,
5284}
5285
5286fn default_date_variation_rate() -> f64 {
5287    0.05
5288}
5289
5290impl Default for DateFormatVariationConfig {
5291    fn default() -> Self {
5292        Self {
5293            enabled: false,
5294            rate: default_date_variation_rate(),
5295            iso_format: true,
5296            us_format: false,
5297            eu_format: false,
5298            long_format: false,
5299        }
5300    }
5301}
5302
5303/// Amount format variation configuration.
5304#[derive(Debug, Clone, Serialize, Deserialize)]
5305pub struct AmountFormatVariationConfig {
5306    /// Enable amount format variations
5307    #[serde(default)]
5308    pub enabled: bool,
5309    /// Overall variation rate
5310    #[serde(default = "default_amount_variation_rate")]
5311    pub rate: f64,
5312    /// Include US comma format (1,234.56)
5313    #[serde(default)]
5314    pub us_comma_format: bool,
5315    /// Include EU format (1.234,56)
5316    #[serde(default)]
5317    pub eu_format: bool,
5318    /// Include currency prefix ($1,234.56)
5319    #[serde(default)]
5320    pub currency_prefix: bool,
5321    /// Include accounting format with parentheses for negatives
5322    #[serde(default)]
5323    pub accounting_format: bool,
5324}
5325
5326fn default_amount_variation_rate() -> f64 {
5327    0.02
5328}
5329
5330impl Default for AmountFormatVariationConfig {
5331    fn default() -> Self {
5332        Self {
5333            enabled: false,
5334            rate: default_amount_variation_rate(),
5335            us_comma_format: false,
5336            eu_format: false,
5337            currency_prefix: false,
5338            accounting_format: false,
5339        }
5340    }
5341}
5342
5343/// Identifier format variation configuration.
5344#[derive(Debug, Clone, Serialize, Deserialize)]
5345pub struct IdentifierFormatVariationConfig {
5346    /// Enable identifier format variations
5347    #[serde(default)]
5348    pub enabled: bool,
5349    /// Overall variation rate
5350    #[serde(default = "default_identifier_variation_rate")]
5351    pub rate: f64,
5352    /// Case variations (uppercase, lowercase, mixed)
5353    #[serde(default)]
5354    pub case_variations: bool,
5355    /// Padding variations (leading zeros)
5356    #[serde(default)]
5357    pub padding_variations: bool,
5358    /// Separator variations (dash vs underscore)
5359    #[serde(default)]
5360    pub separator_variations: bool,
5361}
5362
5363fn default_identifier_variation_rate() -> f64 {
5364    0.02
5365}
5366
5367impl Default for IdentifierFormatVariationConfig {
5368    fn default() -> Self {
5369        Self {
5370            enabled: false,
5371            rate: default_identifier_variation_rate(),
5372            case_variations: false,
5373            padding_variations: false,
5374            separator_variations: false,
5375        }
5376    }
5377}
5378
5379/// Duplicate injection configuration.
5380#[derive(Debug, Clone, Serialize, Deserialize)]
5381pub struct DuplicateSchemaConfig {
5382    /// Enable duplicate injection
5383    #[serde(default)]
5384    pub enabled: bool,
5385    /// Overall duplicate rate
5386    #[serde(default = "default_duplicate_rate")]
5387    pub rate: f64,
5388    /// Exact duplicate proportion (out of duplicates)
5389    #[serde(default = "default_exact_duplicate_ratio")]
5390    pub exact_duplicate_ratio: f64,
5391    /// Near duplicate proportion (slight variations)
5392    #[serde(default = "default_near_duplicate_ratio")]
5393    pub near_duplicate_ratio: f64,
5394    /// Fuzzy duplicate proportion (typos in key fields)
5395    #[serde(default = "default_fuzzy_duplicate_ratio")]
5396    pub fuzzy_duplicate_ratio: f64,
5397    /// Maximum date offset for near/fuzzy duplicates (days)
5398    #[serde(default = "default_max_date_offset")]
5399    pub max_date_offset_days: u32,
5400    /// Maximum amount variance for near duplicates (fraction)
5401    #[serde(default = "default_max_amount_variance")]
5402    pub max_amount_variance: f64,
5403}
5404
5405fn default_duplicate_rate() -> f64 {
5406    0.005
5407}
5408fn default_exact_duplicate_ratio() -> f64 {
5409    0.4
5410}
5411fn default_near_duplicate_ratio() -> f64 {
5412    0.35
5413}
5414fn default_fuzzy_duplicate_ratio() -> f64 {
5415    0.25
5416}
5417fn default_max_date_offset() -> u32 {
5418    3
5419}
5420fn default_max_amount_variance() -> f64 {
5421    0.01
5422}
5423
5424impl Default for DuplicateSchemaConfig {
5425    fn default() -> Self {
5426        Self {
5427            enabled: false,
5428            rate: default_duplicate_rate(),
5429            exact_duplicate_ratio: default_exact_duplicate_ratio(),
5430            near_duplicate_ratio: default_near_duplicate_ratio(),
5431            fuzzy_duplicate_ratio: default_fuzzy_duplicate_ratio(),
5432            max_date_offset_days: default_max_date_offset(),
5433            max_amount_variance: default_max_amount_variance(),
5434        }
5435    }
5436}
5437
5438/// Encoding issue configuration.
5439#[derive(Debug, Clone, Serialize, Deserialize)]
5440pub struct EncodingIssueSchemaConfig {
5441    /// Enable encoding issue injection
5442    #[serde(default)]
5443    pub enabled: bool,
5444    /// Overall encoding issue rate
5445    #[serde(default = "default_encoding_rate")]
5446    pub rate: f64,
5447    /// Include mojibake (UTF-8/Latin-1 confusion)
5448    #[serde(default)]
5449    pub mojibake: bool,
5450    /// Include HTML entity corruption
5451    #[serde(default)]
5452    pub html_entities: bool,
5453    /// Include BOM issues
5454    #[serde(default)]
5455    pub bom_issues: bool,
5456}
5457
5458fn default_encoding_rate() -> f64 {
5459    0.001
5460}
5461
5462impl Default for EncodingIssueSchemaConfig {
5463    fn default() -> Self {
5464        Self {
5465            enabled: false,
5466            rate: default_encoding_rate(),
5467            mojibake: false,
5468            html_entities: false,
5469            bom_issues: false,
5470        }
5471    }
5472}
5473
5474/// Per-sink quality profiles for different output formats.
5475#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5476pub struct SinkQualityProfiles {
5477    /// CSV-specific quality settings
5478    #[serde(default)]
5479    pub csv: Option<SinkQualityOverride>,
5480    /// JSON-specific quality settings
5481    #[serde(default)]
5482    pub json: Option<SinkQualityOverride>,
5483    /// Parquet-specific quality settings
5484    #[serde(default)]
5485    pub parquet: Option<SinkQualityOverride>,
5486}
5487
5488/// Quality setting overrides for a specific sink type.
5489#[derive(Debug, Clone, Serialize, Deserialize)]
5490pub struct SinkQualityOverride {
5491    /// Override enabled state
5492    pub enabled: Option<bool>,
5493    /// Override missing value rate
5494    pub missing_rate: Option<f64>,
5495    /// Override typo rate
5496    pub typo_rate: Option<f64>,
5497    /// Override format variation rate
5498    pub format_variation_rate: Option<f64>,
5499    /// Override duplicate rate
5500    pub duplicate_rate: Option<f64>,
5501}
5502
5503// =============================================================================
5504// Accounting Standards Configuration
5505// =============================================================================
5506
5507/// Accounting standards framework configuration for generating standards-compliant data.
5508///
5509/// Supports US GAAP, IFRS, and French GAAP (PCG) frameworks with specific standards:
5510/// - ASC 606/IFRS 15/PCG: Revenue Recognition
5511/// - ASC 842/IFRS 16/PCG: Leases
5512/// - ASC 820/IFRS 13/PCG: Fair Value Measurement
5513/// - ASC 360/IAS 36/PCG: Impairment
5514#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5515pub struct AccountingStandardsConfig {
5516    /// Enable accounting standards generation
5517    #[serde(default)]
5518    pub enabled: bool,
5519
5520    /// Accounting framework to use.
5521    /// When `None`, the country pack's `accounting.framework` is used as fallback;
5522    /// if that is also absent the orchestrator defaults to US GAAP.
5523    #[serde(default, skip_serializing_if = "Option::is_none")]
5524    pub framework: Option<AccountingFrameworkConfig>,
5525
5526    /// Revenue recognition configuration (ASC 606/IFRS 15)
5527    #[serde(default)]
5528    pub revenue_recognition: RevenueRecognitionConfig,
5529
5530    /// Lease accounting configuration (ASC 842/IFRS 16)
5531    #[serde(default)]
5532    pub leases: LeaseAccountingConfig,
5533
5534    /// Fair value measurement configuration (ASC 820/IFRS 13)
5535    #[serde(default)]
5536    pub fair_value: FairValueConfig,
5537
5538    /// Impairment testing configuration (ASC 360/IAS 36)
5539    #[serde(default)]
5540    pub impairment: ImpairmentConfig,
5541
5542    /// Business combination configuration (IFRS 3 / ASC 805)
5543    #[serde(default)]
5544    pub business_combinations: BusinessCombinationsConfig,
5545
5546    /// Expected Credit Loss configuration (IFRS 9 / ASC 326)
5547    #[serde(default)]
5548    pub expected_credit_loss: EclConfig,
5549
5550    /// Generate framework differences for dual reporting
5551    #[serde(default)]
5552    pub generate_differences: bool,
5553}
5554
5555/// Accounting framework selection.
5556#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
5557#[serde(rename_all = "snake_case")]
5558pub enum AccountingFrameworkConfig {
5559    /// US Generally Accepted Accounting Principles
5560    #[default]
5561    UsGaap,
5562    /// International Financial Reporting Standards
5563    Ifrs,
5564    /// Generate data for both frameworks with reconciliation
5565    DualReporting,
5566    /// French GAAP (Plan Comptable Général – PCG)
5567    FrenchGaap,
5568    /// German GAAP (Handelsgesetzbuch – HGB, §238-263)
5569    GermanGaap,
5570}
5571
5572/// Revenue recognition configuration (ASC 606/IFRS 15).
5573#[derive(Debug, Clone, Serialize, Deserialize)]
5574pub struct RevenueRecognitionConfig {
5575    /// Enable revenue recognition generation
5576    #[serde(default)]
5577    pub enabled: bool,
5578
5579    /// Generate customer contracts
5580    #[serde(default = "default_true")]
5581    pub generate_contracts: bool,
5582
5583    /// Average number of performance obligations per contract
5584    #[serde(default = "default_avg_obligations")]
5585    pub avg_obligations_per_contract: f64,
5586
5587    /// Rate of contracts with variable consideration
5588    #[serde(default = "default_variable_consideration_rate")]
5589    pub variable_consideration_rate: f64,
5590
5591    /// Rate of over-time revenue recognition (vs point-in-time)
5592    #[serde(default = "default_over_time_rate")]
5593    pub over_time_recognition_rate: f64,
5594
5595    /// Number of contracts to generate
5596    #[serde(default = "default_contract_count")]
5597    pub contract_count: usize,
5598}
5599
5600fn default_avg_obligations() -> f64 {
5601    2.0
5602}
5603
5604fn default_variable_consideration_rate() -> f64 {
5605    0.15
5606}
5607
5608fn default_over_time_rate() -> f64 {
5609    0.30
5610}
5611
5612fn default_contract_count() -> usize {
5613    100
5614}
5615
5616impl Default for RevenueRecognitionConfig {
5617    fn default() -> Self {
5618        Self {
5619            enabled: false,
5620            generate_contracts: true,
5621            avg_obligations_per_contract: default_avg_obligations(),
5622            variable_consideration_rate: default_variable_consideration_rate(),
5623            over_time_recognition_rate: default_over_time_rate(),
5624            contract_count: default_contract_count(),
5625        }
5626    }
5627}
5628
5629/// Lease accounting configuration (ASC 842/IFRS 16).
5630#[derive(Debug, Clone, Serialize, Deserialize)]
5631pub struct LeaseAccountingConfig {
5632    /// Enable lease accounting generation
5633    #[serde(default)]
5634    pub enabled: bool,
5635
5636    /// Number of leases to generate
5637    #[serde(default = "default_lease_count")]
5638    pub lease_count: usize,
5639
5640    /// Percentage of finance leases (vs operating)
5641    #[serde(default = "default_finance_lease_pct")]
5642    pub finance_lease_percent: f64,
5643
5644    /// Average lease term in months
5645    #[serde(default = "default_avg_lease_term")]
5646    pub avg_lease_term_months: u32,
5647
5648    /// Generate amortization schedules
5649    #[serde(default = "default_true")]
5650    pub generate_amortization: bool,
5651
5652    /// Real estate lease percentage
5653    #[serde(default = "default_real_estate_pct")]
5654    pub real_estate_percent: f64,
5655}
5656
5657fn default_lease_count() -> usize {
5658    50
5659}
5660
5661fn default_finance_lease_pct() -> f64 {
5662    0.30
5663}
5664
5665fn default_avg_lease_term() -> u32 {
5666    60
5667}
5668
5669fn default_real_estate_pct() -> f64 {
5670    0.40
5671}
5672
5673impl Default for LeaseAccountingConfig {
5674    fn default() -> Self {
5675        Self {
5676            enabled: false,
5677            lease_count: default_lease_count(),
5678            finance_lease_percent: default_finance_lease_pct(),
5679            avg_lease_term_months: default_avg_lease_term(),
5680            generate_amortization: true,
5681            real_estate_percent: default_real_estate_pct(),
5682        }
5683    }
5684}
5685
5686/// Fair value measurement configuration (ASC 820/IFRS 13).
5687#[derive(Debug, Clone, Serialize, Deserialize)]
5688pub struct FairValueConfig {
5689    /// Enable fair value measurement generation
5690    #[serde(default)]
5691    pub enabled: bool,
5692
5693    /// Number of fair value measurements to generate
5694    #[serde(default = "default_fv_count")]
5695    pub measurement_count: usize,
5696
5697    /// Level 1 (quoted prices) percentage
5698    #[serde(default = "default_level1_pct")]
5699    pub level1_percent: f64,
5700
5701    /// Level 2 (observable inputs) percentage
5702    #[serde(default = "default_level2_pct")]
5703    pub level2_percent: f64,
5704
5705    /// Level 3 (unobservable inputs) percentage
5706    #[serde(default = "default_level3_pct")]
5707    pub level3_percent: f64,
5708
5709    /// Include sensitivity analysis for Level 3
5710    #[serde(default)]
5711    pub include_sensitivity_analysis: bool,
5712}
5713
5714fn default_fv_count() -> usize {
5715    25
5716}
5717
5718fn default_level1_pct() -> f64 {
5719    0.40
5720}
5721
5722fn default_level2_pct() -> f64 {
5723    0.35
5724}
5725
5726fn default_level3_pct() -> f64 {
5727    0.25
5728}
5729
5730impl Default for FairValueConfig {
5731    fn default() -> Self {
5732        Self {
5733            enabled: false,
5734            measurement_count: default_fv_count(),
5735            level1_percent: default_level1_pct(),
5736            level2_percent: default_level2_pct(),
5737            level3_percent: default_level3_pct(),
5738            include_sensitivity_analysis: false,
5739        }
5740    }
5741}
5742
5743/// Impairment testing configuration (ASC 360/IAS 36).
5744#[derive(Debug, Clone, Serialize, Deserialize)]
5745pub struct ImpairmentConfig {
5746    /// Enable impairment testing generation
5747    #[serde(default)]
5748    pub enabled: bool,
5749
5750    /// Number of impairment tests to generate
5751    #[serde(default = "default_impairment_count")]
5752    pub test_count: usize,
5753
5754    /// Rate of tests resulting in impairment
5755    #[serde(default = "default_impairment_rate")]
5756    pub impairment_rate: f64,
5757
5758    /// Generate cash flow projections
5759    #[serde(default = "default_true")]
5760    pub generate_projections: bool,
5761
5762    /// Include goodwill impairment tests
5763    #[serde(default)]
5764    pub include_goodwill: bool,
5765}
5766
5767fn default_impairment_count() -> usize {
5768    15
5769}
5770
5771fn default_impairment_rate() -> f64 {
5772    0.10
5773}
5774
5775impl Default for ImpairmentConfig {
5776    fn default() -> Self {
5777        Self {
5778            enabled: false,
5779            test_count: default_impairment_count(),
5780            impairment_rate: default_impairment_rate(),
5781            generate_projections: true,
5782            include_goodwill: false,
5783        }
5784    }
5785}
5786
5787// =============================================================================
5788// Business Combinations Configuration (IFRS 3 / ASC 805)
5789// =============================================================================
5790
5791/// Configuration for generating business combination (acquisition) data.
5792#[derive(Debug, Clone, Serialize, Deserialize)]
5793pub struct BusinessCombinationsConfig {
5794    /// Enable business combination generation
5795    #[serde(default)]
5796    pub enabled: bool,
5797
5798    /// Number of acquisitions to generate per company (1-5)
5799    #[serde(default = "default_bc_acquisition_count")]
5800    pub acquisition_count: usize,
5801}
5802
5803fn default_bc_acquisition_count() -> usize {
5804    2
5805}
5806
5807impl Default for BusinessCombinationsConfig {
5808    fn default() -> Self {
5809        Self {
5810            enabled: false,
5811            acquisition_count: default_bc_acquisition_count(),
5812        }
5813    }
5814}
5815
5816// =============================================================================
5817// ECL Configuration (IFRS 9 / ASC 326)
5818// =============================================================================
5819
5820/// Configuration for Expected Credit Loss generation.
5821#[derive(Debug, Clone, Serialize, Deserialize)]
5822pub struct EclConfig {
5823    /// Enable ECL generation.
5824    #[serde(default)]
5825    pub enabled: bool,
5826
5827    /// Weight for base economic scenario (0–1).
5828    #[serde(default = "default_ecl_base_weight")]
5829    pub base_scenario_weight: f64,
5830
5831    /// Multiplier for base scenario (typically 1.0).
5832    #[serde(default = "default_ecl_base_multiplier")]
5833    pub base_scenario_multiplier: f64,
5834
5835    /// Weight for optimistic economic scenario (0–1).
5836    #[serde(default = "default_ecl_optimistic_weight")]
5837    pub optimistic_scenario_weight: f64,
5838
5839    /// Multiplier for optimistic scenario (< 1.0 means lower losses).
5840    #[serde(default = "default_ecl_optimistic_multiplier")]
5841    pub optimistic_scenario_multiplier: f64,
5842
5843    /// Weight for pessimistic economic scenario (0–1).
5844    #[serde(default = "default_ecl_pessimistic_weight")]
5845    pub pessimistic_scenario_weight: f64,
5846
5847    /// Multiplier for pessimistic scenario (> 1.0 means higher losses).
5848    #[serde(default = "default_ecl_pessimistic_multiplier")]
5849    pub pessimistic_scenario_multiplier: f64,
5850}
5851
5852fn default_ecl_base_weight() -> f64 {
5853    0.50
5854}
5855fn default_ecl_base_multiplier() -> f64 {
5856    1.0
5857}
5858fn default_ecl_optimistic_weight() -> f64 {
5859    0.30
5860}
5861fn default_ecl_optimistic_multiplier() -> f64 {
5862    0.8
5863}
5864fn default_ecl_pessimistic_weight() -> f64 {
5865    0.20
5866}
5867fn default_ecl_pessimistic_multiplier() -> f64 {
5868    1.4
5869}
5870
5871impl Default for EclConfig {
5872    fn default() -> Self {
5873        Self {
5874            enabled: false,
5875            base_scenario_weight: default_ecl_base_weight(),
5876            base_scenario_multiplier: default_ecl_base_multiplier(),
5877            optimistic_scenario_weight: default_ecl_optimistic_weight(),
5878            optimistic_scenario_multiplier: default_ecl_optimistic_multiplier(),
5879            pessimistic_scenario_weight: default_ecl_pessimistic_weight(),
5880            pessimistic_scenario_multiplier: default_ecl_pessimistic_multiplier(),
5881        }
5882    }
5883}
5884
5885// =============================================================================
5886// Audit Standards Configuration
5887// =============================================================================
5888
5889/// Audit standards framework configuration for generating standards-compliant audit data.
5890///
5891/// Supports ISA (International Standards on Auditing) and PCAOB standards:
5892/// - ISA 200-720: Complete coverage of audit standards
5893/// - ISA 520: Analytical Procedures
5894/// - ISA 505: External Confirmations
5895/// - ISA 700/705/706/701: Audit Reports
5896/// - PCAOB AS 2201: ICFR Auditing
5897#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5898pub struct AuditStandardsConfig {
5899    /// Enable audit standards generation
5900    #[serde(default)]
5901    pub enabled: bool,
5902
5903    /// ISA compliance configuration
5904    #[serde(default)]
5905    pub isa_compliance: IsaComplianceConfig,
5906
5907    /// Analytical procedures configuration (ISA 520)
5908    #[serde(default)]
5909    pub analytical_procedures: AnalyticalProceduresConfig,
5910
5911    /// External confirmations configuration (ISA 505)
5912    #[serde(default)]
5913    pub confirmations: ConfirmationsConfig,
5914
5915    /// Audit opinion configuration (ISA 700/705/706/701)
5916    #[serde(default)]
5917    pub opinion: AuditOpinionConfig,
5918
5919    /// Generate complete audit trail with traceability
5920    #[serde(default)]
5921    pub generate_audit_trail: bool,
5922
5923    /// SOX 302/404 compliance configuration
5924    #[serde(default)]
5925    pub sox: SoxComplianceConfig,
5926
5927    /// PCAOB-specific configuration
5928    #[serde(default)]
5929    pub pcaob: PcaobConfig,
5930}
5931
5932/// ISA compliance level configuration.
5933#[derive(Debug, Clone, Serialize, Deserialize)]
5934pub struct IsaComplianceConfig {
5935    /// Enable ISA compliance tracking
5936    #[serde(default)]
5937    pub enabled: bool,
5938
5939    /// Compliance level: "basic", "standard", "comprehensive"
5940    #[serde(default = "default_compliance_level")]
5941    pub compliance_level: String,
5942
5943    /// Generate ISA requirement mappings
5944    #[serde(default = "default_true")]
5945    pub generate_isa_mappings: bool,
5946
5947    /// Generate ISA coverage summary
5948    #[serde(default = "default_true")]
5949    pub generate_coverage_summary: bool,
5950
5951    /// Include PCAOB standard mappings (for dual framework)
5952    #[serde(default)]
5953    pub include_pcaob: bool,
5954
5955    /// Framework to use: "isa", "pcaob", "dual"
5956    #[serde(default = "default_audit_framework")]
5957    pub framework: String,
5958}
5959
5960fn default_compliance_level() -> String {
5961    "standard".to_string()
5962}
5963
5964fn default_audit_framework() -> String {
5965    "isa".to_string()
5966}
5967
5968impl Default for IsaComplianceConfig {
5969    fn default() -> Self {
5970        Self {
5971            enabled: false,
5972            compliance_level: default_compliance_level(),
5973            generate_isa_mappings: true,
5974            generate_coverage_summary: true,
5975            include_pcaob: false,
5976            framework: default_audit_framework(),
5977        }
5978    }
5979}
5980
5981/// Analytical procedures configuration (ISA 520).
5982#[derive(Debug, Clone, Serialize, Deserialize)]
5983pub struct AnalyticalProceduresConfig {
5984    /// Enable analytical procedures generation
5985    #[serde(default)]
5986    pub enabled: bool,
5987
5988    /// Number of procedures per account/area
5989    #[serde(default = "default_procedures_per_account")]
5990    pub procedures_per_account: usize,
5991
5992    /// Probability of variance exceeding threshold
5993    #[serde(default = "default_variance_probability")]
5994    pub variance_probability: f64,
5995
5996    /// Include variance investigations
5997    #[serde(default = "default_true")]
5998    pub generate_investigations: bool,
5999
6000    /// Include financial ratio analysis
6001    #[serde(default = "default_true")]
6002    pub include_ratio_analysis: bool,
6003}
6004
6005fn default_procedures_per_account() -> usize {
6006    3
6007}
6008
6009fn default_variance_probability() -> f64 {
6010    0.20
6011}
6012
6013impl Default for AnalyticalProceduresConfig {
6014    fn default() -> Self {
6015        Self {
6016            enabled: false,
6017            procedures_per_account: default_procedures_per_account(),
6018            variance_probability: default_variance_probability(),
6019            generate_investigations: true,
6020            include_ratio_analysis: true,
6021        }
6022    }
6023}
6024
6025/// External confirmations configuration (ISA 505).
6026#[derive(Debug, Clone, Serialize, Deserialize)]
6027pub struct ConfirmationsConfig {
6028    /// Enable confirmation generation
6029    #[serde(default)]
6030    pub enabled: bool,
6031
6032    /// Number of confirmations to generate
6033    #[serde(default = "default_confirmation_count")]
6034    pub confirmation_count: usize,
6035
6036    /// Positive response rate
6037    #[serde(default = "default_positive_response_rate")]
6038    pub positive_response_rate: f64,
6039
6040    /// Exception rate (responses with differences)
6041    #[serde(default = "default_exception_rate_confirm")]
6042    pub exception_rate: f64,
6043
6044    /// Non-response rate
6045    #[serde(default = "default_non_response_rate")]
6046    pub non_response_rate: f64,
6047
6048    /// Generate alternative procedures for non-responses
6049    #[serde(default = "default_true")]
6050    pub generate_alternative_procedures: bool,
6051}
6052
6053fn default_confirmation_count() -> usize {
6054    50
6055}
6056
6057fn default_positive_response_rate() -> f64 {
6058    0.85
6059}
6060
6061fn default_exception_rate_confirm() -> f64 {
6062    0.10
6063}
6064
6065fn default_non_response_rate() -> f64 {
6066    0.05
6067}
6068
6069impl Default for ConfirmationsConfig {
6070    fn default() -> Self {
6071        Self {
6072            enabled: false,
6073            confirmation_count: default_confirmation_count(),
6074            positive_response_rate: default_positive_response_rate(),
6075            exception_rate: default_exception_rate_confirm(),
6076            non_response_rate: default_non_response_rate(),
6077            generate_alternative_procedures: true,
6078        }
6079    }
6080}
6081
6082/// Audit opinion configuration (ISA 700/705/706/701).
6083#[derive(Debug, Clone, Serialize, Deserialize)]
6084pub struct AuditOpinionConfig {
6085    /// Enable audit opinion generation
6086    #[serde(default)]
6087    pub enabled: bool,
6088
6089    /// Generate Key Audit Matters (KAM) / Critical Audit Matters (CAM)
6090    #[serde(default = "default_true")]
6091    pub generate_kam: bool,
6092
6093    /// Average number of KAMs/CAMs per opinion
6094    #[serde(default = "default_kam_count")]
6095    pub average_kam_count: usize,
6096
6097    /// Rate of modified opinions
6098    #[serde(default = "default_modified_opinion_rate")]
6099    pub modified_opinion_rate: f64,
6100
6101    /// Include emphasis of matter paragraphs
6102    #[serde(default)]
6103    pub include_emphasis_of_matter: bool,
6104
6105    /// Include going concern conclusions
6106    #[serde(default = "default_true")]
6107    pub include_going_concern: bool,
6108}
6109
6110fn default_kam_count() -> usize {
6111    3
6112}
6113
6114fn default_modified_opinion_rate() -> f64 {
6115    0.05
6116}
6117
6118impl Default for AuditOpinionConfig {
6119    fn default() -> Self {
6120        Self {
6121            enabled: false,
6122            generate_kam: true,
6123            average_kam_count: default_kam_count(),
6124            modified_opinion_rate: default_modified_opinion_rate(),
6125            include_emphasis_of_matter: false,
6126            include_going_concern: true,
6127        }
6128    }
6129}
6130
6131/// SOX compliance configuration (Sections 302/404).
6132#[derive(Debug, Clone, Serialize, Deserialize)]
6133pub struct SoxComplianceConfig {
6134    /// Enable SOX compliance generation
6135    #[serde(default)]
6136    pub enabled: bool,
6137
6138    /// Generate Section 302 CEO/CFO certifications
6139    #[serde(default = "default_true")]
6140    pub generate_302_certifications: bool,
6141
6142    /// Generate Section 404 ICFR assessments
6143    #[serde(default = "default_true")]
6144    pub generate_404_assessments: bool,
6145
6146    /// Materiality threshold for SOX testing
6147    #[serde(default = "default_sox_materiality_threshold")]
6148    pub materiality_threshold: f64,
6149
6150    /// Rate of material weaknesses
6151    #[serde(default = "default_material_weakness_rate")]
6152    pub material_weakness_rate: f64,
6153
6154    /// Rate of significant deficiencies
6155    #[serde(default = "default_significant_deficiency_rate")]
6156    pub significant_deficiency_rate: f64,
6157}
6158
6159fn default_material_weakness_rate() -> f64 {
6160    0.02
6161}
6162
6163fn default_significant_deficiency_rate() -> f64 {
6164    0.08
6165}
6166
6167impl Default for SoxComplianceConfig {
6168    fn default() -> Self {
6169        Self {
6170            enabled: false,
6171            generate_302_certifications: true,
6172            generate_404_assessments: true,
6173            materiality_threshold: default_sox_materiality_threshold(),
6174            material_weakness_rate: default_material_weakness_rate(),
6175            significant_deficiency_rate: default_significant_deficiency_rate(),
6176        }
6177    }
6178}
6179
6180/// PCAOB-specific configuration.
6181#[derive(Debug, Clone, Serialize, Deserialize)]
6182pub struct PcaobConfig {
6183    /// Enable PCAOB-specific elements
6184    #[serde(default)]
6185    pub enabled: bool,
6186
6187    /// Treat as PCAOB audit (vs ISA-only)
6188    #[serde(default)]
6189    pub is_pcaob_audit: bool,
6190
6191    /// Generate Critical Audit Matters (CAM)
6192    #[serde(default = "default_true")]
6193    pub generate_cam: bool,
6194
6195    /// Include ICFR opinion (for integrated audits)
6196    #[serde(default)]
6197    pub include_icfr_opinion: bool,
6198
6199    /// Generate PCAOB-ISA standard mappings
6200    #[serde(default)]
6201    pub generate_standard_mappings: bool,
6202}
6203
6204impl Default for PcaobConfig {
6205    fn default() -> Self {
6206        Self {
6207            enabled: false,
6208            is_pcaob_audit: false,
6209            generate_cam: true,
6210            include_icfr_opinion: false,
6211            generate_standard_mappings: false,
6212        }
6213    }
6214}
6215
6216// =============================================================================
6217// Advanced Distribution Configuration
6218// =============================================================================
6219
6220/// Advanced distribution configuration for realistic data generation.
6221///
6222/// This section enables sophisticated distribution models including:
6223/// - Mixture models (multi-modal distributions)
6224/// - Cross-field correlations
6225/// - Conditional distributions
6226/// - Regime changes and economic cycles
6227/// - Statistical validation
6228#[derive(Debug, Clone, Serialize, Deserialize, Default)]
6229pub struct AdvancedDistributionConfig {
6230    /// Enable advanced distribution features.
6231    #[serde(default)]
6232    pub enabled: bool,
6233
6234    /// Mixture model configuration for amounts.
6235    #[serde(default)]
6236    pub amounts: MixtureDistributionSchemaConfig,
6237
6238    /// Cross-field correlation configuration.
6239    #[serde(default)]
6240    pub correlations: CorrelationSchemaConfig,
6241
6242    /// Conditional distribution configurations.
6243    #[serde(default)]
6244    pub conditional: Vec<ConditionalDistributionSchemaConfig>,
6245
6246    /// Regime change configuration.
6247    #[serde(default)]
6248    pub regime_changes: RegimeChangeSchemaConfig,
6249
6250    /// Industry-specific distribution profile.
6251    #[serde(default)]
6252    pub industry_profile: Option<IndustryProfileType>,
6253
6254    /// Statistical validation configuration.
6255    #[serde(default)]
6256    pub validation: StatisticalValidationSchemaConfig,
6257
6258    /// v3.4.4+ — Pareto heavy-tailed distribution for monetary amounts.
6259    /// When set and `enabled`, overrides `amounts` mixture model for the
6260    /// non-fraud amount-sampling path (fraud patterns remain orthogonal).
6261    /// Useful for capex, strategic contracts, and any domain where a small
6262    /// number of very large values dominates the tail.
6263    #[serde(default)]
6264    pub pareto: Option<ParetoSchemaConfig>,
6265}
6266
6267/// Schema-level Pareto distribution configuration (v3.4.4+).
6268///
6269/// Thin wrapper around `datasynth_core::distributions::ParetoConfig` that
6270/// adds an `enabled` gate and serde-friendly field names.
6271#[derive(Debug, Clone, Serialize, Deserialize)]
6272pub struct ParetoSchemaConfig {
6273    /// Enable Pareto sampling. When true, replaces the `amounts` mixture
6274    /// model for the non-fraud amount-sampling path.
6275    #[serde(default)]
6276    pub enabled: bool,
6277
6278    /// Shape parameter (tail heaviness). Lower values → heavier tail.
6279    /// Typical range: 1.5-3.0. Default: 2.0.
6280    #[serde(default = "default_pareto_alpha")]
6281    pub alpha: f64,
6282
6283    /// Scale / minimum value. All samples are >= x_min.
6284    /// Typical: 1000 (for capex) to 100,000 (for large contracts). Default: 100.
6285    #[serde(default = "default_pareto_x_min")]
6286    pub x_min: f64,
6287
6288    /// Optional upper clamp. `None` = unbounded (recommended for realistic
6289    /// heavy tails).
6290    #[serde(default)]
6291    pub max_value: Option<f64>,
6292
6293    /// Decimal places for rounding. Default: 2.
6294    #[serde(default = "default_pareto_decimal_places")]
6295    pub decimal_places: u8,
6296}
6297
6298fn default_pareto_alpha() -> f64 {
6299    2.0
6300}
6301
6302fn default_pareto_x_min() -> f64 {
6303    100.0
6304}
6305
6306fn default_pareto_decimal_places() -> u8 {
6307    2
6308}
6309
6310impl Default for ParetoSchemaConfig {
6311    fn default() -> Self {
6312        Self {
6313            enabled: false,
6314            alpha: default_pareto_alpha(),
6315            x_min: default_pareto_x_min(),
6316            max_value: None,
6317            decimal_places: default_pareto_decimal_places(),
6318        }
6319    }
6320}
6321
6322impl ParetoSchemaConfig {
6323    /// Convert this schema config into a `datasynth_core::distributions::ParetoConfig`.
6324    pub fn to_core_config(&self) -> datasynth_core::distributions::ParetoConfig {
6325        datasynth_core::distributions::ParetoConfig {
6326            alpha: self.alpha,
6327            x_min: self.x_min,
6328            max_value: self.max_value,
6329            decimal_places: self.decimal_places,
6330        }
6331    }
6332}
6333
6334/// Industry profile types for pre-configured distribution settings.
6335#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6336#[serde(rename_all = "snake_case")]
6337pub enum IndustryProfileType {
6338    /// Retail industry profile (POS sales, inventory, seasonal)
6339    Retail,
6340    /// Manufacturing industry profile (raw materials, maintenance, capital)
6341    Manufacturing,
6342    /// Financial services profile (wire transfers, ACH, fee income)
6343    FinancialServices,
6344    /// Healthcare profile (claims, procedures, supplies)
6345    Healthcare,
6346    /// Technology profile (subscriptions, services, R&D)
6347    Technology,
6348}
6349
6350/// Mixture model distribution configuration.
6351#[derive(Debug, Clone, Serialize, Deserialize)]
6352pub struct MixtureDistributionSchemaConfig {
6353    /// Enable mixture model for amount generation.
6354    #[serde(default)]
6355    pub enabled: bool,
6356
6357    /// Distribution type: "gaussian" or "lognormal".
6358    #[serde(default = "default_mixture_type")]
6359    pub distribution_type: MixtureDistributionType,
6360
6361    /// Mixture components with weights.
6362    #[serde(default)]
6363    pub components: Vec<MixtureComponentConfig>,
6364
6365    /// Minimum value constraint.
6366    #[serde(default = "default_min_amount")]
6367    pub min_value: f64,
6368
6369    /// Maximum value constraint (optional).
6370    #[serde(default)]
6371    pub max_value: Option<f64>,
6372
6373    /// Decimal places for rounding.
6374    #[serde(default = "default_decimal_places")]
6375    pub decimal_places: u8,
6376}
6377
6378fn default_mixture_type() -> MixtureDistributionType {
6379    MixtureDistributionType::LogNormal
6380}
6381
6382fn default_min_amount() -> f64 {
6383    0.01
6384}
6385
6386fn default_decimal_places() -> u8 {
6387    2
6388}
6389
6390impl Default for MixtureDistributionSchemaConfig {
6391    fn default() -> Self {
6392        Self {
6393            enabled: false,
6394            distribution_type: MixtureDistributionType::LogNormal,
6395            components: Vec::new(),
6396            min_value: 0.01,
6397            max_value: None,
6398            decimal_places: 2,
6399        }
6400    }
6401}
6402
6403impl MixtureDistributionSchemaConfig {
6404    /// Convert this schema-level config into a [`LogNormalMixtureConfig`]
6405    /// suitable for `LogNormalMixtureSampler::new`. Returns `None` if there
6406    /// are no components (schema default is an empty list, which cannot
6407    /// drive a sampler).
6408    ///
6409    /// Callers should gate this with `self.enabled` before invoking.
6410    pub fn to_log_normal_config(
6411        &self,
6412    ) -> Option<datasynth_core::distributions::LogNormalMixtureConfig> {
6413        if self.components.is_empty() {
6414            return None;
6415        }
6416        Some(datasynth_core::distributions::LogNormalMixtureConfig {
6417            components: self
6418                .components
6419                .iter()
6420                .map(|c| match &c.label {
6421                    Some(lbl) => datasynth_core::distributions::LogNormalComponent::with_label(
6422                        c.weight,
6423                        c.mu,
6424                        c.sigma,
6425                        lbl.clone(),
6426                    ),
6427                    None => datasynth_core::distributions::LogNormalComponent::new(
6428                        c.weight, c.mu, c.sigma,
6429                    ),
6430                })
6431                .collect(),
6432            min_value: self.min_value,
6433            max_value: self.max_value,
6434            decimal_places: self.decimal_places,
6435        })
6436    }
6437
6438    /// Convert this schema-level config into a [`GaussianMixtureConfig`].
6439    /// Returns `None` if there are no components.
6440    pub fn to_gaussian_config(
6441        &self,
6442    ) -> Option<datasynth_core::distributions::GaussianMixtureConfig> {
6443        if self.components.is_empty() {
6444            return None;
6445        }
6446        Some(datasynth_core::distributions::GaussianMixtureConfig {
6447            components: self
6448                .components
6449                .iter()
6450                .map(|c| {
6451                    datasynth_core::distributions::GaussianComponent::new(c.weight, c.mu, c.sigma)
6452                })
6453                .collect(),
6454            allow_negative: true,
6455            min_value: Some(self.min_value),
6456            max_value: self.max_value,
6457        })
6458    }
6459}
6460
6461/// Mixture distribution type.
6462#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6463#[serde(rename_all = "snake_case")]
6464pub enum MixtureDistributionType {
6465    /// Gaussian (normal) mixture
6466    Gaussian,
6467    /// Log-normal mixture (for positive amounts)
6468    #[default]
6469    LogNormal,
6470}
6471
6472/// Configuration for a single mixture component.
6473#[derive(Debug, Clone, Serialize, Deserialize)]
6474pub struct MixtureComponentConfig {
6475    /// Weight of this component (must sum to 1.0 across all components).
6476    pub weight: f64,
6477
6478    /// Location parameter (mean for Gaussian, mu for log-normal).
6479    pub mu: f64,
6480
6481    /// Scale parameter (std dev for Gaussian, sigma for log-normal).
6482    pub sigma: f64,
6483
6484    /// Optional label for this component (e.g., "routine", "significant", "major").
6485    #[serde(default)]
6486    pub label: Option<String>,
6487}
6488
6489/// Cross-field correlation configuration.
6490#[derive(Debug, Clone, Serialize, Deserialize)]
6491pub struct CorrelationSchemaConfig {
6492    /// Enable correlation modeling.
6493    #[serde(default)]
6494    pub enabled: bool,
6495
6496    /// Copula type for dependency modeling.
6497    #[serde(default)]
6498    pub copula_type: CopulaSchemaType,
6499
6500    /// Field definitions for correlation.
6501    #[serde(default)]
6502    pub fields: Vec<CorrelatedFieldConfig>,
6503
6504    /// Correlation matrix (upper triangular, row-major).
6505    /// For n fields, this should have n*(n-1)/2 values.
6506    #[serde(default)]
6507    pub matrix: Vec<f64>,
6508
6509    /// Expected correlations for validation.
6510    #[serde(default)]
6511    pub expected_correlations: Vec<ExpectedCorrelationConfig>,
6512}
6513
6514impl Default for CorrelationSchemaConfig {
6515    fn default() -> Self {
6516        Self {
6517            enabled: false,
6518            copula_type: CopulaSchemaType::Gaussian,
6519            fields: Vec::new(),
6520            matrix: Vec::new(),
6521            expected_correlations: Vec::new(),
6522        }
6523    }
6524}
6525
6526impl CorrelationSchemaConfig {
6527    /// v3.5.4+: extract the correlation for a specific field pair from
6528    /// either the upper-triangular flat matrix (n*(n-1)/2 values) or a
6529    /// full symmetric n×n matrix (n*n values). Returns `None` when the
6530    /// named fields aren't both present or the matrix shape doesn't
6531    /// match.
6532    pub fn correlation_between(&self, field_a: &str, field_b: &str) -> Option<f64> {
6533        let idx_a = self.fields.iter().position(|f| f.name == field_a)?;
6534        let idx_b = self.fields.iter().position(|f| f.name == field_b)?;
6535        if idx_a == idx_b {
6536            return Some(1.0);
6537        }
6538        let (i, j) = if idx_a < idx_b {
6539            (idx_a, idx_b)
6540        } else {
6541            (idx_b, idx_a)
6542        };
6543        let n = self.fields.len();
6544        // Full n×n symmetric matrix?
6545        if self.matrix.len() == n * n {
6546            return self.matrix.get(idx_a * n + idx_b).copied();
6547        }
6548        // Upper triangular flat (row-major, excluding diagonal)?
6549        let expected_tri = n * (n - 1) / 2;
6550        if self.matrix.len() == expected_tri {
6551            // Row i, col j where j > i: flat index is
6552            //   sum_{k=0..i}((n-1-k)) + (j - i - 1)
6553            // = i*(n-1) - i*(i-1)/2 + (j - i - 1)
6554            let flat = i * (n - 1) - i * (i.saturating_sub(1)) / 2 + (j - i - 1);
6555            return self.matrix.get(flat).copied();
6556        }
6557        None
6558    }
6559
6560    /// Convert this schema config to a core `CopulaConfig` when the
6561    /// declared field pair `(field_a, field_b)` has a valid correlation
6562    /// entry. Returns `None` when disabled, fields missing, or matrix
6563    /// malformed.
6564    pub fn to_core_config_for_pair(
6565        &self,
6566        field_a: &str,
6567        field_b: &str,
6568    ) -> Option<datasynth_core::distributions::CopulaConfig> {
6569        if !self.enabled {
6570            return None;
6571        }
6572        let rho = self.correlation_between(field_a, field_b)?;
6573        use datasynth_core::distributions::{CopulaConfig, CopulaType};
6574        let copula_type = match self.copula_type {
6575            CopulaSchemaType::Gaussian => CopulaType::Gaussian,
6576            CopulaSchemaType::Clayton => CopulaType::Clayton,
6577            CopulaSchemaType::Gumbel => CopulaType::Gumbel,
6578            CopulaSchemaType::Frank => CopulaType::Frank,
6579            CopulaSchemaType::StudentT => CopulaType::StudentT,
6580        };
6581        // Gaussian / StudentT interpret theta as correlation; others
6582        // as a shape parameter. Minimal v3.5.4 only wires Gaussian in
6583        // the runtime, but the converter is general so follow-ups can
6584        // light up the other copulas.
6585        let theta = rho.clamp(-0.999, 0.999);
6586        Some(CopulaConfig {
6587            copula_type,
6588            theta,
6589            degrees_of_freedom: 4.0,
6590        })
6591    }
6592}
6593
6594/// Copula type for dependency modeling.
6595#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6596#[serde(rename_all = "snake_case")]
6597pub enum CopulaSchemaType {
6598    /// Gaussian copula (symmetric, no tail dependence)
6599    #[default]
6600    Gaussian,
6601    /// Clayton copula (lower tail dependence)
6602    Clayton,
6603    /// Gumbel copula (upper tail dependence)
6604    Gumbel,
6605    /// Frank copula (symmetric, no tail dependence)
6606    Frank,
6607    /// Student-t copula (both tail dependencies)
6608    StudentT,
6609}
6610
6611/// Configuration for a correlated field.
6612#[derive(Debug, Clone, Serialize, Deserialize)]
6613pub struct CorrelatedFieldConfig {
6614    /// Field name.
6615    pub name: String,
6616
6617    /// Marginal distribution type.
6618    #[serde(default)]
6619    pub distribution: MarginalDistributionConfig,
6620}
6621
6622/// Marginal distribution configuration.
6623#[derive(Debug, Clone, Serialize, Deserialize)]
6624#[serde(tag = "type", rename_all = "snake_case")]
6625pub enum MarginalDistributionConfig {
6626    /// Normal distribution.
6627    Normal {
6628        /// Mean
6629        mu: f64,
6630        /// Standard deviation
6631        sigma: f64,
6632    },
6633    /// Log-normal distribution.
6634    LogNormal {
6635        /// Location parameter
6636        mu: f64,
6637        /// Scale parameter
6638        sigma: f64,
6639    },
6640    /// Uniform distribution.
6641    Uniform {
6642        /// Minimum value
6643        min: f64,
6644        /// Maximum value
6645        max: f64,
6646    },
6647    /// Discrete uniform distribution.
6648    DiscreteUniform {
6649        /// Minimum integer value
6650        min: i32,
6651        /// Maximum integer value
6652        max: i32,
6653    },
6654}
6655
6656impl Default for MarginalDistributionConfig {
6657    fn default() -> Self {
6658        Self::Normal {
6659            mu: 0.0,
6660            sigma: 1.0,
6661        }
6662    }
6663}
6664
6665/// Expected correlation for validation.
6666#[derive(Debug, Clone, Serialize, Deserialize)]
6667pub struct ExpectedCorrelationConfig {
6668    /// First field name.
6669    pub field1: String,
6670    /// Second field name.
6671    pub field2: String,
6672    /// Expected correlation coefficient.
6673    pub expected_r: f64,
6674    /// Acceptable tolerance.
6675    #[serde(default = "default_correlation_tolerance")]
6676    pub tolerance: f64,
6677}
6678
6679fn default_correlation_tolerance() -> f64 {
6680    0.10
6681}
6682
6683/// Conditional distribution configuration.
6684#[derive(Debug, Clone, Serialize, Deserialize)]
6685pub struct ConditionalDistributionSchemaConfig {
6686    /// Output field name to generate.
6687    pub output_field: String,
6688
6689    /// Input field name that conditions the distribution.
6690    pub input_field: String,
6691
6692    /// Breakpoints defining distribution changes.
6693    #[serde(default)]
6694    pub breakpoints: Vec<ConditionalBreakpointConfig>,
6695
6696    /// Default distribution when below all breakpoints.
6697    #[serde(default)]
6698    pub default_distribution: ConditionalDistributionParamsConfig,
6699
6700    /// Minimum output value constraint.
6701    #[serde(default)]
6702    pub min_value: Option<f64>,
6703
6704    /// Maximum output value constraint.
6705    #[serde(default)]
6706    pub max_value: Option<f64>,
6707
6708    /// Decimal places for output rounding.
6709    #[serde(default = "default_decimal_places")]
6710    pub decimal_places: u8,
6711}
6712
6713/// Breakpoint for conditional distribution.
6714#[derive(Debug, Clone, Serialize, Deserialize)]
6715pub struct ConditionalBreakpointConfig {
6716    /// Input value threshold.
6717    pub threshold: f64,
6718
6719    /// Distribution to use when input >= threshold.
6720    pub distribution: ConditionalDistributionParamsConfig,
6721}
6722
6723impl ConditionalDistributionSchemaConfig {
6724    /// Convert this schema config into a core
6725    /// [`ConditionalDistributionConfig`] suitable for
6726    /// [`ConditionalSampler::new`]. v3.5.3+.
6727    pub fn to_core_config(&self) -> datasynth_core::distributions::ConditionalDistributionConfig {
6728        use datasynth_core::distributions::{
6729            Breakpoint, ConditionalDistributionConfig, ConditionalDistributionParams,
6730        };
6731
6732        let default_distribution = convert_conditional_params(&self.default_distribution);
6733        let breakpoints: Vec<Breakpoint> = self
6734            .breakpoints
6735            .iter()
6736            .map(|bp| Breakpoint {
6737                threshold: bp.threshold,
6738                distribution: convert_conditional_params(&bp.distribution),
6739            })
6740            .collect();
6741
6742        // Use a sentinel default_distribution when the schema default is
6743        // its factory default (Fixed { value: 0.0 })  and we have
6744        // breakpoints — we don't want to clobber data for values below
6745        // the first breakpoint.
6746        let final_default = if breakpoints.is_empty() {
6747            default_distribution
6748        } else {
6749            match default_distribution {
6750                ConditionalDistributionParams::Fixed { value: 0.0 } => {
6751                    // Reuse the first breakpoint's distribution as the
6752                    // default to avoid surprising zeros.
6753                    breakpoints[0].distribution.clone()
6754                }
6755                other => other,
6756            }
6757        };
6758
6759        ConditionalDistributionConfig {
6760            output_field: self.output_field.clone(),
6761            input_field: self.input_field.clone(),
6762            breakpoints,
6763            default_distribution: final_default,
6764            min_value: self.min_value,
6765            max_value: self.max_value,
6766            decimal_places: self.decimal_places,
6767        }
6768    }
6769}
6770
6771fn convert_conditional_params(
6772    p: &ConditionalDistributionParamsConfig,
6773) -> datasynth_core::distributions::ConditionalDistributionParams {
6774    use datasynth_core::distributions::ConditionalDistributionParams as Core;
6775    match p {
6776        ConditionalDistributionParamsConfig::Fixed { value } => Core::Fixed { value: *value },
6777        ConditionalDistributionParamsConfig::Normal { mu, sigma } => Core::Normal {
6778            mu: *mu,
6779            sigma: *sigma,
6780        },
6781        ConditionalDistributionParamsConfig::LogNormal { mu, sigma } => Core::LogNormal {
6782            mu: *mu,
6783            sigma: *sigma,
6784        },
6785        ConditionalDistributionParamsConfig::Uniform { min, max } => Core::Uniform {
6786            min: *min,
6787            max: *max,
6788        },
6789        ConditionalDistributionParamsConfig::Beta {
6790            alpha,
6791            beta,
6792            min,
6793            max,
6794        } => Core::Beta {
6795            alpha: *alpha,
6796            beta: *beta,
6797            min: *min,
6798            max: *max,
6799        },
6800        ConditionalDistributionParamsConfig::Discrete { values, weights } => Core::Discrete {
6801            values: values.clone(),
6802            weights: weights.clone(),
6803        },
6804    }
6805}
6806
6807/// Distribution parameters for conditional distributions.
6808#[derive(Debug, Clone, Serialize, Deserialize)]
6809#[serde(tag = "type", rename_all = "snake_case")]
6810pub enum ConditionalDistributionParamsConfig {
6811    /// Fixed value.
6812    Fixed {
6813        /// The fixed value
6814        value: f64,
6815    },
6816    /// Normal distribution.
6817    Normal {
6818        /// Mean
6819        mu: f64,
6820        /// Standard deviation
6821        sigma: f64,
6822    },
6823    /// Log-normal distribution.
6824    LogNormal {
6825        /// Location parameter
6826        mu: f64,
6827        /// Scale parameter
6828        sigma: f64,
6829    },
6830    /// Uniform distribution.
6831    Uniform {
6832        /// Minimum
6833        min: f64,
6834        /// Maximum
6835        max: f64,
6836    },
6837    /// Beta distribution (scaled).
6838    Beta {
6839        /// Alpha parameter
6840        alpha: f64,
6841        /// Beta parameter
6842        beta: f64,
6843        /// Minimum output value
6844        min: f64,
6845        /// Maximum output value
6846        max: f64,
6847    },
6848    /// Discrete values with weights.
6849    Discrete {
6850        /// Possible values
6851        values: Vec<f64>,
6852        /// Weights (should sum to 1.0)
6853        weights: Vec<f64>,
6854    },
6855}
6856
6857impl Default for ConditionalDistributionParamsConfig {
6858    fn default() -> Self {
6859        Self::Normal {
6860            mu: 0.0,
6861            sigma: 1.0,
6862        }
6863    }
6864}
6865
6866/// Regime change configuration.
6867#[derive(Debug, Clone, Serialize, Deserialize, Default)]
6868pub struct RegimeChangeSchemaConfig {
6869    /// Enable regime change modeling.
6870    #[serde(default)]
6871    pub enabled: bool,
6872
6873    /// List of regime changes.
6874    #[serde(default)]
6875    pub changes: Vec<RegimeChangeEventConfig>,
6876
6877    /// Economic cycle configuration.
6878    #[serde(default)]
6879    pub economic_cycle: Option<EconomicCycleSchemaConfig>,
6880
6881    /// Parameter drift configurations.
6882    #[serde(default)]
6883    pub parameter_drifts: Vec<ParameterDriftSchemaConfig>,
6884}
6885
6886/// A single regime change event.
6887#[derive(Debug, Clone, Serialize, Deserialize)]
6888pub struct RegimeChangeEventConfig {
6889    /// Date when the change occurs (ISO 8601 format).
6890    pub date: String,
6891
6892    /// Type of regime change.
6893    pub change_type: RegimeChangeTypeConfig,
6894
6895    /// Description of the change.
6896    #[serde(default)]
6897    pub description: Option<String>,
6898
6899    /// Effects of this regime change.
6900    #[serde(default)]
6901    pub effects: Vec<RegimeEffectConfig>,
6902}
6903
6904/// Type of regime change.
6905#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6906#[serde(rename_all = "snake_case")]
6907pub enum RegimeChangeTypeConfig {
6908    /// Acquisition - sudden volume and amount increase
6909    Acquisition,
6910    /// Divestiture - sudden volume and amount decrease
6911    Divestiture,
6912    /// Price increase - amounts increase
6913    PriceIncrease,
6914    /// Price decrease - amounts decrease
6915    PriceDecrease,
6916    /// New product launch - volume ramp-up
6917    ProductLaunch,
6918    /// Product discontinuation - volume ramp-down
6919    ProductDiscontinuation,
6920    /// Policy change - affects patterns
6921    PolicyChange,
6922    /// Competitor entry - market disruption
6923    CompetitorEntry,
6924    /// Custom effect
6925    Custom,
6926}
6927
6928/// Effect of a regime change on a specific field.
6929#[derive(Debug, Clone, Serialize, Deserialize)]
6930pub struct RegimeEffectConfig {
6931    /// Field being affected.
6932    pub field: String,
6933
6934    /// Multiplier to apply (1.0 = no change, 1.5 = 50% increase).
6935    pub multiplier: f64,
6936}
6937
6938/// Economic cycle configuration.
6939#[derive(Debug, Clone, Serialize, Deserialize)]
6940pub struct EconomicCycleSchemaConfig {
6941    /// Enable economic cycle modeling.
6942    #[serde(default)]
6943    pub enabled: bool,
6944
6945    /// Cycle period in months (e.g., 48 for 4-year business cycle).
6946    #[serde(default = "default_cycle_period")]
6947    pub period_months: u32,
6948
6949    /// Amplitude of cycle effect (0.0-1.0).
6950    #[serde(default = "default_cycle_amplitude")]
6951    pub amplitude: f64,
6952
6953    /// Phase offset in months.
6954    #[serde(default)]
6955    pub phase_offset: u32,
6956
6957    /// Recession periods (start_month, duration_months).
6958    #[serde(default)]
6959    pub recessions: Vec<RecessionPeriodConfig>,
6960}
6961
6962fn default_cycle_period() -> u32 {
6963    48
6964}
6965
6966fn default_cycle_amplitude() -> f64 {
6967    0.15
6968}
6969
6970impl Default for EconomicCycleSchemaConfig {
6971    fn default() -> Self {
6972        Self {
6973            enabled: false,
6974            period_months: 48,
6975            amplitude: 0.15,
6976            phase_offset: 0,
6977            recessions: Vec::new(),
6978        }
6979    }
6980}
6981
6982/// Recession period configuration.
6983#[derive(Debug, Clone, Serialize, Deserialize)]
6984pub struct RecessionPeriodConfig {
6985    /// Start month (0-indexed from generation start).
6986    pub start_month: u32,
6987
6988    /// Duration in months.
6989    pub duration_months: u32,
6990
6991    /// Severity (0.0-1.0, affects volume reduction).
6992    #[serde(default = "default_recession_severity")]
6993    pub severity: f64,
6994}
6995
6996impl RegimeChangeSchemaConfig {
6997    /// Populate the regime-change, economic-cycle, and parameter-drift
6998    /// slots on a `DriftConfig` from this schema config. v3.5.2+.
6999    ///
7000    /// `generation_start` must match `config.global.start_date` so that
7001    /// absolute regime-change dates can be mapped to 0-indexed periods.
7002    /// Unparseable / out-of-range dates are silently skipped to keep
7003    /// runtime robust against user typos.
7004    pub fn apply_to(
7005        &self,
7006        drift: &mut datasynth_core::distributions::DriftConfig,
7007        generation_start: chrono::NaiveDate,
7008    ) {
7009        if !self.enabled {
7010            return;
7011        }
7012
7013        // Enable drift if any regime-change feature wants it.
7014        drift.enabled = true;
7015
7016        // Regime-change events (absolute dates → period offsets).
7017        for event in &self.changes {
7018            let period = match chrono::NaiveDate::parse_from_str(&event.date, "%Y-%m-%d") {
7019                Ok(d) => {
7020                    let days = (d - generation_start).num_days();
7021                    if days < 0 {
7022                        continue;
7023                    }
7024                    // Approximate month by dividing by 30.4 so we don't
7025                    // need chrono::Months arithmetic.
7026                    (days as f64 / 30.4).round() as u32
7027                }
7028                Err(_) => continue,
7029            };
7030            let change_type = convert_regime_change_type(event.change_type);
7031            let core_effects = event
7032                .effects
7033                .iter()
7034                .map(|e| datasynth_core::distributions::RegimeEffect {
7035                    field: e.field.clone(),
7036                    multiplier: e.multiplier,
7037                })
7038                .collect();
7039            drift
7040                .regime_changes
7041                .push(datasynth_core::distributions::RegimeChange {
7042                    period,
7043                    change_type,
7044                    description: event.description.clone(),
7045                    effects: core_effects,
7046                    transition_periods: 0,
7047                });
7048        }
7049
7050        // Economic cycle.
7051        if let Some(ec) = &self.economic_cycle {
7052            if ec.enabled {
7053                let recession_periods: Vec<u32> = ec
7054                    .recessions
7055                    .iter()
7056                    .flat_map(|r| r.start_month..r.start_month + r.duration_months)
7057                    .collect();
7058                // Use the most-severe recession as the severity driver;
7059                // fall back to default when none declared.
7060                let severity = ec
7061                    .recessions
7062                    .iter()
7063                    .map(|r| 1.0 - r.severity)
7064                    .fold(0.75f64, f64::min);
7065                drift.economic_cycle = datasynth_core::distributions::EconomicCycleConfig {
7066                    enabled: true,
7067                    cycle_length: ec.period_months,
7068                    amplitude: ec.amplitude,
7069                    phase_offset: ec.phase_offset,
7070                    recession_periods,
7071                    recession_severity: severity,
7072                };
7073                drift.drift_type = datasynth_core::distributions::DriftType::Mixed;
7074            }
7075        }
7076
7077        // Parameter drifts.
7078        for pd in &self.parameter_drifts {
7079            let drift_type = match pd.drift_type {
7080                ParameterDriftTypeConfig::Linear => {
7081                    datasynth_core::distributions::ParameterDriftType::Linear
7082                }
7083                ParameterDriftTypeConfig::Exponential => {
7084                    datasynth_core::distributions::ParameterDriftType::Exponential
7085                }
7086                ParameterDriftTypeConfig::Logistic => {
7087                    datasynth_core::distributions::ParameterDriftType::Logistic
7088                }
7089                ParameterDriftTypeConfig::Step => {
7090                    datasynth_core::distributions::ParameterDriftType::Step
7091                }
7092            };
7093            drift
7094                .parameter_drifts
7095                .push(datasynth_core::distributions::ParameterDrift {
7096                    parameter: pd.parameter.clone(),
7097                    drift_type,
7098                    initial_value: pd.start_value,
7099                    target_or_rate: pd.end_value,
7100                    start_period: pd.start_period,
7101                    end_period: pd.end_period,
7102                    steepness: 1.0,
7103                });
7104        }
7105    }
7106}
7107
7108fn convert_regime_change_type(
7109    t: RegimeChangeTypeConfig,
7110) -> datasynth_core::distributions::RegimeChangeType {
7111    use datasynth_core::distributions::RegimeChangeType as Core;
7112    match t {
7113        RegimeChangeTypeConfig::Acquisition => Core::Acquisition,
7114        RegimeChangeTypeConfig::Divestiture => Core::Divestiture,
7115        RegimeChangeTypeConfig::PriceIncrease => Core::PriceIncrease,
7116        RegimeChangeTypeConfig::PriceDecrease => Core::PriceDecrease,
7117        RegimeChangeTypeConfig::ProductLaunch => Core::ProductLaunch,
7118        RegimeChangeTypeConfig::ProductDiscontinuation => Core::ProductDiscontinuation,
7119        RegimeChangeTypeConfig::PolicyChange => Core::PolicyChange,
7120        RegimeChangeTypeConfig::CompetitorEntry => Core::CompetitorEntry,
7121        RegimeChangeTypeConfig::Custom => Core::Custom,
7122    }
7123}
7124
7125fn default_recession_severity() -> f64 {
7126    0.20
7127}
7128
7129/// Parameter drift configuration.
7130#[derive(Debug, Clone, Serialize, Deserialize)]
7131pub struct ParameterDriftSchemaConfig {
7132    /// Parameter being drifted.
7133    pub parameter: String,
7134
7135    /// Drift type.
7136    pub drift_type: ParameterDriftTypeConfig,
7137
7138    /// Start value.
7139    pub start_value: f64,
7140
7141    /// End value.
7142    pub end_value: f64,
7143
7144    /// Start period (month, 0-indexed).
7145    #[serde(default)]
7146    pub start_period: u32,
7147
7148    /// End period (month, optional - defaults to end of generation).
7149    #[serde(default)]
7150    pub end_period: Option<u32>,
7151}
7152
7153/// Parameter drift type.
7154#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
7155#[serde(rename_all = "snake_case")]
7156pub enum ParameterDriftTypeConfig {
7157    /// Linear interpolation
7158    #[default]
7159    Linear,
7160    /// Exponential growth/decay
7161    Exponential,
7162    /// S-curve (logistic)
7163    Logistic,
7164    /// Step function
7165    Step,
7166}
7167
7168/// Statistical validation configuration.
7169#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7170pub struct StatisticalValidationSchemaConfig {
7171    /// Enable statistical validation.
7172    #[serde(default)]
7173    pub enabled: bool,
7174
7175    /// Statistical tests to run.
7176    #[serde(default)]
7177    pub tests: Vec<StatisticalTestConfig>,
7178
7179    /// Validation reporting configuration.
7180    #[serde(default)]
7181    pub reporting: ValidationReportingConfig,
7182}
7183
7184/// Statistical test configuration.
7185#[derive(Debug, Clone, Serialize, Deserialize)]
7186#[serde(tag = "type", rename_all = "snake_case")]
7187pub enum StatisticalTestConfig {
7188    /// Benford's Law first digit test.
7189    BenfordFirstDigit {
7190        /// Threshold MAD for failure.
7191        #[serde(default = "default_benford_threshold")]
7192        threshold_mad: f64,
7193        /// Warning MAD threshold.
7194        #[serde(default = "default_benford_warning")]
7195        warning_mad: f64,
7196    },
7197    /// Distribution fit test.
7198    DistributionFit {
7199        /// Target distribution to test.
7200        target: TargetDistributionConfig,
7201        /// K-S test significance level.
7202        #[serde(default = "default_ks_significance")]
7203        ks_significance: f64,
7204        /// Test method (ks, anderson_darling, chi_squared).
7205        #[serde(default)]
7206        method: DistributionFitMethod,
7207    },
7208    /// Correlation check.
7209    CorrelationCheck {
7210        /// Expected correlations to validate.
7211        expected_correlations: Vec<ExpectedCorrelationConfig>,
7212    },
7213    /// Chi-squared test.
7214    ChiSquared {
7215        /// Number of bins.
7216        #[serde(default = "default_chi_squared_bins")]
7217        bins: usize,
7218        /// Significance level.
7219        #[serde(default = "default_chi_squared_significance")]
7220        significance: f64,
7221    },
7222    /// Anderson-Darling test.
7223    AndersonDarling {
7224        /// Target distribution.
7225        target: TargetDistributionConfig,
7226        /// Significance level.
7227        #[serde(default = "default_ad_significance")]
7228        significance: f64,
7229    },
7230}
7231
7232fn default_benford_threshold() -> f64 {
7233    0.015
7234}
7235
7236fn default_benford_warning() -> f64 {
7237    0.010
7238}
7239
7240fn default_ks_significance() -> f64 {
7241    0.05
7242}
7243
7244fn default_chi_squared_bins() -> usize {
7245    10
7246}
7247
7248fn default_chi_squared_significance() -> f64 {
7249    0.05
7250}
7251
7252fn default_ad_significance() -> f64 {
7253    0.05
7254}
7255
7256/// Target distribution for fit tests.
7257#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
7258#[serde(rename_all = "snake_case")]
7259pub enum TargetDistributionConfig {
7260    /// Normal distribution
7261    Normal,
7262    /// Log-normal distribution
7263    #[default]
7264    LogNormal,
7265    /// Exponential distribution
7266    Exponential,
7267    /// Uniform distribution
7268    Uniform,
7269}
7270
7271/// Distribution fit test method.
7272#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
7273#[serde(rename_all = "snake_case")]
7274pub enum DistributionFitMethod {
7275    /// Kolmogorov-Smirnov test
7276    #[default]
7277    KolmogorovSmirnov,
7278    /// Anderson-Darling test
7279    AndersonDarling,
7280    /// Chi-squared test
7281    ChiSquared,
7282}
7283
7284/// Validation reporting configuration.
7285#[derive(Debug, Clone, Serialize, Deserialize)]
7286pub struct ValidationReportingConfig {
7287    /// Output validation report to file.
7288    #[serde(default)]
7289    pub output_report: bool,
7290
7291    /// Report format.
7292    #[serde(default)]
7293    pub format: ValidationReportFormat,
7294
7295    /// Fail generation if validation fails.
7296    #[serde(default)]
7297    pub fail_on_error: bool,
7298
7299    /// Include detailed statistics in report.
7300    #[serde(default = "default_true")]
7301    pub include_details: bool,
7302}
7303
7304impl Default for ValidationReportingConfig {
7305    fn default() -> Self {
7306        Self {
7307            output_report: false,
7308            format: ValidationReportFormat::Json,
7309            fail_on_error: false,
7310            include_details: true,
7311        }
7312    }
7313}
7314
7315/// Validation report format.
7316#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
7317#[serde(rename_all = "snake_case")]
7318pub enum ValidationReportFormat {
7319    /// JSON format
7320    #[default]
7321    Json,
7322    /// YAML format
7323    Yaml,
7324    /// HTML report
7325    Html,
7326}
7327
7328// =============================================================================
7329// Temporal Patterns Configuration
7330// =============================================================================
7331
7332/// Temporal patterns configuration for business days, period-end dynamics, and processing lags.
7333///
7334/// This section enables sophisticated temporal modeling including:
7335/// - Business day calculations and settlement dates
7336/// - Regional holiday calendars
7337/// - Period-end decay curves (non-flat volume spikes)
7338/// - Processing lag modeling (event-to-posting delays)
7339#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7340pub struct TemporalPatternsConfig {
7341    /// Enable temporal patterns features.
7342    #[serde(default)]
7343    pub enabled: bool,
7344
7345    /// Business day calculation configuration.
7346    #[serde(default)]
7347    pub business_days: BusinessDaySchemaConfig,
7348
7349    /// Regional calendar configuration.
7350    #[serde(default)]
7351    pub calendars: CalendarSchemaConfig,
7352
7353    /// Period-end dynamics configuration.
7354    #[serde(default)]
7355    pub period_end: PeriodEndSchemaConfig,
7356
7357    /// Processing lag configuration.
7358    #[serde(default)]
7359    pub processing_lags: ProcessingLagSchemaConfig,
7360
7361    /// Fiscal calendar configuration (custom year start, 4-4-5, 13-period).
7362    #[serde(default)]
7363    pub fiscal_calendar: FiscalCalendarSchemaConfig,
7364
7365    /// Intra-day patterns configuration (morning spike, lunch dip, EOD rush).
7366    #[serde(default)]
7367    pub intraday: IntraDaySchemaConfig,
7368
7369    /// Timezone handling configuration.
7370    #[serde(default)]
7371    pub timezones: TimezoneSchemaConfig,
7372}
7373
7374/// Business day calculation configuration.
7375#[derive(Debug, Clone, Serialize, Deserialize)]
7376pub struct BusinessDaySchemaConfig {
7377    /// Enable business day calculations.
7378    #[serde(default = "default_true")]
7379    pub enabled: bool,
7380
7381    /// Half-day policy: "full_day", "half_day", "non_business_day".
7382    #[serde(default = "default_half_day_policy")]
7383    pub half_day_policy: String,
7384
7385    /// Settlement rules configuration.
7386    #[serde(default)]
7387    pub settlement_rules: SettlementRulesSchemaConfig,
7388
7389    /// Month-end convention: "modified_following", "preceding", "following", "end_of_month".
7390    #[serde(default = "default_month_end_convention")]
7391    pub month_end_convention: String,
7392
7393    /// Weekend days (e.g., ["saturday", "sunday"] or ["friday", "saturday"] for Middle East).
7394    #[serde(default)]
7395    pub weekend_days: Option<Vec<String>>,
7396}
7397
7398fn default_half_day_policy() -> String {
7399    "half_day".to_string()
7400}
7401
7402fn default_month_end_convention() -> String {
7403    "modified_following".to_string()
7404}
7405
7406impl Default for BusinessDaySchemaConfig {
7407    fn default() -> Self {
7408        Self {
7409            enabled: true,
7410            half_day_policy: "half_day".to_string(),
7411            settlement_rules: SettlementRulesSchemaConfig::default(),
7412            month_end_convention: "modified_following".to_string(),
7413            weekend_days: None,
7414        }
7415    }
7416}
7417
7418/// Settlement rules configuration.
7419#[derive(Debug, Clone, Serialize, Deserialize)]
7420pub struct SettlementRulesSchemaConfig {
7421    /// Equity settlement days (T+N).
7422    #[serde(default = "default_settlement_2")]
7423    pub equity_days: i32,
7424
7425    /// Government bonds settlement days.
7426    #[serde(default = "default_settlement_1")]
7427    pub government_bonds_days: i32,
7428
7429    /// FX spot settlement days.
7430    #[serde(default = "default_settlement_2")]
7431    pub fx_spot_days: i32,
7432
7433    /// Corporate bonds settlement days.
7434    #[serde(default = "default_settlement_2")]
7435    pub corporate_bonds_days: i32,
7436
7437    /// Wire transfer cutoff time (HH:MM format).
7438    #[serde(default = "default_wire_cutoff")]
7439    pub wire_cutoff_time: String,
7440
7441    /// International wire settlement days.
7442    #[serde(default = "default_settlement_1")]
7443    pub wire_international_days: i32,
7444
7445    /// ACH settlement days.
7446    #[serde(default = "default_settlement_1")]
7447    pub ach_days: i32,
7448}
7449
7450fn default_settlement_1() -> i32 {
7451    1
7452}
7453
7454fn default_settlement_2() -> i32 {
7455    2
7456}
7457
7458fn default_wire_cutoff() -> String {
7459    "14:00".to_string()
7460}
7461
7462impl Default for SettlementRulesSchemaConfig {
7463    fn default() -> Self {
7464        Self {
7465            equity_days: 2,
7466            government_bonds_days: 1,
7467            fx_spot_days: 2,
7468            corporate_bonds_days: 2,
7469            wire_cutoff_time: "14:00".to_string(),
7470            wire_international_days: 1,
7471            ach_days: 1,
7472        }
7473    }
7474}
7475
7476/// Regional calendar configuration.
7477#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7478pub struct CalendarSchemaConfig {
7479    /// List of regions to include (e.g., ["US", "DE", "BR", "SG", "KR"]).
7480    #[serde(default)]
7481    pub regions: Vec<String>,
7482
7483    /// Custom holidays (in addition to regional calendars).
7484    #[serde(default)]
7485    pub custom_holidays: Vec<CustomHolidaySchemaConfig>,
7486}
7487
7488/// Custom holiday configuration.
7489#[derive(Debug, Clone, Serialize, Deserialize)]
7490pub struct CustomHolidaySchemaConfig {
7491    /// Holiday name.
7492    pub name: String,
7493    /// Month (1-12).
7494    pub month: u8,
7495    /// Day of month.
7496    pub day: u8,
7497    /// Activity multiplier (0.0-1.0, default 0.05).
7498    #[serde(default = "default_holiday_multiplier")]
7499    pub activity_multiplier: f64,
7500}
7501
7502fn default_holiday_multiplier() -> f64 {
7503    0.05
7504}
7505
7506/// Period-end dynamics configuration.
7507#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7508pub struct PeriodEndSchemaConfig {
7509    /// Model type: "flat", "exponential", "extended_crunch", "daily_profile".
7510    #[serde(default)]
7511    pub model: Option<String>,
7512
7513    /// Month-end configuration.
7514    #[serde(default)]
7515    pub month_end: Option<PeriodEndModelSchemaConfig>,
7516
7517    /// Quarter-end configuration.
7518    #[serde(default)]
7519    pub quarter_end: Option<PeriodEndModelSchemaConfig>,
7520
7521    /// Year-end configuration.
7522    #[serde(default)]
7523    pub year_end: Option<PeriodEndModelSchemaConfig>,
7524}
7525
7526/// Period-end model configuration.
7527#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7528pub struct PeriodEndModelSchemaConfig {
7529    /// Inherit configuration from another period (e.g., "month_end").
7530    #[serde(default)]
7531    pub inherit_from: Option<String>,
7532
7533    /// Additional multiplier on top of inherited/base model.
7534    #[serde(default)]
7535    pub additional_multiplier: Option<f64>,
7536
7537    /// Days before period end to start acceleration (negative, e.g., -10).
7538    #[serde(default)]
7539    pub start_day: Option<i32>,
7540
7541    /// Base multiplier at start of acceleration.
7542    #[serde(default)]
7543    pub base_multiplier: Option<f64>,
7544
7545    /// Peak multiplier on last day.
7546    #[serde(default)]
7547    pub peak_multiplier: Option<f64>,
7548
7549    /// Decay rate for exponential model (0.1-0.5 typical).
7550    #[serde(default)]
7551    pub decay_rate: Option<f64>,
7552
7553    /// Sustained high days for crunch model.
7554    #[serde(default)]
7555    pub sustained_high_days: Option<i32>,
7556}
7557
7558/// Processing lag configuration.
7559#[derive(Debug, Clone, Serialize, Deserialize)]
7560pub struct ProcessingLagSchemaConfig {
7561    /// Enable processing lag calculations.
7562    #[serde(default = "default_true")]
7563    pub enabled: bool,
7564
7565    /// Sales order lag configuration (log-normal mu, sigma).
7566    #[serde(default)]
7567    pub sales_order_lag: Option<LagDistributionSchemaConfig>,
7568
7569    /// Purchase order lag configuration.
7570    #[serde(default)]
7571    pub purchase_order_lag: Option<LagDistributionSchemaConfig>,
7572
7573    /// Goods receipt lag configuration.
7574    #[serde(default)]
7575    pub goods_receipt_lag: Option<LagDistributionSchemaConfig>,
7576
7577    /// Invoice receipt lag configuration.
7578    #[serde(default)]
7579    pub invoice_receipt_lag: Option<LagDistributionSchemaConfig>,
7580
7581    /// Invoice issue lag configuration.
7582    #[serde(default)]
7583    pub invoice_issue_lag: Option<LagDistributionSchemaConfig>,
7584
7585    /// Payment lag configuration.
7586    #[serde(default)]
7587    pub payment_lag: Option<LagDistributionSchemaConfig>,
7588
7589    /// Journal entry lag configuration.
7590    #[serde(default)]
7591    pub journal_entry_lag: Option<LagDistributionSchemaConfig>,
7592
7593    /// Cross-day posting configuration.
7594    #[serde(default)]
7595    pub cross_day_posting: Option<CrossDayPostingSchemaConfig>,
7596}
7597
7598impl Default for ProcessingLagSchemaConfig {
7599    fn default() -> Self {
7600        Self {
7601            enabled: true,
7602            sales_order_lag: None,
7603            purchase_order_lag: None,
7604            goods_receipt_lag: None,
7605            invoice_receipt_lag: None,
7606            invoice_issue_lag: None,
7607            payment_lag: None,
7608            journal_entry_lag: None,
7609            cross_day_posting: None,
7610        }
7611    }
7612}
7613
7614/// Lag distribution configuration (log-normal parameters).
7615#[derive(Debug, Clone, Serialize, Deserialize)]
7616pub struct LagDistributionSchemaConfig {
7617    /// Log-scale mean (mu for log-normal).
7618    pub mu: f64,
7619    /// Log-scale standard deviation (sigma for log-normal).
7620    pub sigma: f64,
7621    /// Minimum lag in hours.
7622    #[serde(default)]
7623    pub min_hours: Option<f64>,
7624    /// Maximum lag in hours.
7625    #[serde(default)]
7626    pub max_hours: Option<f64>,
7627}
7628
7629/// Cross-day posting configuration.
7630#[derive(Debug, Clone, Serialize, Deserialize)]
7631pub struct CrossDayPostingSchemaConfig {
7632    /// Enable cross-day posting logic.
7633    #[serde(default = "default_true")]
7634    pub enabled: bool,
7635
7636    /// Probability of next-day posting by hour (map of hour -> probability).
7637    /// E.g., { 17: 0.7, 19: 0.9, 21: 0.99 }
7638    #[serde(default)]
7639    pub probability_by_hour: std::collections::HashMap<u8, f64>,
7640}
7641
7642impl Default for CrossDayPostingSchemaConfig {
7643    fn default() -> Self {
7644        let mut probability_by_hour = std::collections::HashMap::new();
7645        probability_by_hour.insert(17, 0.3);
7646        probability_by_hour.insert(18, 0.6);
7647        probability_by_hour.insert(19, 0.8);
7648        probability_by_hour.insert(20, 0.9);
7649        probability_by_hour.insert(21, 0.95);
7650        probability_by_hour.insert(22, 0.99);
7651
7652        Self {
7653            enabled: true,
7654            probability_by_hour,
7655        }
7656    }
7657}
7658
7659// =============================================================================
7660// Fiscal Calendar Configuration (P2)
7661// =============================================================================
7662
7663/// Fiscal calendar configuration.
7664///
7665/// Supports calendar year, custom year start, 4-4-5 retail calendar,
7666/// and 13-period calendars.
7667#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7668pub struct FiscalCalendarSchemaConfig {
7669    /// Enable non-standard fiscal calendar.
7670    #[serde(default)]
7671    pub enabled: bool,
7672
7673    /// Fiscal calendar type: "calendar_year", "custom", "four_four_five", "thirteen_period".
7674    #[serde(default = "default_fiscal_calendar_type")]
7675    pub calendar_type: String,
7676
7677    /// Month the fiscal year starts (1-12). Used for custom year start.
7678    #[serde(default)]
7679    pub year_start_month: Option<u8>,
7680
7681    /// Day the fiscal year starts (1-31). Used for custom year start.
7682    #[serde(default)]
7683    pub year_start_day: Option<u8>,
7684
7685    /// 4-4-5 calendar configuration (if calendar_type is "four_four_five").
7686    #[serde(default)]
7687    pub four_four_five: Option<FourFourFiveSchemaConfig>,
7688}
7689
7690fn default_fiscal_calendar_type() -> String {
7691    "calendar_year".to_string()
7692}
7693
7694/// 4-4-5 retail calendar configuration.
7695#[derive(Debug, Clone, Serialize, Deserialize)]
7696pub struct FourFourFiveSchemaConfig {
7697    /// Week pattern: "four_four_five", "four_five_four", "five_four_four".
7698    #[serde(default = "default_week_pattern")]
7699    pub pattern: String,
7700
7701    /// Anchor type: "first_sunday", "last_saturday", "nearest_saturday".
7702    #[serde(default = "default_anchor_type")]
7703    pub anchor_type: String,
7704
7705    /// Anchor month (1-12).
7706    #[serde(default = "default_anchor_month")]
7707    pub anchor_month: u8,
7708
7709    /// Where to place leap week: "q4_period3" or "q1_period1".
7710    #[serde(default = "default_leap_week_placement")]
7711    pub leap_week_placement: String,
7712}
7713
7714fn default_week_pattern() -> String {
7715    "four_four_five".to_string()
7716}
7717
7718fn default_anchor_type() -> String {
7719    "last_saturday".to_string()
7720}
7721
7722fn default_anchor_month() -> u8 {
7723    1 // January
7724}
7725
7726fn default_leap_week_placement() -> String {
7727    "q4_period3".to_string()
7728}
7729
7730impl Default for FourFourFiveSchemaConfig {
7731    fn default() -> Self {
7732        Self {
7733            pattern: "four_four_five".to_string(),
7734            anchor_type: "last_saturday".to_string(),
7735            anchor_month: 1,
7736            leap_week_placement: "q4_period3".to_string(),
7737        }
7738    }
7739}
7740
7741// =============================================================================
7742// Intra-Day Patterns Configuration (P2)
7743// =============================================================================
7744
7745/// Intra-day patterns configuration.
7746///
7747/// Defines time-of-day segments with different activity multipliers
7748/// for realistic modeling of morning spikes, lunch dips, and end-of-day rushes.
7749#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7750pub struct IntraDaySchemaConfig {
7751    /// Enable intra-day patterns.
7752    #[serde(default)]
7753    pub enabled: bool,
7754
7755    /// Custom intra-day segments.
7756    #[serde(default)]
7757    pub segments: Vec<IntraDaySegmentSchemaConfig>,
7758}
7759
7760/// Intra-day segment configuration.
7761#[derive(Debug, Clone, Serialize, Deserialize)]
7762pub struct IntraDaySegmentSchemaConfig {
7763    /// Name of the segment (e.g., "morning_spike", "lunch_dip").
7764    pub name: String,
7765
7766    /// Start time (HH:MM format).
7767    pub start: String,
7768
7769    /// End time (HH:MM format).
7770    pub end: String,
7771
7772    /// Activity multiplier (1.0 = normal).
7773    #[serde(default = "default_multiplier")]
7774    pub multiplier: f64,
7775
7776    /// Posting type: "human", "system", "both".
7777    #[serde(default = "default_posting_type")]
7778    pub posting_type: String,
7779}
7780
7781fn default_multiplier() -> f64 {
7782    1.0
7783}
7784
7785fn default_posting_type() -> String {
7786    "both".to_string()
7787}
7788
7789// =============================================================================
7790// Timezone Configuration
7791// =============================================================================
7792
7793/// Timezone handling configuration for multi-region entities.
7794#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7795pub struct TimezoneSchemaConfig {
7796    /// Enable timezone handling.
7797    #[serde(default)]
7798    pub enabled: bool,
7799
7800    /// Default timezone (IANA format, e.g., "America/New_York").
7801    #[serde(default = "default_timezone")]
7802    pub default_timezone: String,
7803
7804    /// Consolidation timezone for group reporting (IANA format).
7805    #[serde(default = "default_consolidation_timezone")]
7806    pub consolidation_timezone: String,
7807
7808    /// Entity-to-timezone mappings.
7809    /// Supports patterns like "EU_*" -> "Europe/London".
7810    #[serde(default)]
7811    pub entity_mappings: Vec<EntityTimezoneMapping>,
7812}
7813
7814fn default_timezone() -> String {
7815    "America/New_York".to_string()
7816}
7817
7818fn default_consolidation_timezone() -> String {
7819    "UTC".to_string()
7820}
7821
7822/// Mapping from entity pattern to timezone.
7823#[derive(Debug, Clone, Serialize, Deserialize)]
7824pub struct EntityTimezoneMapping {
7825    /// Entity code pattern (e.g., "EU_*", "*_APAC", "1000").
7826    pub pattern: String,
7827
7828    /// Timezone (IANA format, e.g., "Europe/London").
7829    pub timezone: String,
7830}
7831
7832// =============================================================================
7833// Vendor Network Configuration
7834// =============================================================================
7835
7836/// Configuration for multi-tier vendor network generation.
7837#[derive(Debug, Clone, Serialize, Deserialize)]
7838pub struct VendorNetworkSchemaConfig {
7839    /// Enable vendor network generation.
7840    #[serde(default)]
7841    pub enabled: bool,
7842
7843    /// Maximum depth of supply chain tiers (1-3).
7844    #[serde(default = "default_vendor_tier_depth")]
7845    pub depth: u8,
7846
7847    /// Tier 1 vendor count configuration.
7848    #[serde(default)]
7849    pub tier1: TierCountSchemaConfig,
7850
7851    /// Tier 2 vendors per Tier 1 parent.
7852    #[serde(default)]
7853    pub tier2_per_parent: TierCountSchemaConfig,
7854
7855    /// Tier 3 vendors per Tier 2 parent.
7856    #[serde(default)]
7857    pub tier3_per_parent: TierCountSchemaConfig,
7858
7859    /// Vendor cluster distribution.
7860    #[serde(default)]
7861    pub clusters: VendorClusterSchemaConfig,
7862
7863    /// Concentration limits.
7864    #[serde(default)]
7865    pub dependencies: DependencySchemaConfig,
7866}
7867
7868fn default_vendor_tier_depth() -> u8 {
7869    3
7870}
7871
7872impl Default for VendorNetworkSchemaConfig {
7873    fn default() -> Self {
7874        Self {
7875            enabled: false,
7876            depth: 3,
7877            tier1: TierCountSchemaConfig { min: 50, max: 100 },
7878            tier2_per_parent: TierCountSchemaConfig { min: 4, max: 10 },
7879            tier3_per_parent: TierCountSchemaConfig { min: 2, max: 5 },
7880            clusters: VendorClusterSchemaConfig::default(),
7881            dependencies: DependencySchemaConfig::default(),
7882        }
7883    }
7884}
7885
7886/// Tier count configuration.
7887#[derive(Debug, Clone, Serialize, Deserialize)]
7888pub struct TierCountSchemaConfig {
7889    /// Minimum count.
7890    #[serde(default = "default_tier_min")]
7891    pub min: usize,
7892
7893    /// Maximum count.
7894    #[serde(default = "default_tier_max")]
7895    pub max: usize,
7896}
7897
7898fn default_tier_min() -> usize {
7899    5
7900}
7901
7902fn default_tier_max() -> usize {
7903    20
7904}
7905
7906impl Default for TierCountSchemaConfig {
7907    fn default() -> Self {
7908        Self {
7909            min: default_tier_min(),
7910            max: default_tier_max(),
7911        }
7912    }
7913}
7914
7915/// Vendor cluster distribution configuration.
7916#[derive(Debug, Clone, Serialize, Deserialize)]
7917pub struct VendorClusterSchemaConfig {
7918    /// Reliable strategic vendors percentage (default: 0.20).
7919    #[serde(default = "default_reliable_strategic")]
7920    pub reliable_strategic: f64,
7921
7922    /// Standard operational vendors percentage (default: 0.50).
7923    #[serde(default = "default_standard_operational")]
7924    pub standard_operational: f64,
7925
7926    /// Transactional vendors percentage (default: 0.25).
7927    #[serde(default = "default_transactional")]
7928    pub transactional: f64,
7929
7930    /// Problematic vendors percentage (default: 0.05).
7931    #[serde(default = "default_problematic")]
7932    pub problematic: f64,
7933}
7934
7935fn default_reliable_strategic() -> f64 {
7936    0.20
7937}
7938
7939fn default_standard_operational() -> f64 {
7940    0.50
7941}
7942
7943fn default_transactional() -> f64 {
7944    0.25
7945}
7946
7947fn default_problematic() -> f64 {
7948    0.05
7949}
7950
7951impl Default for VendorClusterSchemaConfig {
7952    fn default() -> Self {
7953        Self {
7954            reliable_strategic: 0.20,
7955            standard_operational: 0.50,
7956            transactional: 0.25,
7957            problematic: 0.05,
7958        }
7959    }
7960}
7961
7962/// Dependency and concentration limits configuration.
7963#[derive(Debug, Clone, Serialize, Deserialize)]
7964pub struct DependencySchemaConfig {
7965    /// Maximum concentration for a single vendor (default: 0.15).
7966    #[serde(default = "default_max_single_vendor")]
7967    pub max_single_vendor_concentration: f64,
7968
7969    /// Maximum concentration for top 5 vendors (default: 0.45).
7970    #[serde(default = "default_max_top5")]
7971    pub top_5_concentration: f64,
7972
7973    /// Percentage of single-source vendors (default: 0.05).
7974    #[serde(default = "default_single_source_percent")]
7975    pub single_source_percent: f64,
7976}
7977
7978fn default_max_single_vendor() -> f64 {
7979    0.15
7980}
7981
7982fn default_max_top5() -> f64 {
7983    0.45
7984}
7985
7986fn default_single_source_percent() -> f64 {
7987    0.05
7988}
7989
7990impl Default for DependencySchemaConfig {
7991    fn default() -> Self {
7992        Self {
7993            max_single_vendor_concentration: 0.15,
7994            top_5_concentration: 0.45,
7995            single_source_percent: 0.05,
7996        }
7997    }
7998}
7999
8000// =============================================================================
8001// Customer Segmentation Configuration
8002// =============================================================================
8003
8004/// Configuration for customer segmentation generation.
8005#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8006pub struct CustomerSegmentationSchemaConfig {
8007    /// Enable customer segmentation generation.
8008    #[serde(default)]
8009    pub enabled: bool,
8010
8011    /// Value segment distribution.
8012    #[serde(default)]
8013    pub value_segments: ValueSegmentsSchemaConfig,
8014
8015    /// Lifecycle stage configuration.
8016    #[serde(default)]
8017    pub lifecycle: LifecycleSchemaConfig,
8018
8019    /// Network (referrals, hierarchies) configuration.
8020    #[serde(default)]
8021    pub networks: CustomerNetworksSchemaConfig,
8022}
8023
8024/// Customer value segments distribution configuration.
8025#[derive(Debug, Clone, Serialize, Deserialize)]
8026pub struct ValueSegmentsSchemaConfig {
8027    /// Enterprise segment configuration.
8028    #[serde(default)]
8029    pub enterprise: SegmentDetailSchemaConfig,
8030
8031    /// Mid-market segment configuration.
8032    #[serde(default)]
8033    pub mid_market: SegmentDetailSchemaConfig,
8034
8035    /// SMB segment configuration.
8036    #[serde(default)]
8037    pub smb: SegmentDetailSchemaConfig,
8038
8039    /// Consumer segment configuration.
8040    #[serde(default)]
8041    pub consumer: SegmentDetailSchemaConfig,
8042}
8043
8044impl Default for ValueSegmentsSchemaConfig {
8045    fn default() -> Self {
8046        Self {
8047            enterprise: SegmentDetailSchemaConfig {
8048                revenue_share: 0.40,
8049                customer_share: 0.05,
8050                avg_order_value_range: "50000+".to_string(),
8051            },
8052            mid_market: SegmentDetailSchemaConfig {
8053                revenue_share: 0.35,
8054                customer_share: 0.20,
8055                avg_order_value_range: "5000-50000".to_string(),
8056            },
8057            smb: SegmentDetailSchemaConfig {
8058                revenue_share: 0.20,
8059                customer_share: 0.50,
8060                avg_order_value_range: "500-5000".to_string(),
8061            },
8062            consumer: SegmentDetailSchemaConfig {
8063                revenue_share: 0.05,
8064                customer_share: 0.25,
8065                avg_order_value_range: "50-500".to_string(),
8066            },
8067        }
8068    }
8069}
8070
8071/// Individual segment detail configuration.
8072#[derive(Debug, Clone, Serialize, Deserialize)]
8073pub struct SegmentDetailSchemaConfig {
8074    /// Revenue share for this segment.
8075    #[serde(default)]
8076    pub revenue_share: f64,
8077
8078    /// Customer share for this segment.
8079    #[serde(default)]
8080    pub customer_share: f64,
8081
8082    /// Average order value range (e.g., "5000-50000" or "50000+").
8083    #[serde(default)]
8084    pub avg_order_value_range: String,
8085}
8086
8087impl Default for SegmentDetailSchemaConfig {
8088    fn default() -> Self {
8089        Self {
8090            revenue_share: 0.25,
8091            customer_share: 0.25,
8092            avg_order_value_range: "1000-10000".to_string(),
8093        }
8094    }
8095}
8096
8097/// Customer lifecycle stage configuration.
8098#[derive(Debug, Clone, Serialize, Deserialize)]
8099pub struct LifecycleSchemaConfig {
8100    /// Prospect stage rate.
8101    #[serde(default)]
8102    pub prospect_rate: f64,
8103
8104    /// New customer stage rate.
8105    #[serde(default = "default_new_rate")]
8106    pub new_rate: f64,
8107
8108    /// Growth stage rate.
8109    #[serde(default = "default_growth_rate")]
8110    pub growth_rate: f64,
8111
8112    /// Mature stage rate.
8113    #[serde(default = "default_mature_rate")]
8114    pub mature_rate: f64,
8115
8116    /// At-risk stage rate.
8117    #[serde(default = "default_at_risk_rate")]
8118    pub at_risk_rate: f64,
8119
8120    /// Churned stage rate.
8121    #[serde(default = "default_churned_rate")]
8122    pub churned_rate: f64,
8123
8124    /// Won-back stage rate (churned customers reacquired).
8125    #[serde(default)]
8126    pub won_back_rate: f64,
8127}
8128
8129fn default_new_rate() -> f64 {
8130    0.10
8131}
8132
8133fn default_growth_rate() -> f64 {
8134    0.15
8135}
8136
8137fn default_mature_rate() -> f64 {
8138    0.60
8139}
8140
8141fn default_at_risk_rate() -> f64 {
8142    0.10
8143}
8144
8145fn default_churned_rate() -> f64 {
8146    0.05
8147}
8148
8149impl Default for LifecycleSchemaConfig {
8150    fn default() -> Self {
8151        Self {
8152            prospect_rate: 0.0,
8153            new_rate: 0.10,
8154            growth_rate: 0.15,
8155            mature_rate: 0.60,
8156            at_risk_rate: 0.10,
8157            churned_rate: 0.05,
8158            won_back_rate: 0.0,
8159        }
8160    }
8161}
8162
8163/// Customer networks configuration (referrals, hierarchies).
8164#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8165pub struct CustomerNetworksSchemaConfig {
8166    /// Referral network configuration.
8167    #[serde(default)]
8168    pub referrals: ReferralSchemaConfig,
8169
8170    /// Corporate hierarchy configuration.
8171    #[serde(default)]
8172    pub corporate_hierarchies: HierarchySchemaConfig,
8173}
8174
8175/// Referral network configuration.
8176#[derive(Debug, Clone, Serialize, Deserialize)]
8177pub struct ReferralSchemaConfig {
8178    /// Enable referral generation.
8179    #[serde(default = "default_true")]
8180    pub enabled: bool,
8181
8182    /// Rate of customers acquired via referral.
8183    #[serde(default = "default_referral_rate")]
8184    pub referral_rate: f64,
8185}
8186
8187fn default_referral_rate() -> f64 {
8188    0.15
8189}
8190
8191impl Default for ReferralSchemaConfig {
8192    fn default() -> Self {
8193        Self {
8194            enabled: true,
8195            referral_rate: 0.15,
8196        }
8197    }
8198}
8199
8200/// Corporate hierarchy configuration.
8201#[derive(Debug, Clone, Serialize, Deserialize)]
8202pub struct HierarchySchemaConfig {
8203    /// Enable corporate hierarchy generation.
8204    #[serde(default = "default_true")]
8205    pub enabled: bool,
8206
8207    /// Rate of customers in hierarchies.
8208    #[serde(default = "default_hierarchy_rate")]
8209    pub probability: f64,
8210}
8211
8212fn default_hierarchy_rate() -> f64 {
8213    0.30
8214}
8215
8216impl Default for HierarchySchemaConfig {
8217    fn default() -> Self {
8218        Self {
8219            enabled: true,
8220            probability: 0.30,
8221        }
8222    }
8223}
8224
8225// =============================================================================
8226// Relationship Strength Configuration
8227// =============================================================================
8228
8229/// Configuration for relationship strength calculation.
8230#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8231pub struct RelationshipStrengthSchemaConfig {
8232    /// Enable relationship strength calculation.
8233    #[serde(default)]
8234    pub enabled: bool,
8235
8236    /// Calculation weights.
8237    #[serde(default)]
8238    pub calculation: StrengthCalculationSchemaConfig,
8239
8240    /// Strength thresholds for classification.
8241    #[serde(default)]
8242    pub thresholds: StrengthThresholdsSchemaConfig,
8243}
8244
8245/// Strength calculation weights configuration.
8246#[derive(Debug, Clone, Serialize, Deserialize)]
8247pub struct StrengthCalculationSchemaConfig {
8248    /// Weight for transaction volume (default: 0.30).
8249    #[serde(default = "default_volume_weight")]
8250    pub transaction_volume_weight: f64,
8251
8252    /// Weight for transaction count (default: 0.25).
8253    #[serde(default = "default_count_weight")]
8254    pub transaction_count_weight: f64,
8255
8256    /// Weight for relationship duration (default: 0.20).
8257    #[serde(default = "default_duration_weight")]
8258    pub relationship_duration_weight: f64,
8259
8260    /// Weight for recency (default: 0.15).
8261    #[serde(default = "default_recency_weight")]
8262    pub recency_weight: f64,
8263
8264    /// Weight for mutual connections (default: 0.10).
8265    #[serde(default = "default_mutual_weight")]
8266    pub mutual_connections_weight: f64,
8267
8268    /// Recency half-life in days (default: 90).
8269    #[serde(default = "default_recency_half_life")]
8270    pub recency_half_life_days: u32,
8271}
8272
8273fn default_volume_weight() -> f64 {
8274    0.30
8275}
8276
8277fn default_count_weight() -> f64 {
8278    0.25
8279}
8280
8281fn default_duration_weight() -> f64 {
8282    0.20
8283}
8284
8285fn default_recency_weight() -> f64 {
8286    0.15
8287}
8288
8289fn default_mutual_weight() -> f64 {
8290    0.10
8291}
8292
8293fn default_recency_half_life() -> u32 {
8294    90
8295}
8296
8297impl Default for StrengthCalculationSchemaConfig {
8298    fn default() -> Self {
8299        Self {
8300            transaction_volume_weight: 0.30,
8301            transaction_count_weight: 0.25,
8302            relationship_duration_weight: 0.20,
8303            recency_weight: 0.15,
8304            mutual_connections_weight: 0.10,
8305            recency_half_life_days: 90,
8306        }
8307    }
8308}
8309
8310/// Strength thresholds for relationship classification.
8311#[derive(Debug, Clone, Serialize, Deserialize)]
8312pub struct StrengthThresholdsSchemaConfig {
8313    /// Threshold for strong relationships (default: 0.7).
8314    #[serde(default = "default_strong_threshold")]
8315    pub strong: f64,
8316
8317    /// Threshold for moderate relationships (default: 0.4).
8318    #[serde(default = "default_moderate_threshold")]
8319    pub moderate: f64,
8320
8321    /// Threshold for weak relationships (default: 0.1).
8322    #[serde(default = "default_weak_threshold")]
8323    pub weak: f64,
8324}
8325
8326fn default_strong_threshold() -> f64 {
8327    0.7
8328}
8329
8330fn default_moderate_threshold() -> f64 {
8331    0.4
8332}
8333
8334fn default_weak_threshold() -> f64 {
8335    0.1
8336}
8337
8338impl Default for StrengthThresholdsSchemaConfig {
8339    fn default() -> Self {
8340        Self {
8341            strong: 0.7,
8342            moderate: 0.4,
8343            weak: 0.1,
8344        }
8345    }
8346}
8347
8348// =============================================================================
8349// Cross-Process Links Configuration
8350// =============================================================================
8351
8352/// Configuration for cross-process linkages.
8353#[derive(Debug, Clone, Serialize, Deserialize)]
8354pub struct CrossProcessLinksSchemaConfig {
8355    /// Enable cross-process link generation.
8356    #[serde(default)]
8357    pub enabled: bool,
8358
8359    /// Enable inventory links between P2P and O2C.
8360    #[serde(default = "default_true")]
8361    pub inventory_p2p_o2c: bool,
8362
8363    /// Enable payment to bank reconciliation links.
8364    #[serde(default = "default_true")]
8365    pub payment_bank_reconciliation: bool,
8366
8367    /// Enable intercompany bilateral matching.
8368    #[serde(default = "default_true")]
8369    pub intercompany_bilateral: bool,
8370
8371    /// Percentage of GR/Deliveries to link via inventory (0.0 - 1.0).
8372    #[serde(default = "default_inventory_link_rate")]
8373    pub inventory_link_rate: f64,
8374}
8375
8376fn default_inventory_link_rate() -> f64 {
8377    0.30
8378}
8379
8380impl Default for CrossProcessLinksSchemaConfig {
8381    fn default() -> Self {
8382        Self {
8383            enabled: false,
8384            inventory_p2p_o2c: true,
8385            payment_bank_reconciliation: true,
8386            intercompany_bilateral: true,
8387            inventory_link_rate: 0.30,
8388        }
8389    }
8390}
8391
8392// =============================================================================
8393// Organizational Events Configuration
8394// =============================================================================
8395
8396/// Configuration for organizational events (acquisitions, divestitures, etc.).
8397#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8398pub struct OrganizationalEventsSchemaConfig {
8399    /// Enable organizational events.
8400    #[serde(default)]
8401    pub enabled: bool,
8402
8403    /// Effect blending mode (multiplicative, additive, maximum, minimum).
8404    #[serde(default)]
8405    pub effect_blending: EffectBlendingModeConfig,
8406
8407    /// Organizational events (acquisitions, divestitures, reorganizations, etc.).
8408    #[serde(default)]
8409    pub events: Vec<OrganizationalEventSchemaConfig>,
8410
8411    /// Process evolution events.
8412    #[serde(default)]
8413    pub process_evolution: Vec<ProcessEvolutionSchemaConfig>,
8414
8415    /// Technology transition events.
8416    #[serde(default)]
8417    pub technology_transitions: Vec<TechnologyTransitionSchemaConfig>,
8418}
8419
8420/// Effect blending mode for combining multiple event effects.
8421#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
8422#[serde(rename_all = "snake_case")]
8423pub enum EffectBlendingModeConfig {
8424    /// Multiply effects together.
8425    #[default]
8426    Multiplicative,
8427    /// Add effects together.
8428    Additive,
8429    /// Take the maximum effect.
8430    Maximum,
8431    /// Take the minimum effect.
8432    Minimum,
8433}
8434
8435/// Configuration for a single organizational event.
8436#[derive(Debug, Clone, Serialize, Deserialize)]
8437pub struct OrganizationalEventSchemaConfig {
8438    /// Event ID.
8439    pub id: String,
8440
8441    /// Event type and configuration.
8442    pub event_type: OrganizationalEventTypeSchemaConfig,
8443
8444    /// Effective date.
8445    pub effective_date: String,
8446
8447    /// Transition duration in months.
8448    #[serde(default = "default_org_transition_months")]
8449    pub transition_months: u32,
8450
8451    /// Description.
8452    #[serde(default)]
8453    pub description: Option<String>,
8454}
8455
8456fn default_org_transition_months() -> u32 {
8457    6
8458}
8459
8460/// Organizational event type configuration.
8461#[derive(Debug, Clone, Serialize, Deserialize)]
8462#[serde(tag = "type", rename_all = "snake_case")]
8463pub enum OrganizationalEventTypeSchemaConfig {
8464    /// Acquisition event.
8465    Acquisition {
8466        /// Acquired entity code.
8467        acquired_entity: String,
8468        /// Volume increase multiplier.
8469        #[serde(default = "default_acquisition_volume")]
8470        volume_increase: f64,
8471        /// Integration error rate.
8472        #[serde(default = "default_acquisition_error")]
8473        integration_error_rate: f64,
8474        /// Parallel posting days.
8475        #[serde(default = "default_parallel_days")]
8476        parallel_posting_days: u32,
8477    },
8478    /// Divestiture event.
8479    Divestiture {
8480        /// Divested entity code.
8481        divested_entity: String,
8482        /// Volume reduction factor.
8483        #[serde(default = "default_divestiture_volume")]
8484        volume_reduction: f64,
8485        /// Remove entity from generation.
8486        #[serde(default = "default_true_val")]
8487        remove_entity: bool,
8488    },
8489    /// Reorganization event.
8490    Reorganization {
8491        /// Cost center remapping.
8492        #[serde(default)]
8493        cost_center_remapping: std::collections::HashMap<String, String>,
8494        /// Transition error rate.
8495        #[serde(default = "default_reorg_error")]
8496        transition_error_rate: f64,
8497    },
8498    /// Leadership change event.
8499    LeadershipChange {
8500        /// Role that changed.
8501        role: String,
8502        /// Policy changes.
8503        #[serde(default)]
8504        policy_changes: Vec<String>,
8505    },
8506    /// Workforce reduction event.
8507    WorkforceReduction {
8508        /// Reduction percentage.
8509        #[serde(default = "default_workforce_reduction")]
8510        reduction_percent: f64,
8511        /// Error rate increase.
8512        #[serde(default = "default_workforce_error")]
8513        error_rate_increase: f64,
8514    },
8515    /// Merger event.
8516    Merger {
8517        /// Merged entity code.
8518        merged_entity: String,
8519        /// Volume increase multiplier.
8520        #[serde(default = "default_merger_volume")]
8521        volume_increase: f64,
8522    },
8523}
8524
8525fn default_acquisition_volume() -> f64 {
8526    1.35
8527}
8528
8529fn default_acquisition_error() -> f64 {
8530    0.05
8531}
8532
8533fn default_parallel_days() -> u32 {
8534    30
8535}
8536
8537fn default_divestiture_volume() -> f64 {
8538    0.70
8539}
8540
8541fn default_true_val() -> bool {
8542    true
8543}
8544
8545fn default_reorg_error() -> f64 {
8546    0.04
8547}
8548
8549fn default_workforce_reduction() -> f64 {
8550    0.10
8551}
8552
8553fn default_workforce_error() -> f64 {
8554    0.05
8555}
8556
8557fn default_merger_volume() -> f64 {
8558    1.80
8559}
8560
8561/// Configuration for a process evolution event.
8562#[derive(Debug, Clone, Serialize, Deserialize)]
8563pub struct ProcessEvolutionSchemaConfig {
8564    /// Event ID.
8565    pub id: String,
8566
8567    /// Event type.
8568    pub event_type: ProcessEvolutionTypeSchemaConfig,
8569
8570    /// Effective date.
8571    pub effective_date: String,
8572
8573    /// Description.
8574    #[serde(default)]
8575    pub description: Option<String>,
8576}
8577
8578/// Process evolution type configuration.
8579#[derive(Debug, Clone, Serialize, Deserialize)]
8580#[serde(tag = "type", rename_all = "snake_case")]
8581pub enum ProcessEvolutionTypeSchemaConfig {
8582    /// Process automation.
8583    ProcessAutomation {
8584        /// Process name.
8585        process_name: String,
8586        /// Manual rate before.
8587        #[serde(default = "default_manual_before")]
8588        manual_rate_before: f64,
8589        /// Manual rate after.
8590        #[serde(default = "default_manual_after")]
8591        manual_rate_after: f64,
8592    },
8593    /// Approval workflow change.
8594    ApprovalWorkflowChange {
8595        /// Description.
8596        description: String,
8597    },
8598    /// Control enhancement.
8599    ControlEnhancement {
8600        /// Control ID.
8601        control_id: String,
8602        /// Error reduction.
8603        #[serde(default = "default_error_reduction")]
8604        error_reduction: f64,
8605    },
8606}
8607
8608fn default_manual_before() -> f64 {
8609    0.80
8610}
8611
8612fn default_manual_after() -> f64 {
8613    0.15
8614}
8615
8616fn default_error_reduction() -> f64 {
8617    0.02
8618}
8619
8620/// Configuration for a technology transition event.
8621#[derive(Debug, Clone, Serialize, Deserialize)]
8622pub struct TechnologyTransitionSchemaConfig {
8623    /// Event ID.
8624    pub id: String,
8625
8626    /// Event type.
8627    pub event_type: TechnologyTransitionTypeSchemaConfig,
8628
8629    /// Description.
8630    #[serde(default)]
8631    pub description: Option<String>,
8632}
8633
8634/// Technology transition type configuration.
8635#[derive(Debug, Clone, Serialize, Deserialize)]
8636#[serde(tag = "type", rename_all = "snake_case")]
8637pub enum TechnologyTransitionTypeSchemaConfig {
8638    /// ERP migration.
8639    ErpMigration {
8640        /// Source system.
8641        source_system: String,
8642        /// Target system.
8643        target_system: String,
8644        /// Cutover date.
8645        cutover_date: String,
8646        /// Stabilization end date.
8647        stabilization_end: String,
8648        /// Duplicate rate during migration.
8649        #[serde(default = "default_erp_duplicate_rate")]
8650        duplicate_rate: f64,
8651        /// Format mismatch rate.
8652        #[serde(default = "default_format_mismatch")]
8653        format_mismatch_rate: f64,
8654    },
8655    /// Module implementation.
8656    ModuleImplementation {
8657        /// Module name.
8658        module_name: String,
8659        /// Go-live date.
8660        go_live_date: String,
8661    },
8662}
8663
8664fn default_erp_duplicate_rate() -> f64 {
8665    0.02
8666}
8667
8668fn default_format_mismatch() -> f64 {
8669    0.03
8670}
8671
8672// =============================================================================
8673// Behavioral Drift Configuration
8674// =============================================================================
8675
8676/// Configuration for behavioral drift (vendor, customer, employee behavior).
8677///
8678/// **Deprecated (v4.1.2):** this schema section is currently
8679/// validated-but-inert — no runtime code consumes its fields. Users
8680/// who want behavioral drift-style effects should reach for
8681/// `distributions.regime_changes` (v3.5.2+), which drives the
8682/// `DriftController` via the parameter-drift path. The schema type
8683/// remains for backward-compatible YAML loading; it will be removed
8684/// in a future major version once `regime_changes` gains per-entity
8685/// (vendor / customer / employee) targeting.
8686#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8687pub struct BehavioralDriftSchemaConfig {
8688    /// Enable behavioral drift.
8689    #[serde(default)]
8690    pub enabled: bool,
8691
8692    /// Vendor behavior drift.
8693    #[serde(default)]
8694    pub vendor_behavior: VendorBehaviorSchemaConfig,
8695
8696    /// Customer behavior drift.
8697    #[serde(default)]
8698    pub customer_behavior: CustomerBehaviorSchemaConfig,
8699
8700    /// Employee behavior drift.
8701    #[serde(default)]
8702    pub employee_behavior: EmployeeBehaviorSchemaConfig,
8703
8704    /// Collective behavior drift.
8705    #[serde(default)]
8706    pub collective: CollectiveBehaviorSchemaConfig,
8707}
8708
8709/// Vendor behavior drift configuration.
8710#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8711pub struct VendorBehaviorSchemaConfig {
8712    /// Payment terms drift.
8713    #[serde(default)]
8714    pub payment_terms_drift: PaymentTermsDriftSchemaConfig,
8715
8716    /// Quality drift.
8717    #[serde(default)]
8718    pub quality_drift: QualityDriftSchemaConfig,
8719}
8720
8721/// Payment terms drift configuration.
8722#[derive(Debug, Clone, Serialize, Deserialize)]
8723pub struct PaymentTermsDriftSchemaConfig {
8724    /// Extension rate per year (days).
8725    #[serde(default = "default_extension_rate")]
8726    pub extension_rate_per_year: f64,
8727
8728    /// Economic sensitivity.
8729    #[serde(default = "default_economic_sensitivity")]
8730    pub economic_sensitivity: f64,
8731}
8732
8733fn default_extension_rate() -> f64 {
8734    2.5
8735}
8736
8737fn default_economic_sensitivity() -> f64 {
8738    1.0
8739}
8740
8741impl Default for PaymentTermsDriftSchemaConfig {
8742    fn default() -> Self {
8743        Self {
8744            extension_rate_per_year: 2.5,
8745            economic_sensitivity: 1.0,
8746        }
8747    }
8748}
8749
8750/// Quality drift configuration.
8751#[derive(Debug, Clone, Serialize, Deserialize)]
8752pub struct QualityDriftSchemaConfig {
8753    /// New vendor improvement rate (per year).
8754    #[serde(default = "default_improvement_rate")]
8755    pub new_vendor_improvement_rate: f64,
8756
8757    /// Complacency decline rate (per year after first year).
8758    #[serde(default = "default_decline_rate")]
8759    pub complacency_decline_rate: f64,
8760}
8761
8762fn default_improvement_rate() -> f64 {
8763    0.02
8764}
8765
8766fn default_decline_rate() -> f64 {
8767    0.01
8768}
8769
8770impl Default for QualityDriftSchemaConfig {
8771    fn default() -> Self {
8772        Self {
8773            new_vendor_improvement_rate: 0.02,
8774            complacency_decline_rate: 0.01,
8775        }
8776    }
8777}
8778
8779/// Customer behavior drift configuration.
8780#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8781pub struct CustomerBehaviorSchemaConfig {
8782    /// Payment drift.
8783    #[serde(default)]
8784    pub payment_drift: CustomerPaymentDriftSchemaConfig,
8785
8786    /// Order drift.
8787    #[serde(default)]
8788    pub order_drift: OrderDriftSchemaConfig,
8789}
8790
8791/// Customer payment drift configuration.
8792#[derive(Debug, Clone, Serialize, Deserialize)]
8793pub struct CustomerPaymentDriftSchemaConfig {
8794    /// Days extension during downturn (min, max).
8795    #[serde(default = "default_downturn_extension")]
8796    pub downturn_days_extension: (u32, u32),
8797
8798    /// Bad debt increase during downturn.
8799    #[serde(default = "default_bad_debt_increase")]
8800    pub downturn_bad_debt_increase: f64,
8801}
8802
8803fn default_downturn_extension() -> (u32, u32) {
8804    (5, 15)
8805}
8806
8807fn default_bad_debt_increase() -> f64 {
8808    0.02
8809}
8810
8811impl Default for CustomerPaymentDriftSchemaConfig {
8812    fn default() -> Self {
8813        Self {
8814            downturn_days_extension: (5, 15),
8815            downturn_bad_debt_increase: 0.02,
8816        }
8817    }
8818}
8819
8820/// Order drift configuration.
8821#[derive(Debug, Clone, Serialize, Deserialize)]
8822pub struct OrderDriftSchemaConfig {
8823    /// Digital shift rate (per year).
8824    #[serde(default = "default_digital_shift")]
8825    pub digital_shift_rate: f64,
8826}
8827
8828fn default_digital_shift() -> f64 {
8829    0.05
8830}
8831
8832impl Default for OrderDriftSchemaConfig {
8833    fn default() -> Self {
8834        Self {
8835            digital_shift_rate: 0.05,
8836        }
8837    }
8838}
8839
8840/// Employee behavior drift configuration.
8841#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8842pub struct EmployeeBehaviorSchemaConfig {
8843    /// Approval drift.
8844    #[serde(default)]
8845    pub approval_drift: ApprovalDriftSchemaConfig,
8846
8847    /// Error drift.
8848    #[serde(default)]
8849    pub error_drift: ErrorDriftSchemaConfig,
8850}
8851
8852/// Approval drift configuration.
8853#[derive(Debug, Clone, Serialize, Deserialize)]
8854pub struct ApprovalDriftSchemaConfig {
8855    /// EOM intensity increase per year.
8856    #[serde(default = "default_eom_intensity")]
8857    pub eom_intensity_increase_per_year: f64,
8858
8859    /// Rubber stamp volume threshold.
8860    #[serde(default = "default_rubber_stamp")]
8861    pub rubber_stamp_volume_threshold: u32,
8862}
8863
8864fn default_eom_intensity() -> f64 {
8865    0.05
8866}
8867
8868fn default_rubber_stamp() -> u32 {
8869    50
8870}
8871
8872impl Default for ApprovalDriftSchemaConfig {
8873    fn default() -> Self {
8874        Self {
8875            eom_intensity_increase_per_year: 0.05,
8876            rubber_stamp_volume_threshold: 50,
8877        }
8878    }
8879}
8880
8881/// Error drift configuration.
8882#[derive(Debug, Clone, Serialize, Deserialize)]
8883pub struct ErrorDriftSchemaConfig {
8884    /// New employee error rate.
8885    #[serde(default = "default_new_error")]
8886    pub new_employee_error_rate: f64,
8887
8888    /// Learning curve months.
8889    #[serde(default = "default_learning_months")]
8890    pub learning_curve_months: u32,
8891}
8892
8893fn default_new_error() -> f64 {
8894    0.08
8895}
8896
8897fn default_learning_months() -> u32 {
8898    6
8899}
8900
8901impl Default for ErrorDriftSchemaConfig {
8902    fn default() -> Self {
8903        Self {
8904            new_employee_error_rate: 0.08,
8905            learning_curve_months: 6,
8906        }
8907    }
8908}
8909
8910/// Collective behavior drift configuration.
8911#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8912pub struct CollectiveBehaviorSchemaConfig {
8913    /// Automation adoption configuration.
8914    #[serde(default)]
8915    pub automation_adoption: AutomationAdoptionSchemaConfig,
8916}
8917
8918/// Automation adoption configuration.
8919#[derive(Debug, Clone, Serialize, Deserialize)]
8920pub struct AutomationAdoptionSchemaConfig {
8921    /// Enable S-curve adoption model.
8922    #[serde(default)]
8923    pub s_curve_enabled: bool,
8924
8925    /// Adoption midpoint in months.
8926    #[serde(default = "default_midpoint")]
8927    pub adoption_midpoint_months: u32,
8928
8929    /// Steepness of adoption curve.
8930    #[serde(default = "default_steepness")]
8931    pub steepness: f64,
8932}
8933
8934fn default_midpoint() -> u32 {
8935    24
8936}
8937
8938fn default_steepness() -> f64 {
8939    0.15
8940}
8941
8942impl Default for AutomationAdoptionSchemaConfig {
8943    fn default() -> Self {
8944        Self {
8945            s_curve_enabled: false,
8946            adoption_midpoint_months: 24,
8947            steepness: 0.15,
8948        }
8949    }
8950}
8951
8952// =============================================================================
8953// Market Drift Configuration
8954// =============================================================================
8955
8956/// Configuration for market drift (economic cycles, commodities, price shocks).
8957///
8958/// **Deprecated (v4.1.2):** validated-but-inert. Use
8959/// `distributions.regime_changes.economic_cycle` +
8960/// `distributions.regime_changes.parameter_drifts` for the
8961/// equivalent runtime behaviour (shipped in v3.5.2). The schema
8962/// type remains for backward-compatible YAML loading; will be
8963/// removed in v5.0.
8964#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8965pub struct MarketDriftSchemaConfig {
8966    /// Enable market drift.
8967    #[serde(default)]
8968    pub enabled: bool,
8969
8970    /// Economic cycle configuration.
8971    #[serde(default)]
8972    pub economic_cycle: MarketEconomicCycleSchemaConfig,
8973
8974    /// Industry-specific cycles.
8975    #[serde(default)]
8976    pub industry_cycles: std::collections::HashMap<String, IndustryCycleSchemaConfig>,
8977
8978    /// Commodity drift configuration.
8979    #[serde(default)]
8980    pub commodities: CommoditiesSchemaConfig,
8981}
8982
8983/// Market economic cycle configuration.
8984#[derive(Debug, Clone, Serialize, Deserialize)]
8985pub struct MarketEconomicCycleSchemaConfig {
8986    /// Enable economic cycle.
8987    #[serde(default)]
8988    pub enabled: bool,
8989
8990    /// Cycle type.
8991    #[serde(default)]
8992    pub cycle_type: CycleTypeSchemaConfig,
8993
8994    /// Cycle period in months.
8995    #[serde(default = "default_market_cycle_period")]
8996    pub period_months: u32,
8997
8998    /// Amplitude.
8999    #[serde(default = "default_market_amplitude")]
9000    pub amplitude: f64,
9001
9002    /// Recession configuration.
9003    #[serde(default)]
9004    pub recession: RecessionSchemaConfig,
9005}
9006
9007fn default_market_cycle_period() -> u32 {
9008    48
9009}
9010
9011fn default_market_amplitude() -> f64 {
9012    0.15
9013}
9014
9015impl Default for MarketEconomicCycleSchemaConfig {
9016    fn default() -> Self {
9017        Self {
9018            enabled: false,
9019            cycle_type: CycleTypeSchemaConfig::Sinusoidal,
9020            period_months: 48,
9021            amplitude: 0.15,
9022            recession: RecessionSchemaConfig::default(),
9023        }
9024    }
9025}
9026
9027/// Cycle type configuration.
9028#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
9029#[serde(rename_all = "snake_case")]
9030pub enum CycleTypeSchemaConfig {
9031    /// Sinusoidal cycle.
9032    #[default]
9033    Sinusoidal,
9034    /// Asymmetric cycle.
9035    Asymmetric,
9036    /// Mean-reverting cycle.
9037    MeanReverting,
9038}
9039
9040/// Recession configuration.
9041#[derive(Debug, Clone, Serialize, Deserialize)]
9042pub struct RecessionSchemaConfig {
9043    /// Enable recession simulation.
9044    #[serde(default)]
9045    pub enabled: bool,
9046
9047    /// Probability per year.
9048    #[serde(default = "default_recession_prob")]
9049    pub probability_per_year: f64,
9050
9051    /// Severity.
9052    #[serde(default)]
9053    pub severity: RecessionSeveritySchemaConfig,
9054
9055    /// Specific recession periods.
9056    #[serde(default)]
9057    pub recession_periods: Vec<RecessionPeriodSchemaConfig>,
9058}
9059
9060fn default_recession_prob() -> f64 {
9061    0.10
9062}
9063
9064impl Default for RecessionSchemaConfig {
9065    fn default() -> Self {
9066        Self {
9067            enabled: false,
9068            probability_per_year: 0.10,
9069            severity: RecessionSeveritySchemaConfig::Moderate,
9070            recession_periods: Vec::new(),
9071        }
9072    }
9073}
9074
9075/// Recession severity configuration.
9076#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
9077#[serde(rename_all = "snake_case")]
9078pub enum RecessionSeveritySchemaConfig {
9079    /// Mild recession.
9080    Mild,
9081    /// Moderate recession.
9082    #[default]
9083    Moderate,
9084    /// Severe recession.
9085    Severe,
9086}
9087
9088/// Recession period configuration.
9089#[derive(Debug, Clone, Serialize, Deserialize)]
9090pub struct RecessionPeriodSchemaConfig {
9091    /// Start month.
9092    pub start_month: u32,
9093    /// Duration in months.
9094    pub duration_months: u32,
9095}
9096
9097/// Industry cycle configuration.
9098#[derive(Debug, Clone, Serialize, Deserialize)]
9099pub struct IndustryCycleSchemaConfig {
9100    /// Period in months.
9101    #[serde(default = "default_industry_period")]
9102    pub period_months: u32,
9103
9104    /// Amplitude.
9105    #[serde(default = "default_industry_amp")]
9106    pub amplitude: f64,
9107}
9108
9109fn default_industry_period() -> u32 {
9110    36
9111}
9112
9113fn default_industry_amp() -> f64 {
9114    0.20
9115}
9116
9117/// Commodities drift configuration.
9118#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9119pub struct CommoditiesSchemaConfig {
9120    /// Enable commodity drift.
9121    #[serde(default)]
9122    pub enabled: bool,
9123
9124    /// Commodity items.
9125    #[serde(default)]
9126    pub items: Vec<CommodityItemSchemaConfig>,
9127}
9128
9129/// Commodity item configuration.
9130#[derive(Debug, Clone, Serialize, Deserialize)]
9131pub struct CommodityItemSchemaConfig {
9132    /// Commodity name.
9133    pub name: String,
9134
9135    /// Volatility.
9136    #[serde(default = "default_volatility")]
9137    pub volatility: f64,
9138
9139    /// COGS pass-through.
9140    #[serde(default)]
9141    pub cogs_pass_through: f64,
9142
9143    /// Overhead pass-through.
9144    #[serde(default)]
9145    pub overhead_pass_through: f64,
9146}
9147
9148fn default_volatility() -> f64 {
9149    0.20
9150}
9151
9152// =============================================================================
9153// Drift Labeling Configuration
9154// =============================================================================
9155
9156/// Configuration for drift ground truth labeling.
9157///
9158/// **Deprecated (v4.1.2):** validated-but-inert. The v3.3.0
9159/// analytics-metadata phase (`DriftEventGenerator` +
9160/// `AnalyticsMetadataSnapshot.drift_events`) produces drift labels
9161/// at runtime — configure it via `analytics_metadata.drift_events`
9162/// instead. The schema type remains for backward-compatible YAML
9163/// loading; will be removed in v5.0.
9164#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9165pub struct DriftLabelingSchemaConfig {
9166    /// Enable drift labeling.
9167    #[serde(default)]
9168    pub enabled: bool,
9169
9170    /// Statistical drift labeling.
9171    #[serde(default)]
9172    pub statistical: StatisticalDriftLabelingSchemaConfig,
9173
9174    /// Categorical drift labeling.
9175    #[serde(default)]
9176    pub categorical: CategoricalDriftLabelingSchemaConfig,
9177
9178    /// Temporal drift labeling.
9179    #[serde(default)]
9180    pub temporal: TemporalDriftLabelingSchemaConfig,
9181
9182    /// Regulatory calendar preset.
9183    #[serde(default)]
9184    pub regulatory_calendar_preset: Option<String>,
9185}
9186
9187/// Statistical drift labeling configuration.
9188#[derive(Debug, Clone, Serialize, Deserialize)]
9189pub struct StatisticalDriftLabelingSchemaConfig {
9190    /// Enable statistical drift labeling.
9191    #[serde(default = "default_true_val")]
9192    pub enabled: bool,
9193
9194    /// Minimum magnitude threshold.
9195    #[serde(default = "default_min_magnitude")]
9196    pub min_magnitude_threshold: f64,
9197}
9198
9199fn default_min_magnitude() -> f64 {
9200    0.05
9201}
9202
9203impl Default for StatisticalDriftLabelingSchemaConfig {
9204    fn default() -> Self {
9205        Self {
9206            enabled: true,
9207            min_magnitude_threshold: 0.05,
9208        }
9209    }
9210}
9211
9212/// Categorical drift labeling configuration.
9213#[derive(Debug, Clone, Serialize, Deserialize)]
9214pub struct CategoricalDriftLabelingSchemaConfig {
9215    /// Enable categorical drift labeling.
9216    #[serde(default = "default_true_val")]
9217    pub enabled: bool,
9218}
9219
9220impl Default for CategoricalDriftLabelingSchemaConfig {
9221    fn default() -> Self {
9222        Self { enabled: true }
9223    }
9224}
9225
9226/// Temporal drift labeling configuration.
9227#[derive(Debug, Clone, Serialize, Deserialize)]
9228pub struct TemporalDriftLabelingSchemaConfig {
9229    /// Enable temporal drift labeling.
9230    #[serde(default = "default_true_val")]
9231    pub enabled: bool,
9232}
9233
9234impl Default for TemporalDriftLabelingSchemaConfig {
9235    fn default() -> Self {
9236        Self { enabled: true }
9237    }
9238}
9239
9240// =============================================================================
9241// Enhanced Anomaly Injection Configuration
9242// =============================================================================
9243
9244/// Enhanced anomaly injection configuration.
9245///
9246/// Provides comprehensive anomaly injection capabilities including:
9247/// - Multi-stage fraud schemes (embezzlement, revenue manipulation, kickbacks)
9248/// - Correlated anomaly injection (co-occurrence patterns, error cascades)
9249/// - Near-miss generation for false positive reduction
9250/// - Detection difficulty classification
9251/// - Context-aware injection based on entity behavior
9252#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9253pub struct EnhancedAnomalyConfig {
9254    /// Enable enhanced anomaly injection.
9255    #[serde(default)]
9256    pub enabled: bool,
9257
9258    /// Base anomaly rates.
9259    #[serde(default)]
9260    pub rates: AnomalyRateConfig,
9261
9262    /// Multi-stage fraud scheme configuration.
9263    #[serde(default)]
9264    pub multi_stage_schemes: MultiStageSchemeConfig,
9265
9266    /// Correlated anomaly injection configuration.
9267    #[serde(default)]
9268    pub correlated_injection: CorrelatedInjectionConfig,
9269
9270    /// Near-miss generation configuration.
9271    #[serde(default)]
9272    pub near_miss: NearMissConfig,
9273
9274    /// Detection difficulty classification configuration.
9275    #[serde(default)]
9276    pub difficulty_classification: DifficultyClassificationConfig,
9277
9278    /// Context-aware injection configuration.
9279    #[serde(default)]
9280    pub context_aware: ContextAwareConfig,
9281
9282    /// Enhanced labeling configuration.
9283    #[serde(default)]
9284    pub labeling: EnhancedLabelingConfig,
9285}
9286
9287/// Base anomaly rate configuration.
9288#[derive(Debug, Clone, Serialize, Deserialize)]
9289pub struct AnomalyRateConfig {
9290    /// Total anomaly rate (0.0 to 1.0).
9291    #[serde(default = "default_total_anomaly_rate")]
9292    pub total_rate: f64,
9293
9294    /// Fraud anomaly rate.
9295    #[serde(default = "default_fraud_anomaly_rate")]
9296    pub fraud_rate: f64,
9297
9298    /// Error anomaly rate.
9299    #[serde(default = "default_error_anomaly_rate")]
9300    pub error_rate: f64,
9301
9302    /// Process issue rate.
9303    #[serde(default = "default_process_anomaly_rate")]
9304    pub process_rate: f64,
9305}
9306
9307fn default_total_anomaly_rate() -> f64 {
9308    0.03
9309}
9310fn default_fraud_anomaly_rate() -> f64 {
9311    0.01
9312}
9313fn default_error_anomaly_rate() -> f64 {
9314    0.015
9315}
9316fn default_process_anomaly_rate() -> f64 {
9317    0.005
9318}
9319
9320impl Default for AnomalyRateConfig {
9321    fn default() -> Self {
9322        Self {
9323            total_rate: default_total_anomaly_rate(),
9324            fraud_rate: default_fraud_anomaly_rate(),
9325            error_rate: default_error_anomaly_rate(),
9326            process_rate: default_process_anomaly_rate(),
9327        }
9328    }
9329}
9330
9331/// Multi-stage fraud scheme configuration.
9332#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9333pub struct MultiStageSchemeConfig {
9334    /// Enable multi-stage fraud schemes.
9335    #[serde(default)]
9336    pub enabled: bool,
9337
9338    /// Embezzlement scheme configuration.
9339    #[serde(default)]
9340    pub embezzlement: EmbezzlementSchemeConfig,
9341
9342    /// Revenue manipulation scheme configuration.
9343    #[serde(default)]
9344    pub revenue_manipulation: RevenueManipulationSchemeConfig,
9345
9346    /// Vendor kickback scheme configuration.
9347    #[serde(default)]
9348    pub kickback: KickbackSchemeConfig,
9349}
9350
9351/// Embezzlement scheme configuration.
9352#[derive(Debug, Clone, Serialize, Deserialize)]
9353pub struct EmbezzlementSchemeConfig {
9354    /// Probability of starting an embezzlement scheme per perpetrator per year.
9355    #[serde(default = "default_embezzlement_probability")]
9356    pub probability: f64,
9357
9358    /// Testing stage configuration.
9359    #[serde(default)]
9360    pub testing_stage: SchemeStageConfig,
9361
9362    /// Escalation stage configuration.
9363    #[serde(default)]
9364    pub escalation_stage: SchemeStageConfig,
9365
9366    /// Acceleration stage configuration.
9367    #[serde(default)]
9368    pub acceleration_stage: SchemeStageConfig,
9369
9370    /// Desperation stage configuration.
9371    #[serde(default)]
9372    pub desperation_stage: SchemeStageConfig,
9373}
9374
9375fn default_embezzlement_probability() -> f64 {
9376    0.02
9377}
9378
9379impl Default for EmbezzlementSchemeConfig {
9380    fn default() -> Self {
9381        Self {
9382            probability: default_embezzlement_probability(),
9383            testing_stage: SchemeStageConfig {
9384                duration_months: 2,
9385                amount_min: 100.0,
9386                amount_max: 500.0,
9387                transaction_count_min: 2,
9388                transaction_count_max: 5,
9389                difficulty: "hard".to_string(),
9390            },
9391            escalation_stage: SchemeStageConfig {
9392                duration_months: 6,
9393                amount_min: 500.0,
9394                amount_max: 2000.0,
9395                transaction_count_min: 3,
9396                transaction_count_max: 8,
9397                difficulty: "moderate".to_string(),
9398            },
9399            acceleration_stage: SchemeStageConfig {
9400                duration_months: 3,
9401                amount_min: 2000.0,
9402                amount_max: 10000.0,
9403                transaction_count_min: 5,
9404                transaction_count_max: 12,
9405                difficulty: "easy".to_string(),
9406            },
9407            desperation_stage: SchemeStageConfig {
9408                duration_months: 1,
9409                amount_min: 10000.0,
9410                amount_max: 50000.0,
9411                transaction_count_min: 3,
9412                transaction_count_max: 6,
9413                difficulty: "trivial".to_string(),
9414            },
9415        }
9416    }
9417}
9418
9419/// Revenue manipulation scheme configuration.
9420#[derive(Debug, Clone, Serialize, Deserialize)]
9421pub struct RevenueManipulationSchemeConfig {
9422    /// Probability of starting a revenue manipulation scheme per period.
9423    #[serde(default = "default_revenue_manipulation_probability")]
9424    pub probability: f64,
9425
9426    /// Early revenue recognition inflation target (Q4).
9427    #[serde(default = "default_early_recognition_target")]
9428    pub early_recognition_target: f64,
9429
9430    /// Expense deferral inflation target (Q1).
9431    #[serde(default = "default_expense_deferral_target")]
9432    pub expense_deferral_target: f64,
9433
9434    /// Reserve release inflation target (Q2).
9435    #[serde(default = "default_reserve_release_target")]
9436    pub reserve_release_target: f64,
9437
9438    /// Channel stuffing inflation target (Q4).
9439    #[serde(default = "default_channel_stuffing_target")]
9440    pub channel_stuffing_target: f64,
9441}
9442
9443fn default_revenue_manipulation_probability() -> f64 {
9444    0.01
9445}
9446fn default_early_recognition_target() -> f64 {
9447    0.02
9448}
9449fn default_expense_deferral_target() -> f64 {
9450    0.03
9451}
9452fn default_reserve_release_target() -> f64 {
9453    0.02
9454}
9455fn default_channel_stuffing_target() -> f64 {
9456    0.05
9457}
9458
9459impl Default for RevenueManipulationSchemeConfig {
9460    fn default() -> Self {
9461        Self {
9462            probability: default_revenue_manipulation_probability(),
9463            early_recognition_target: default_early_recognition_target(),
9464            expense_deferral_target: default_expense_deferral_target(),
9465            reserve_release_target: default_reserve_release_target(),
9466            channel_stuffing_target: default_channel_stuffing_target(),
9467        }
9468    }
9469}
9470
9471/// Vendor kickback scheme configuration.
9472#[derive(Debug, Clone, Serialize, Deserialize)]
9473pub struct KickbackSchemeConfig {
9474    /// Probability of starting a kickback scheme.
9475    #[serde(default = "default_kickback_probability")]
9476    pub probability: f64,
9477
9478    /// Minimum price inflation percentage.
9479    #[serde(default = "default_kickback_inflation_min")]
9480    pub inflation_min: f64,
9481
9482    /// Maximum price inflation percentage.
9483    #[serde(default = "default_kickback_inflation_max")]
9484    pub inflation_max: f64,
9485
9486    /// Kickback percentage (of inflation).
9487    #[serde(default = "default_kickback_percent")]
9488    pub kickback_percent: f64,
9489
9490    /// Setup duration in months.
9491    #[serde(default = "default_kickback_setup_months")]
9492    pub setup_months: u32,
9493
9494    /// Main operation duration in months.
9495    #[serde(default = "default_kickback_operation_months")]
9496    pub operation_months: u32,
9497}
9498
9499fn default_kickback_probability() -> f64 {
9500    0.01
9501}
9502fn default_kickback_inflation_min() -> f64 {
9503    0.10
9504}
9505fn default_kickback_inflation_max() -> f64 {
9506    0.25
9507}
9508fn default_kickback_percent() -> f64 {
9509    0.50
9510}
9511fn default_kickback_setup_months() -> u32 {
9512    3
9513}
9514fn default_kickback_operation_months() -> u32 {
9515    12
9516}
9517
9518impl Default for KickbackSchemeConfig {
9519    fn default() -> Self {
9520        Self {
9521            probability: default_kickback_probability(),
9522            inflation_min: default_kickback_inflation_min(),
9523            inflation_max: default_kickback_inflation_max(),
9524            kickback_percent: default_kickback_percent(),
9525            setup_months: default_kickback_setup_months(),
9526            operation_months: default_kickback_operation_months(),
9527        }
9528    }
9529}
9530
9531/// Individual scheme stage configuration.
9532#[derive(Debug, Clone, Serialize, Deserialize)]
9533pub struct SchemeStageConfig {
9534    /// Duration in months.
9535    pub duration_months: u32,
9536
9537    /// Minimum transaction amount.
9538    pub amount_min: f64,
9539
9540    /// Maximum transaction amount.
9541    pub amount_max: f64,
9542
9543    /// Minimum number of transactions.
9544    pub transaction_count_min: u32,
9545
9546    /// Maximum number of transactions.
9547    pub transaction_count_max: u32,
9548
9549    /// Detection difficulty level (trivial, easy, moderate, hard, expert).
9550    pub difficulty: String,
9551}
9552
9553impl Default for SchemeStageConfig {
9554    fn default() -> Self {
9555        Self {
9556            duration_months: 3,
9557            amount_min: 100.0,
9558            amount_max: 1000.0,
9559            transaction_count_min: 2,
9560            transaction_count_max: 10,
9561            difficulty: "moderate".to_string(),
9562        }
9563    }
9564}
9565
9566/// Correlated anomaly injection configuration.
9567#[derive(Debug, Clone, Serialize, Deserialize)]
9568pub struct CorrelatedInjectionConfig {
9569    /// Enable correlated anomaly injection.
9570    #[serde(default)]
9571    pub enabled: bool,
9572
9573    /// Enable fraud concealment co-occurrence patterns.
9574    #[serde(default = "default_true_val")]
9575    pub fraud_concealment: bool,
9576
9577    /// Enable error cascade patterns.
9578    #[serde(default = "default_true_val")]
9579    pub error_cascade: bool,
9580
9581    /// Enable temporal clustering (period-end spikes).
9582    #[serde(default = "default_true_val")]
9583    pub temporal_clustering: bool,
9584
9585    /// Temporal clustering configuration.
9586    #[serde(default)]
9587    pub temporal_clustering_config: TemporalClusteringConfig,
9588
9589    /// Co-occurrence patterns.
9590    #[serde(default)]
9591    pub co_occurrence_patterns: Vec<CoOccurrencePatternConfig>,
9592}
9593
9594impl Default for CorrelatedInjectionConfig {
9595    fn default() -> Self {
9596        Self {
9597            enabled: false,
9598            fraud_concealment: true,
9599            error_cascade: true,
9600            temporal_clustering: true,
9601            temporal_clustering_config: TemporalClusteringConfig::default(),
9602            co_occurrence_patterns: Vec::new(),
9603        }
9604    }
9605}
9606
9607/// Temporal clustering configuration.
9608#[derive(Debug, Clone, Serialize, Deserialize)]
9609pub struct TemporalClusteringConfig {
9610    /// Period-end error multiplier.
9611    #[serde(default = "default_period_end_multiplier")]
9612    pub period_end_multiplier: f64,
9613
9614    /// Number of business days before period end to apply multiplier.
9615    #[serde(default = "default_period_end_days")]
9616    pub period_end_days: u32,
9617
9618    /// Quarter-end additional multiplier.
9619    #[serde(default = "default_quarter_end_multiplier")]
9620    pub quarter_end_multiplier: f64,
9621
9622    /// Year-end additional multiplier.
9623    #[serde(default = "default_year_end_multiplier")]
9624    pub year_end_multiplier: f64,
9625}
9626
9627fn default_period_end_multiplier() -> f64 {
9628    2.5
9629}
9630fn default_period_end_days() -> u32 {
9631    5
9632}
9633fn default_quarter_end_multiplier() -> f64 {
9634    1.5
9635}
9636fn default_year_end_multiplier() -> f64 {
9637    2.0
9638}
9639
9640impl Default for TemporalClusteringConfig {
9641    fn default() -> Self {
9642        Self {
9643            period_end_multiplier: default_period_end_multiplier(),
9644            period_end_days: default_period_end_days(),
9645            quarter_end_multiplier: default_quarter_end_multiplier(),
9646            year_end_multiplier: default_year_end_multiplier(),
9647        }
9648    }
9649}
9650
9651/// Co-occurrence pattern configuration.
9652#[derive(Debug, Clone, Serialize, Deserialize)]
9653pub struct CoOccurrencePatternConfig {
9654    /// Pattern name.
9655    pub name: String,
9656
9657    /// Primary anomaly type that triggers the pattern.
9658    pub primary_type: String,
9659
9660    /// Correlated anomalies.
9661    pub correlated: Vec<CorrelatedAnomalyConfig>,
9662}
9663
9664/// Correlated anomaly configuration.
9665#[derive(Debug, Clone, Serialize, Deserialize)]
9666pub struct CorrelatedAnomalyConfig {
9667    /// Anomaly type.
9668    pub anomaly_type: String,
9669
9670    /// Probability of occurrence (0.0 to 1.0).
9671    pub probability: f64,
9672
9673    /// Minimum lag in days.
9674    pub lag_days_min: i32,
9675
9676    /// Maximum lag in days.
9677    pub lag_days_max: i32,
9678}
9679
9680/// Near-miss generation configuration.
9681#[derive(Debug, Clone, Serialize, Deserialize)]
9682pub struct NearMissConfig {
9683    /// Enable near-miss generation.
9684    #[serde(default)]
9685    pub enabled: bool,
9686
9687    /// Proportion of "anomalies" that are actually near-misses (0.0 to 1.0).
9688    #[serde(default = "default_near_miss_proportion")]
9689    pub proportion: f64,
9690
9691    /// Enable near-duplicate pattern.
9692    #[serde(default = "default_true_val")]
9693    pub near_duplicate: bool,
9694
9695    /// Near-duplicate date difference range in days.
9696    #[serde(default)]
9697    pub near_duplicate_days: NearDuplicateDaysConfig,
9698
9699    /// Enable threshold proximity pattern.
9700    #[serde(default = "default_true_val")]
9701    pub threshold_proximity: bool,
9702
9703    /// Threshold proximity range (e.g., 0.90-0.99 of threshold).
9704    #[serde(default)]
9705    pub threshold_proximity_range: ThresholdProximityRangeConfig,
9706
9707    /// Enable unusual but legitimate patterns.
9708    #[serde(default = "default_true_val")]
9709    pub unusual_legitimate: bool,
9710
9711    /// Types of unusual legitimate patterns to generate.
9712    #[serde(default = "default_unusual_legitimate_types")]
9713    pub unusual_legitimate_types: Vec<String>,
9714
9715    /// Enable corrected error patterns.
9716    #[serde(default = "default_true_val")]
9717    pub corrected_errors: bool,
9718
9719    /// Corrected error correction lag range in days.
9720    #[serde(default)]
9721    pub corrected_error_lag: CorrectedErrorLagConfig,
9722}
9723
9724fn default_near_miss_proportion() -> f64 {
9725    0.30
9726}
9727
9728fn default_unusual_legitimate_types() -> Vec<String> {
9729    vec![
9730        "year_end_bonus".to_string(),
9731        "contract_prepayment".to_string(),
9732        "insurance_claim".to_string(),
9733        "settlement_payment".to_string(),
9734    ]
9735}
9736
9737impl Default for NearMissConfig {
9738    fn default() -> Self {
9739        Self {
9740            enabled: false,
9741            proportion: default_near_miss_proportion(),
9742            near_duplicate: true,
9743            near_duplicate_days: NearDuplicateDaysConfig::default(),
9744            threshold_proximity: true,
9745            threshold_proximity_range: ThresholdProximityRangeConfig::default(),
9746            unusual_legitimate: true,
9747            unusual_legitimate_types: default_unusual_legitimate_types(),
9748            corrected_errors: true,
9749            corrected_error_lag: CorrectedErrorLagConfig::default(),
9750        }
9751    }
9752}
9753
9754/// Near-duplicate days configuration.
9755#[derive(Debug, Clone, Serialize, Deserialize)]
9756pub struct NearDuplicateDaysConfig {
9757    /// Minimum days apart.
9758    #[serde(default = "default_near_duplicate_min")]
9759    pub min: u32,
9760
9761    /// Maximum days apart.
9762    #[serde(default = "default_near_duplicate_max")]
9763    pub max: u32,
9764}
9765
9766fn default_near_duplicate_min() -> u32 {
9767    1
9768}
9769fn default_near_duplicate_max() -> u32 {
9770    3
9771}
9772
9773impl Default for NearDuplicateDaysConfig {
9774    fn default() -> Self {
9775        Self {
9776            min: default_near_duplicate_min(),
9777            max: default_near_duplicate_max(),
9778        }
9779    }
9780}
9781
9782/// Threshold proximity range configuration.
9783#[derive(Debug, Clone, Serialize, Deserialize)]
9784pub struct ThresholdProximityRangeConfig {
9785    /// Minimum proximity (e.g., 0.90 = 90% of threshold).
9786    #[serde(default = "default_threshold_proximity_min")]
9787    pub min: f64,
9788
9789    /// Maximum proximity (e.g., 0.99 = 99% of threshold).
9790    #[serde(default = "default_threshold_proximity_max")]
9791    pub max: f64,
9792}
9793
9794fn default_threshold_proximity_min() -> f64 {
9795    0.90
9796}
9797fn default_threshold_proximity_max() -> f64 {
9798    0.99
9799}
9800
9801impl Default for ThresholdProximityRangeConfig {
9802    fn default() -> Self {
9803        Self {
9804            min: default_threshold_proximity_min(),
9805            max: default_threshold_proximity_max(),
9806        }
9807    }
9808}
9809
9810/// Corrected error lag configuration.
9811#[derive(Debug, Clone, Serialize, Deserialize)]
9812pub struct CorrectedErrorLagConfig {
9813    /// Minimum correction lag in days.
9814    #[serde(default = "default_corrected_error_lag_min")]
9815    pub min: u32,
9816
9817    /// Maximum correction lag in days.
9818    #[serde(default = "default_corrected_error_lag_max")]
9819    pub max: u32,
9820}
9821
9822fn default_corrected_error_lag_min() -> u32 {
9823    1
9824}
9825fn default_corrected_error_lag_max() -> u32 {
9826    5
9827}
9828
9829impl Default for CorrectedErrorLagConfig {
9830    fn default() -> Self {
9831        Self {
9832            min: default_corrected_error_lag_min(),
9833            max: default_corrected_error_lag_max(),
9834        }
9835    }
9836}
9837
9838/// Detection difficulty classification configuration.
9839#[derive(Debug, Clone, Serialize, Deserialize)]
9840pub struct DifficultyClassificationConfig {
9841    /// Enable detection difficulty classification.
9842    #[serde(default)]
9843    pub enabled: bool,
9844
9845    /// Target distribution of difficulty levels.
9846    #[serde(default)]
9847    pub target_distribution: DifficultyDistributionConfig,
9848}
9849
9850impl Default for DifficultyClassificationConfig {
9851    fn default() -> Self {
9852        Self {
9853            enabled: true,
9854            target_distribution: DifficultyDistributionConfig::default(),
9855        }
9856    }
9857}
9858
9859/// Target distribution of detection difficulty levels.
9860#[derive(Debug, Clone, Serialize, Deserialize)]
9861pub struct DifficultyDistributionConfig {
9862    /// Proportion of trivial anomalies (expected 99% detection).
9863    #[serde(default = "default_difficulty_trivial")]
9864    pub trivial: f64,
9865
9866    /// Proportion of easy anomalies (expected 90% detection).
9867    #[serde(default = "default_difficulty_easy")]
9868    pub easy: f64,
9869
9870    /// Proportion of moderate anomalies (expected 70% detection).
9871    #[serde(default = "default_difficulty_moderate")]
9872    pub moderate: f64,
9873
9874    /// Proportion of hard anomalies (expected 40% detection).
9875    #[serde(default = "default_difficulty_hard")]
9876    pub hard: f64,
9877
9878    /// Proportion of expert anomalies (expected 15% detection).
9879    #[serde(default = "default_difficulty_expert")]
9880    pub expert: f64,
9881}
9882
9883fn default_difficulty_trivial() -> f64 {
9884    0.15
9885}
9886fn default_difficulty_easy() -> f64 {
9887    0.25
9888}
9889fn default_difficulty_moderate() -> f64 {
9890    0.30
9891}
9892fn default_difficulty_hard() -> f64 {
9893    0.20
9894}
9895fn default_difficulty_expert() -> f64 {
9896    0.10
9897}
9898
9899impl Default for DifficultyDistributionConfig {
9900    fn default() -> Self {
9901        Self {
9902            trivial: default_difficulty_trivial(),
9903            easy: default_difficulty_easy(),
9904            moderate: default_difficulty_moderate(),
9905            hard: default_difficulty_hard(),
9906            expert: default_difficulty_expert(),
9907        }
9908    }
9909}
9910
9911/// Context-aware injection configuration.
9912#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9913pub struct ContextAwareConfig {
9914    /// Enable context-aware injection.
9915    #[serde(default)]
9916    pub enabled: bool,
9917
9918    /// Vendor-specific anomaly rules.
9919    #[serde(default)]
9920    pub vendor_rules: VendorAnomalyRulesConfig,
9921
9922    /// Employee-specific anomaly rules.
9923    #[serde(default)]
9924    pub employee_rules: EmployeeAnomalyRulesConfig,
9925
9926    /// Account-specific anomaly rules.
9927    #[serde(default)]
9928    pub account_rules: AccountAnomalyRulesConfig,
9929
9930    /// Behavioral baseline configuration.
9931    #[serde(default)]
9932    pub behavioral_baseline: BehavioralBaselineConfig,
9933}
9934
9935/// Vendor-specific anomaly rules configuration.
9936#[derive(Debug, Clone, Serialize, Deserialize)]
9937pub struct VendorAnomalyRulesConfig {
9938    /// Error rate multiplier for new vendors (< threshold days).
9939    #[serde(default = "default_new_vendor_multiplier")]
9940    pub new_vendor_error_multiplier: f64,
9941
9942    /// Days threshold for "new" vendor classification.
9943    #[serde(default = "default_new_vendor_threshold")]
9944    pub new_vendor_threshold_days: u32,
9945
9946    /// Error rate multiplier for international vendors.
9947    #[serde(default = "default_international_multiplier")]
9948    pub international_error_multiplier: f64,
9949
9950    /// Strategic vendor anomaly types (may differ from general vendors).
9951    #[serde(default = "default_strategic_vendor_types")]
9952    pub strategic_vendor_anomaly_types: Vec<String>,
9953}
9954
9955fn default_new_vendor_multiplier() -> f64 {
9956    2.5
9957}
9958fn default_new_vendor_threshold() -> u32 {
9959    90
9960}
9961fn default_international_multiplier() -> f64 {
9962    1.5
9963}
9964fn default_strategic_vendor_types() -> Vec<String> {
9965    vec![
9966        "pricing_dispute".to_string(),
9967        "contract_violation".to_string(),
9968    ]
9969}
9970
9971impl Default for VendorAnomalyRulesConfig {
9972    fn default() -> Self {
9973        Self {
9974            new_vendor_error_multiplier: default_new_vendor_multiplier(),
9975            new_vendor_threshold_days: default_new_vendor_threshold(),
9976            international_error_multiplier: default_international_multiplier(),
9977            strategic_vendor_anomaly_types: default_strategic_vendor_types(),
9978        }
9979    }
9980}
9981
9982/// Employee-specific anomaly rules configuration.
9983#[derive(Debug, Clone, Serialize, Deserialize)]
9984pub struct EmployeeAnomalyRulesConfig {
9985    /// Error rate for new employees (< threshold days).
9986    #[serde(default = "default_new_employee_rate")]
9987    pub new_employee_error_rate: f64,
9988
9989    /// Days threshold for "new" employee classification.
9990    #[serde(default = "default_new_employee_threshold")]
9991    pub new_employee_threshold_days: u32,
9992
9993    /// Transaction volume threshold for fatigue errors.
9994    #[serde(default = "default_volume_fatigue_threshold")]
9995    pub volume_fatigue_threshold: u32,
9996
9997    /// Error rate multiplier when primary approver is absent.
9998    #[serde(default = "default_coverage_multiplier")]
9999    pub coverage_error_multiplier: f64,
10000}
10001
10002fn default_new_employee_rate() -> f64 {
10003    0.05
10004}
10005fn default_new_employee_threshold() -> u32 {
10006    180
10007}
10008fn default_volume_fatigue_threshold() -> u32 {
10009    50
10010}
10011fn default_coverage_multiplier() -> f64 {
10012    1.8
10013}
10014
10015impl Default for EmployeeAnomalyRulesConfig {
10016    fn default() -> Self {
10017        Self {
10018            new_employee_error_rate: default_new_employee_rate(),
10019            new_employee_threshold_days: default_new_employee_threshold(),
10020            volume_fatigue_threshold: default_volume_fatigue_threshold(),
10021            coverage_error_multiplier: default_coverage_multiplier(),
10022        }
10023    }
10024}
10025
10026/// Account-specific anomaly rules configuration.
10027#[derive(Debug, Clone, Serialize, Deserialize)]
10028pub struct AccountAnomalyRulesConfig {
10029    /// Error rate multiplier for high-risk accounts.
10030    #[serde(default = "default_high_risk_multiplier")]
10031    pub high_risk_account_multiplier: f64,
10032
10033    /// Account codes considered high-risk.
10034    #[serde(default = "default_high_risk_accounts")]
10035    pub high_risk_accounts: Vec<String>,
10036
10037    /// Error rate multiplier for suspense accounts.
10038    #[serde(default = "default_suspense_multiplier")]
10039    pub suspense_account_multiplier: f64,
10040
10041    /// Account codes considered suspense accounts.
10042    #[serde(default = "default_suspense_accounts")]
10043    pub suspense_accounts: Vec<String>,
10044
10045    /// Error rate multiplier for intercompany accounts.
10046    #[serde(default = "default_intercompany_multiplier")]
10047    pub intercompany_account_multiplier: f64,
10048}
10049
10050fn default_high_risk_multiplier() -> f64 {
10051    2.0
10052}
10053fn default_high_risk_accounts() -> Vec<String> {
10054    vec![
10055        "1100".to_string(), // AR Control
10056        "2000".to_string(), // AP Control
10057        "3000".to_string(), // Cash
10058    ]
10059}
10060fn default_suspense_multiplier() -> f64 {
10061    3.0
10062}
10063fn default_suspense_accounts() -> Vec<String> {
10064    vec!["9999".to_string(), "9998".to_string()]
10065}
10066fn default_intercompany_multiplier() -> f64 {
10067    1.5
10068}
10069
10070impl Default for AccountAnomalyRulesConfig {
10071    fn default() -> Self {
10072        Self {
10073            high_risk_account_multiplier: default_high_risk_multiplier(),
10074            high_risk_accounts: default_high_risk_accounts(),
10075            suspense_account_multiplier: default_suspense_multiplier(),
10076            suspense_accounts: default_suspense_accounts(),
10077            intercompany_account_multiplier: default_intercompany_multiplier(),
10078        }
10079    }
10080}
10081
10082/// Behavioral baseline configuration.
10083#[derive(Debug, Clone, Serialize, Deserialize)]
10084pub struct BehavioralBaselineConfig {
10085    /// Enable behavioral baseline tracking.
10086    #[serde(default)]
10087    pub enabled: bool,
10088
10089    /// Number of days to build baseline from.
10090    #[serde(default = "default_baseline_period")]
10091    pub baseline_period_days: u32,
10092
10093    /// Standard deviation threshold for amount anomalies.
10094    #[serde(default = "default_deviation_threshold")]
10095    pub deviation_threshold_std: f64,
10096
10097    /// Standard deviation threshold for frequency anomalies.
10098    #[serde(default = "default_frequency_deviation")]
10099    pub frequency_deviation_threshold: f64,
10100}
10101
10102fn default_baseline_period() -> u32 {
10103    90
10104}
10105fn default_deviation_threshold() -> f64 {
10106    3.0
10107}
10108fn default_frequency_deviation() -> f64 {
10109    2.0
10110}
10111
10112impl Default for BehavioralBaselineConfig {
10113    fn default() -> Self {
10114        Self {
10115            enabled: false,
10116            baseline_period_days: default_baseline_period(),
10117            deviation_threshold_std: default_deviation_threshold(),
10118            frequency_deviation_threshold: default_frequency_deviation(),
10119        }
10120    }
10121}
10122
10123/// Enhanced labeling configuration.
10124#[derive(Debug, Clone, Serialize, Deserialize)]
10125pub struct EnhancedLabelingConfig {
10126    /// Enable severity scoring.
10127    #[serde(default = "default_true_val")]
10128    pub severity_scoring: bool,
10129
10130    /// Enable difficulty classification.
10131    #[serde(default = "default_true_val")]
10132    pub difficulty_classification: bool,
10133
10134    /// Materiality thresholds for severity classification.
10135    #[serde(default)]
10136    pub materiality_thresholds: MaterialityThresholdsConfig,
10137}
10138
10139impl Default for EnhancedLabelingConfig {
10140    fn default() -> Self {
10141        Self {
10142            severity_scoring: true,
10143            difficulty_classification: true,
10144            materiality_thresholds: MaterialityThresholdsConfig::default(),
10145        }
10146    }
10147}
10148
10149/// Materiality thresholds configuration.
10150#[derive(Debug, Clone, Serialize, Deserialize)]
10151pub struct MaterialityThresholdsConfig {
10152    /// Threshold for trivial impact (as percentage of total).
10153    #[serde(default = "default_materiality_trivial")]
10154    pub trivial: f64,
10155
10156    /// Threshold for immaterial impact.
10157    #[serde(default = "default_materiality_immaterial")]
10158    pub immaterial: f64,
10159
10160    /// Threshold for material impact.
10161    #[serde(default = "default_materiality_material")]
10162    pub material: f64,
10163
10164    /// Threshold for highly material impact.
10165    #[serde(default = "default_materiality_highly_material")]
10166    pub highly_material: f64,
10167}
10168
10169fn default_materiality_trivial() -> f64 {
10170    0.001
10171}
10172fn default_materiality_immaterial() -> f64 {
10173    0.01
10174}
10175fn default_materiality_material() -> f64 {
10176    0.05
10177}
10178fn default_materiality_highly_material() -> f64 {
10179    0.10
10180}
10181
10182impl Default for MaterialityThresholdsConfig {
10183    fn default() -> Self {
10184        Self {
10185            trivial: default_materiality_trivial(),
10186            immaterial: default_materiality_immaterial(),
10187            material: default_materiality_material(),
10188            highly_material: default_materiality_highly_material(),
10189        }
10190    }
10191}
10192
10193// =============================================================================
10194// Industry-Specific Configuration
10195// =============================================================================
10196
10197/// Industry-specific transaction and anomaly generation configuration.
10198///
10199/// This configuration enables generation of industry-authentic:
10200/// - Transaction types with appropriate terminology
10201/// - Master data (BOM, routings, clinical codes, etc.)
10202/// - Industry-specific anomaly patterns
10203/// - Regulatory framework compliance
10204#[derive(Debug, Clone, Serialize, Deserialize, Default)]
10205pub struct IndustrySpecificConfig {
10206    /// Enable industry-specific generation.
10207    #[serde(default)]
10208    pub enabled: bool,
10209
10210    /// Manufacturing industry settings.
10211    #[serde(default)]
10212    pub manufacturing: ManufacturingConfig,
10213
10214    /// Retail industry settings.
10215    #[serde(default)]
10216    pub retail: RetailConfig,
10217
10218    /// Healthcare industry settings.
10219    #[serde(default)]
10220    pub healthcare: HealthcareConfig,
10221
10222    /// Technology industry settings.
10223    #[serde(default)]
10224    pub technology: TechnologyConfig,
10225
10226    /// Financial services industry settings.
10227    #[serde(default)]
10228    pub financial_services: FinancialServicesConfig,
10229
10230    /// Professional services industry settings.
10231    #[serde(default)]
10232    pub professional_services: ProfessionalServicesConfig,
10233}
10234
10235/// Manufacturing industry configuration.
10236#[derive(Debug, Clone, Serialize, Deserialize)]
10237pub struct ManufacturingConfig {
10238    /// Enable manufacturing-specific generation.
10239    #[serde(default)]
10240    pub enabled: bool,
10241
10242    /// Bill of Materials depth (typical: 3-7).
10243    #[serde(default = "default_bom_depth")]
10244    pub bom_depth: u32,
10245
10246    /// Whether to use just-in-time inventory.
10247    #[serde(default)]
10248    pub just_in_time: bool,
10249
10250    /// Production order types to generate.
10251    #[serde(default = "default_production_order_types")]
10252    pub production_order_types: Vec<String>,
10253
10254    /// Quality framework (ISO_9001, Six_Sigma, etc.).
10255    #[serde(default)]
10256    pub quality_framework: Option<String>,
10257
10258    /// Number of supplier tiers to model (1-3).
10259    #[serde(default = "default_supplier_tiers")]
10260    pub supplier_tiers: u32,
10261
10262    /// Standard cost update frequency.
10263    #[serde(default = "default_cost_frequency")]
10264    pub standard_cost_frequency: String,
10265
10266    /// Target yield rate (0.95-0.99 typical).
10267    #[serde(default = "default_yield_rate")]
10268    pub target_yield_rate: f64,
10269
10270    /// Scrap percentage threshold for alerts.
10271    #[serde(default = "default_scrap_threshold")]
10272    pub scrap_alert_threshold: f64,
10273
10274    /// Manufacturing anomaly injection rates.
10275    #[serde(default)]
10276    pub anomaly_rates: ManufacturingAnomalyRates,
10277
10278    /// Cost accounting configuration (WIP → FG → COGS pipeline).
10279    #[serde(default)]
10280    pub cost_accounting: ManufacturingCostAccountingConfig,
10281}
10282
10283/// Configuration for manufacturing cost accounting JE generation.
10284#[derive(Debug, Clone, Serialize, Deserialize)]
10285pub struct ManufacturingCostAccountingConfig {
10286    /// Enable multi-stage cost flow (WIP → FG → COGS) instead of flat JEs.
10287    #[serde(default = "default_true")]
10288    pub enabled: bool,
10289
10290    /// Generate standard cost variance JEs.
10291    #[serde(default = "default_true")]
10292    pub variance_accounts_enabled: bool,
10293
10294    /// Generate warranty provisions from quality inspection failures.
10295    #[serde(default = "default_true")]
10296    pub warranty_provisions_enabled: bool,
10297
10298    /// Minimum defect rate (0.0-1.0) to trigger warranty provision generation.
10299    #[serde(default = "default_warranty_defect_threshold")]
10300    pub warranty_defect_threshold: f64,
10301}
10302
10303fn default_warranty_defect_threshold() -> f64 {
10304    0.01
10305}
10306
10307impl Default for ManufacturingCostAccountingConfig {
10308    fn default() -> Self {
10309        Self {
10310            enabled: true,
10311            variance_accounts_enabled: true,
10312            warranty_provisions_enabled: true,
10313            warranty_defect_threshold: 0.01,
10314        }
10315    }
10316}
10317
10318fn default_bom_depth() -> u32 {
10319    4
10320}
10321
10322fn default_production_order_types() -> Vec<String> {
10323    vec![
10324        "standard".to_string(),
10325        "rework".to_string(),
10326        "prototype".to_string(),
10327    ]
10328}
10329
10330fn default_supplier_tiers() -> u32 {
10331    2
10332}
10333
10334fn default_cost_frequency() -> String {
10335    "quarterly".to_string()
10336}
10337
10338fn default_yield_rate() -> f64 {
10339    0.97
10340}
10341
10342fn default_scrap_threshold() -> f64 {
10343    0.03
10344}
10345
10346impl Default for ManufacturingConfig {
10347    fn default() -> Self {
10348        Self {
10349            enabled: false,
10350            bom_depth: default_bom_depth(),
10351            just_in_time: false,
10352            production_order_types: default_production_order_types(),
10353            quality_framework: Some("ISO_9001".to_string()),
10354            supplier_tiers: default_supplier_tiers(),
10355            standard_cost_frequency: default_cost_frequency(),
10356            target_yield_rate: default_yield_rate(),
10357            scrap_alert_threshold: default_scrap_threshold(),
10358            anomaly_rates: ManufacturingAnomalyRates::default(),
10359            cost_accounting: ManufacturingCostAccountingConfig::default(),
10360        }
10361    }
10362}
10363
10364/// Manufacturing anomaly injection rates.
10365#[derive(Debug, Clone, Serialize, Deserialize)]
10366pub struct ManufacturingAnomalyRates {
10367    /// Yield manipulation rate.
10368    #[serde(default = "default_mfg_yield_rate")]
10369    pub yield_manipulation: f64,
10370
10371    /// Labor misallocation rate.
10372    #[serde(default = "default_mfg_labor_rate")]
10373    pub labor_misallocation: f64,
10374
10375    /// Phantom production rate.
10376    #[serde(default = "default_mfg_phantom_rate")]
10377    pub phantom_production: f64,
10378
10379    /// Standard cost manipulation rate.
10380    #[serde(default = "default_mfg_cost_rate")]
10381    pub standard_cost_manipulation: f64,
10382
10383    /// Inventory fraud rate.
10384    #[serde(default = "default_mfg_inventory_rate")]
10385    pub inventory_fraud: f64,
10386}
10387
10388fn default_mfg_yield_rate() -> f64 {
10389    0.015
10390}
10391
10392fn default_mfg_labor_rate() -> f64 {
10393    0.02
10394}
10395
10396fn default_mfg_phantom_rate() -> f64 {
10397    0.005
10398}
10399
10400fn default_mfg_cost_rate() -> f64 {
10401    0.01
10402}
10403
10404fn default_mfg_inventory_rate() -> f64 {
10405    0.008
10406}
10407
10408impl Default for ManufacturingAnomalyRates {
10409    fn default() -> Self {
10410        Self {
10411            yield_manipulation: default_mfg_yield_rate(),
10412            labor_misallocation: default_mfg_labor_rate(),
10413            phantom_production: default_mfg_phantom_rate(),
10414            standard_cost_manipulation: default_mfg_cost_rate(),
10415            inventory_fraud: default_mfg_inventory_rate(),
10416        }
10417    }
10418}
10419
10420/// Retail industry configuration.
10421#[derive(Debug, Clone, Serialize, Deserialize)]
10422pub struct RetailConfig {
10423    /// Enable retail-specific generation.
10424    #[serde(default)]
10425    pub enabled: bool,
10426
10427    /// Store type distribution.
10428    #[serde(default)]
10429    pub store_types: RetailStoreTypeConfig,
10430
10431    /// Average daily transactions per store.
10432    #[serde(default = "default_retail_daily_txns")]
10433    pub avg_daily_transactions: u32,
10434
10435    /// Enable loss prevention tracking.
10436    #[serde(default = "default_true")]
10437    pub loss_prevention: bool,
10438
10439    /// Shrinkage rate (0.01-0.03 typical).
10440    #[serde(default = "default_shrinkage_rate")]
10441    pub shrinkage_rate: f64,
10442
10443    /// Retail anomaly injection rates.
10444    #[serde(default)]
10445    pub anomaly_rates: RetailAnomalyRates,
10446}
10447
10448fn default_retail_daily_txns() -> u32 {
10449    500
10450}
10451
10452fn default_shrinkage_rate() -> f64 {
10453    0.015
10454}
10455
10456impl Default for RetailConfig {
10457    fn default() -> Self {
10458        Self {
10459            enabled: false,
10460            store_types: RetailStoreTypeConfig::default(),
10461            avg_daily_transactions: default_retail_daily_txns(),
10462            loss_prevention: true,
10463            shrinkage_rate: default_shrinkage_rate(),
10464            anomaly_rates: RetailAnomalyRates::default(),
10465        }
10466    }
10467}
10468
10469/// Retail store type distribution.
10470#[derive(Debug, Clone, Serialize, Deserialize)]
10471pub struct RetailStoreTypeConfig {
10472    /// Percentage of flagship stores.
10473    #[serde(default = "default_flagship_pct")]
10474    pub flagship: f64,
10475
10476    /// Percentage of regional stores.
10477    #[serde(default = "default_regional_pct")]
10478    pub regional: f64,
10479
10480    /// Percentage of outlet stores.
10481    #[serde(default = "default_outlet_pct")]
10482    pub outlet: f64,
10483
10484    /// Percentage of e-commerce.
10485    #[serde(default = "default_ecommerce_pct")]
10486    pub ecommerce: f64,
10487}
10488
10489fn default_flagship_pct() -> f64 {
10490    0.10
10491}
10492
10493fn default_regional_pct() -> f64 {
10494    0.50
10495}
10496
10497fn default_outlet_pct() -> f64 {
10498    0.25
10499}
10500
10501fn default_ecommerce_pct() -> f64 {
10502    0.15
10503}
10504
10505impl Default for RetailStoreTypeConfig {
10506    fn default() -> Self {
10507        Self {
10508            flagship: default_flagship_pct(),
10509            regional: default_regional_pct(),
10510            outlet: default_outlet_pct(),
10511            ecommerce: default_ecommerce_pct(),
10512        }
10513    }
10514}
10515
10516/// Retail anomaly injection rates.
10517#[derive(Debug, Clone, Serialize, Deserialize)]
10518pub struct RetailAnomalyRates {
10519    /// Sweethearting rate.
10520    #[serde(default = "default_sweethearting_rate")]
10521    pub sweethearting: f64,
10522
10523    /// Skimming rate.
10524    #[serde(default = "default_skimming_rate")]
10525    pub skimming: f64,
10526
10527    /// Refund fraud rate.
10528    #[serde(default = "default_refund_fraud_rate")]
10529    pub refund_fraud: f64,
10530
10531    /// Void abuse rate.
10532    #[serde(default = "default_void_abuse_rate")]
10533    pub void_abuse: f64,
10534
10535    /// Gift card fraud rate.
10536    #[serde(default = "default_gift_card_rate")]
10537    pub gift_card_fraud: f64,
10538
10539    /// Vendor kickback rate.
10540    #[serde(default = "default_retail_kickback_rate")]
10541    pub vendor_kickback: f64,
10542}
10543
10544fn default_sweethearting_rate() -> f64 {
10545    0.02
10546}
10547
10548fn default_skimming_rate() -> f64 {
10549    0.005
10550}
10551
10552fn default_refund_fraud_rate() -> f64 {
10553    0.015
10554}
10555
10556fn default_void_abuse_rate() -> f64 {
10557    0.01
10558}
10559
10560fn default_gift_card_rate() -> f64 {
10561    0.008
10562}
10563
10564fn default_retail_kickback_rate() -> f64 {
10565    0.003
10566}
10567
10568impl Default for RetailAnomalyRates {
10569    fn default() -> Self {
10570        Self {
10571            sweethearting: default_sweethearting_rate(),
10572            skimming: default_skimming_rate(),
10573            refund_fraud: default_refund_fraud_rate(),
10574            void_abuse: default_void_abuse_rate(),
10575            gift_card_fraud: default_gift_card_rate(),
10576            vendor_kickback: default_retail_kickback_rate(),
10577        }
10578    }
10579}
10580
10581/// Healthcare industry configuration.
10582#[derive(Debug, Clone, Serialize, Deserialize)]
10583pub struct HealthcareConfig {
10584    /// Enable healthcare-specific generation.
10585    #[serde(default)]
10586    pub enabled: bool,
10587
10588    /// Healthcare facility type.
10589    #[serde(default = "default_facility_type")]
10590    pub facility_type: String,
10591
10592    /// Payer mix distribution.
10593    #[serde(default)]
10594    pub payer_mix: HealthcarePayerMix,
10595
10596    /// Coding systems enabled.
10597    #[serde(default)]
10598    pub coding_systems: HealthcareCodingSystems,
10599
10600    /// Healthcare compliance settings.
10601    #[serde(default)]
10602    pub compliance: HealthcareComplianceConfig,
10603
10604    /// Average daily encounters.
10605    #[serde(default = "default_daily_encounters")]
10606    pub avg_daily_encounters: u32,
10607
10608    /// Average charges per encounter.
10609    #[serde(default = "default_charges_per_encounter")]
10610    pub avg_charges_per_encounter: u32,
10611
10612    /// Denial rate (0.0-1.0).
10613    #[serde(default = "default_hc_denial_rate")]
10614    pub denial_rate: f64,
10615
10616    /// Bad debt rate (0.0-1.0).
10617    #[serde(default = "default_hc_bad_debt_rate")]
10618    pub bad_debt_rate: f64,
10619
10620    /// Charity care rate (0.0-1.0).
10621    #[serde(default = "default_hc_charity_care_rate")]
10622    pub charity_care_rate: f64,
10623
10624    /// Healthcare anomaly injection rates.
10625    #[serde(default)]
10626    pub anomaly_rates: HealthcareAnomalyRates,
10627}
10628
10629fn default_facility_type() -> String {
10630    "hospital".to_string()
10631}
10632
10633fn default_daily_encounters() -> u32 {
10634    150
10635}
10636
10637fn default_charges_per_encounter() -> u32 {
10638    8
10639}
10640
10641fn default_hc_denial_rate() -> f64 {
10642    0.05
10643}
10644
10645fn default_hc_bad_debt_rate() -> f64 {
10646    0.03
10647}
10648
10649fn default_hc_charity_care_rate() -> f64 {
10650    0.02
10651}
10652
10653impl Default for HealthcareConfig {
10654    fn default() -> Self {
10655        Self {
10656            enabled: false,
10657            facility_type: default_facility_type(),
10658            payer_mix: HealthcarePayerMix::default(),
10659            coding_systems: HealthcareCodingSystems::default(),
10660            compliance: HealthcareComplianceConfig::default(),
10661            avg_daily_encounters: default_daily_encounters(),
10662            avg_charges_per_encounter: default_charges_per_encounter(),
10663            denial_rate: default_hc_denial_rate(),
10664            bad_debt_rate: default_hc_bad_debt_rate(),
10665            charity_care_rate: default_hc_charity_care_rate(),
10666            anomaly_rates: HealthcareAnomalyRates::default(),
10667        }
10668    }
10669}
10670
10671/// Healthcare payer mix distribution.
10672#[derive(Debug, Clone, Serialize, Deserialize)]
10673pub struct HealthcarePayerMix {
10674    /// Medicare percentage.
10675    #[serde(default = "default_medicare_pct")]
10676    pub medicare: f64,
10677
10678    /// Medicaid percentage.
10679    #[serde(default = "default_medicaid_pct")]
10680    pub medicaid: f64,
10681
10682    /// Commercial insurance percentage.
10683    #[serde(default = "default_commercial_pct")]
10684    pub commercial: f64,
10685
10686    /// Self-pay percentage.
10687    #[serde(default = "default_self_pay_pct")]
10688    pub self_pay: f64,
10689}
10690
10691fn default_medicare_pct() -> f64 {
10692    0.40
10693}
10694
10695fn default_medicaid_pct() -> f64 {
10696    0.20
10697}
10698
10699fn default_commercial_pct() -> f64 {
10700    0.30
10701}
10702
10703fn default_self_pay_pct() -> f64 {
10704    0.10
10705}
10706
10707impl Default for HealthcarePayerMix {
10708    fn default() -> Self {
10709        Self {
10710            medicare: default_medicare_pct(),
10711            medicaid: default_medicaid_pct(),
10712            commercial: default_commercial_pct(),
10713            self_pay: default_self_pay_pct(),
10714        }
10715    }
10716}
10717
10718/// Healthcare coding systems configuration.
10719#[derive(Debug, Clone, Serialize, Deserialize)]
10720pub struct HealthcareCodingSystems {
10721    /// Enable ICD-10 diagnosis coding.
10722    #[serde(default = "default_true")]
10723    pub icd10: bool,
10724
10725    /// Enable CPT procedure coding.
10726    #[serde(default = "default_true")]
10727    pub cpt: bool,
10728
10729    /// Enable DRG grouping.
10730    #[serde(default = "default_true")]
10731    pub drg: bool,
10732
10733    /// Enable HCPCS Level II coding.
10734    #[serde(default = "default_true")]
10735    pub hcpcs: bool,
10736
10737    /// Enable revenue codes.
10738    #[serde(default = "default_true")]
10739    pub revenue_codes: bool,
10740}
10741
10742impl Default for HealthcareCodingSystems {
10743    fn default() -> Self {
10744        Self {
10745            icd10: true,
10746            cpt: true,
10747            drg: true,
10748            hcpcs: true,
10749            revenue_codes: true,
10750        }
10751    }
10752}
10753
10754/// Healthcare compliance configuration.
10755#[derive(Debug, Clone, Serialize, Deserialize)]
10756pub struct HealthcareComplianceConfig {
10757    /// Enable HIPAA compliance.
10758    #[serde(default = "default_true")]
10759    pub hipaa: bool,
10760
10761    /// Enable Stark Law compliance.
10762    #[serde(default = "default_true")]
10763    pub stark_law: bool,
10764
10765    /// Enable Anti-Kickback Statute compliance.
10766    #[serde(default = "default_true")]
10767    pub anti_kickback: bool,
10768
10769    /// Enable False Claims Act compliance.
10770    #[serde(default = "default_true")]
10771    pub false_claims_act: bool,
10772
10773    /// Enable EMTALA compliance (for hospitals).
10774    #[serde(default = "default_true")]
10775    pub emtala: bool,
10776}
10777
10778impl Default for HealthcareComplianceConfig {
10779    fn default() -> Self {
10780        Self {
10781            hipaa: true,
10782            stark_law: true,
10783            anti_kickback: true,
10784            false_claims_act: true,
10785            emtala: true,
10786        }
10787    }
10788}
10789
10790/// Healthcare anomaly injection rates.
10791#[derive(Debug, Clone, Serialize, Deserialize)]
10792pub struct HealthcareAnomalyRates {
10793    /// Upcoding rate.
10794    #[serde(default = "default_upcoding_rate")]
10795    pub upcoding: f64,
10796
10797    /// Unbundling rate.
10798    #[serde(default = "default_unbundling_rate")]
10799    pub unbundling: f64,
10800
10801    /// Phantom billing rate.
10802    #[serde(default = "default_phantom_billing_rate")]
10803    pub phantom_billing: f64,
10804
10805    /// Kickback rate.
10806    #[serde(default = "default_healthcare_kickback_rate")]
10807    pub kickbacks: f64,
10808
10809    /// Duplicate billing rate.
10810    #[serde(default = "default_duplicate_billing_rate")]
10811    pub duplicate_billing: f64,
10812
10813    /// Medical necessity abuse rate.
10814    #[serde(default = "default_med_necessity_rate")]
10815    pub medical_necessity_abuse: f64,
10816}
10817
10818fn default_upcoding_rate() -> f64 {
10819    0.02
10820}
10821
10822fn default_unbundling_rate() -> f64 {
10823    0.015
10824}
10825
10826fn default_phantom_billing_rate() -> f64 {
10827    0.005
10828}
10829
10830fn default_healthcare_kickback_rate() -> f64 {
10831    0.003
10832}
10833
10834fn default_duplicate_billing_rate() -> f64 {
10835    0.008
10836}
10837
10838fn default_med_necessity_rate() -> f64 {
10839    0.01
10840}
10841
10842impl Default for HealthcareAnomalyRates {
10843    fn default() -> Self {
10844        Self {
10845            upcoding: default_upcoding_rate(),
10846            unbundling: default_unbundling_rate(),
10847            phantom_billing: default_phantom_billing_rate(),
10848            kickbacks: default_healthcare_kickback_rate(),
10849            duplicate_billing: default_duplicate_billing_rate(),
10850            medical_necessity_abuse: default_med_necessity_rate(),
10851        }
10852    }
10853}
10854
10855/// Technology industry configuration.
10856#[derive(Debug, Clone, Serialize, Deserialize)]
10857pub struct TechnologyConfig {
10858    /// Enable technology-specific generation.
10859    #[serde(default)]
10860    pub enabled: bool,
10861
10862    /// Revenue model type.
10863    #[serde(default = "default_revenue_model")]
10864    pub revenue_model: String,
10865
10866    /// Subscription revenue percentage (for SaaS).
10867    #[serde(default = "default_subscription_pct")]
10868    pub subscription_revenue_pct: f64,
10869
10870    /// License revenue percentage.
10871    #[serde(default = "default_license_pct")]
10872    pub license_revenue_pct: f64,
10873
10874    /// Services revenue percentage.
10875    #[serde(default = "default_services_pct")]
10876    pub services_revenue_pct: f64,
10877
10878    /// R&D capitalization settings.
10879    #[serde(default)]
10880    pub rd_capitalization: RdCapitalizationConfig,
10881
10882    /// Technology anomaly injection rates.
10883    #[serde(default)]
10884    pub anomaly_rates: TechnologyAnomalyRates,
10885}
10886
10887fn default_revenue_model() -> String {
10888    "saas".to_string()
10889}
10890
10891fn default_subscription_pct() -> f64 {
10892    0.60
10893}
10894
10895fn default_license_pct() -> f64 {
10896    0.25
10897}
10898
10899fn default_services_pct() -> f64 {
10900    0.15
10901}
10902
10903impl Default for TechnologyConfig {
10904    fn default() -> Self {
10905        Self {
10906            enabled: false,
10907            revenue_model: default_revenue_model(),
10908            subscription_revenue_pct: default_subscription_pct(),
10909            license_revenue_pct: default_license_pct(),
10910            services_revenue_pct: default_services_pct(),
10911            rd_capitalization: RdCapitalizationConfig::default(),
10912            anomaly_rates: TechnologyAnomalyRates::default(),
10913        }
10914    }
10915}
10916
10917/// R&D capitalization configuration.
10918#[derive(Debug, Clone, Serialize, Deserialize)]
10919pub struct RdCapitalizationConfig {
10920    /// Enable R&D capitalization.
10921    #[serde(default = "default_true")]
10922    pub enabled: bool,
10923
10924    /// Capitalization rate (0.0-1.0).
10925    #[serde(default = "default_cap_rate")]
10926    pub capitalization_rate: f64,
10927
10928    /// Useful life in years.
10929    #[serde(default = "default_useful_life")]
10930    pub useful_life_years: u32,
10931}
10932
10933fn default_cap_rate() -> f64 {
10934    0.30
10935}
10936
10937fn default_useful_life() -> u32 {
10938    3
10939}
10940
10941impl Default for RdCapitalizationConfig {
10942    fn default() -> Self {
10943        Self {
10944            enabled: true,
10945            capitalization_rate: default_cap_rate(),
10946            useful_life_years: default_useful_life(),
10947        }
10948    }
10949}
10950
10951/// Technology anomaly injection rates.
10952#[derive(Debug, Clone, Serialize, Deserialize)]
10953pub struct TechnologyAnomalyRates {
10954    /// Premature revenue recognition rate.
10955    #[serde(default = "default_premature_rev_rate")]
10956    pub premature_revenue: f64,
10957
10958    /// Side letter abuse rate.
10959    #[serde(default = "default_side_letter_rate")]
10960    pub side_letter_abuse: f64,
10961
10962    /// Channel stuffing rate.
10963    #[serde(default = "default_channel_stuffing_rate")]
10964    pub channel_stuffing: f64,
10965
10966    /// Improper capitalization rate.
10967    #[serde(default = "default_improper_cap_rate")]
10968    pub improper_capitalization: f64,
10969}
10970
10971fn default_premature_rev_rate() -> f64 {
10972    0.015
10973}
10974
10975fn default_side_letter_rate() -> f64 {
10976    0.008
10977}
10978
10979fn default_channel_stuffing_rate() -> f64 {
10980    0.01
10981}
10982
10983fn default_improper_cap_rate() -> f64 {
10984    0.012
10985}
10986
10987impl Default for TechnologyAnomalyRates {
10988    fn default() -> Self {
10989        Self {
10990            premature_revenue: default_premature_rev_rate(),
10991            side_letter_abuse: default_side_letter_rate(),
10992            channel_stuffing: default_channel_stuffing_rate(),
10993            improper_capitalization: default_improper_cap_rate(),
10994        }
10995    }
10996}
10997
10998/// Financial services industry configuration.
10999#[derive(Debug, Clone, Serialize, Deserialize)]
11000pub struct FinancialServicesConfig {
11001    /// Enable financial services-specific generation.
11002    #[serde(default)]
11003    pub enabled: bool,
11004
11005    /// Financial institution type.
11006    #[serde(default = "default_fi_type")]
11007    pub institution_type: String,
11008
11009    /// Regulatory framework.
11010    #[serde(default = "default_fi_regulatory")]
11011    pub regulatory_framework: String,
11012
11013    /// Financial services anomaly injection rates.
11014    #[serde(default)]
11015    pub anomaly_rates: FinancialServicesAnomalyRates,
11016}
11017
11018fn default_fi_type() -> String {
11019    "commercial_bank".to_string()
11020}
11021
11022fn default_fi_regulatory() -> String {
11023    "us_banking".to_string()
11024}
11025
11026impl Default for FinancialServicesConfig {
11027    fn default() -> Self {
11028        Self {
11029            enabled: false,
11030            institution_type: default_fi_type(),
11031            regulatory_framework: default_fi_regulatory(),
11032            anomaly_rates: FinancialServicesAnomalyRates::default(),
11033        }
11034    }
11035}
11036
11037/// Financial services anomaly injection rates.
11038#[derive(Debug, Clone, Serialize, Deserialize)]
11039pub struct FinancialServicesAnomalyRates {
11040    /// Loan fraud rate.
11041    #[serde(default = "default_loan_fraud_rate")]
11042    pub loan_fraud: f64,
11043
11044    /// Trading fraud rate.
11045    #[serde(default = "default_trading_fraud_rate")]
11046    pub trading_fraud: f64,
11047
11048    /// Insurance fraud rate.
11049    #[serde(default = "default_insurance_fraud_rate")]
11050    pub insurance_fraud: f64,
11051
11052    /// Account manipulation rate.
11053    #[serde(default = "default_account_manip_rate")]
11054    pub account_manipulation: f64,
11055}
11056
11057fn default_loan_fraud_rate() -> f64 {
11058    0.01
11059}
11060
11061fn default_trading_fraud_rate() -> f64 {
11062    0.008
11063}
11064
11065fn default_insurance_fraud_rate() -> f64 {
11066    0.012
11067}
11068
11069fn default_account_manip_rate() -> f64 {
11070    0.005
11071}
11072
11073impl Default for FinancialServicesAnomalyRates {
11074    fn default() -> Self {
11075        Self {
11076            loan_fraud: default_loan_fraud_rate(),
11077            trading_fraud: default_trading_fraud_rate(),
11078            insurance_fraud: default_insurance_fraud_rate(),
11079            account_manipulation: default_account_manip_rate(),
11080        }
11081    }
11082}
11083
11084/// Professional services industry configuration.
11085#[derive(Debug, Clone, Serialize, Deserialize)]
11086pub struct ProfessionalServicesConfig {
11087    /// Enable professional services-specific generation.
11088    #[serde(default)]
11089    pub enabled: bool,
11090
11091    /// Firm type.
11092    #[serde(default = "default_firm_type")]
11093    pub firm_type: String,
11094
11095    /// Billing model.
11096    #[serde(default = "default_billing_model")]
11097    pub billing_model: String,
11098
11099    /// Average hourly rate.
11100    #[serde(default = "default_hourly_rate")]
11101    pub avg_hourly_rate: f64,
11102
11103    /// Trust account settings (for law firms).
11104    #[serde(default)]
11105    pub trust_accounting: TrustAccountingConfig,
11106
11107    /// Professional services anomaly injection rates.
11108    #[serde(default)]
11109    pub anomaly_rates: ProfessionalServicesAnomalyRates,
11110}
11111
11112fn default_firm_type() -> String {
11113    "consulting".to_string()
11114}
11115
11116fn default_billing_model() -> String {
11117    "time_and_materials".to_string()
11118}
11119
11120fn default_hourly_rate() -> f64 {
11121    250.0
11122}
11123
11124impl Default for ProfessionalServicesConfig {
11125    fn default() -> Self {
11126        Self {
11127            enabled: false,
11128            firm_type: default_firm_type(),
11129            billing_model: default_billing_model(),
11130            avg_hourly_rate: default_hourly_rate(),
11131            trust_accounting: TrustAccountingConfig::default(),
11132            anomaly_rates: ProfessionalServicesAnomalyRates::default(),
11133        }
11134    }
11135}
11136
11137/// Trust accounting configuration for law firms.
11138#[derive(Debug, Clone, Serialize, Deserialize)]
11139pub struct TrustAccountingConfig {
11140    /// Enable trust accounting.
11141    #[serde(default)]
11142    pub enabled: bool,
11143
11144    /// Require three-way reconciliation.
11145    #[serde(default = "default_true")]
11146    pub require_three_way_reconciliation: bool,
11147}
11148
11149impl Default for TrustAccountingConfig {
11150    fn default() -> Self {
11151        Self {
11152            enabled: false,
11153            require_three_way_reconciliation: true,
11154        }
11155    }
11156}
11157
11158/// Professional services anomaly injection rates.
11159#[derive(Debug, Clone, Serialize, Deserialize)]
11160pub struct ProfessionalServicesAnomalyRates {
11161    /// Time billing fraud rate.
11162    #[serde(default = "default_time_fraud_rate")]
11163    pub time_billing_fraud: f64,
11164
11165    /// Expense report fraud rate.
11166    #[serde(default = "default_expense_fraud_rate")]
11167    pub expense_fraud: f64,
11168
11169    /// Trust misappropriation rate.
11170    #[serde(default = "default_trust_misappropriation_rate")]
11171    pub trust_misappropriation: f64,
11172}
11173
11174fn default_time_fraud_rate() -> f64 {
11175    0.02
11176}
11177
11178fn default_expense_fraud_rate() -> f64 {
11179    0.015
11180}
11181
11182fn default_trust_misappropriation_rate() -> f64 {
11183    0.003
11184}
11185
11186impl Default for ProfessionalServicesAnomalyRates {
11187    fn default() -> Self {
11188        Self {
11189            time_billing_fraud: default_time_fraud_rate(),
11190            expense_fraud: default_expense_fraud_rate(),
11191            trust_misappropriation: default_trust_misappropriation_rate(),
11192        }
11193    }
11194}
11195
11196/// Fingerprint privacy configuration for extraction and synthesis.
11197///
11198/// Controls the privacy parameters used when extracting fingerprints
11199/// from sensitive data. Supports predefined levels or custom (epsilon, delta) tuples.
11200///
11201/// ```yaml
11202/// fingerprint_privacy:
11203///   level: custom
11204///   epsilon: 0.5
11205///   delta: 1.0e-5
11206///   k_anonymity: 10
11207///   composition_method: renyi_dp
11208/// ```
11209#[derive(Debug, Clone, Serialize, Deserialize)]
11210pub struct FingerprintPrivacyConfig {
11211    /// Privacy level preset. Use "custom" for user-specified epsilon/delta.
11212    #[serde(default)]
11213    pub level: String,
11214    /// Custom epsilon value (only used when level = "custom").
11215    #[serde(default = "default_epsilon")]
11216    pub epsilon: f64,
11217    /// Custom delta value for (epsilon, delta)-DP (only used with RDP/zCDP).
11218    #[serde(default = "default_delta")]
11219    pub delta: f64,
11220    /// K-anonymity threshold.
11221    #[serde(default = "default_k_anonymity")]
11222    pub k_anonymity: u32,
11223    /// Composition method: "naive", "advanced", "renyi_dp", "zcdp".
11224    #[serde(default)]
11225    pub composition_method: String,
11226}
11227
11228fn default_epsilon() -> f64 {
11229    1.0
11230}
11231
11232fn default_delta() -> f64 {
11233    1e-5
11234}
11235
11236fn default_k_anonymity() -> u32 {
11237    5
11238}
11239
11240impl Default for FingerprintPrivacyConfig {
11241    fn default() -> Self {
11242        Self {
11243            level: "standard".to_string(),
11244            epsilon: default_epsilon(),
11245            delta: default_delta(),
11246            k_anonymity: default_k_anonymity(),
11247            composition_method: "naive".to_string(),
11248        }
11249    }
11250}
11251
11252/// Quality gates configuration for pass/fail thresholds on generation runs.
11253///
11254/// ```yaml
11255/// quality_gates:
11256///   enabled: true
11257///   profile: strict  # strict, default, lenient, custom
11258///   fail_on_violation: true
11259///   custom_gates:
11260///     - name: benford_compliance
11261///       metric: benford_mad
11262///       threshold: 0.015
11263///       comparison: lte
11264/// ```
11265#[derive(Debug, Clone, Serialize, Deserialize)]
11266pub struct QualityGatesSchemaConfig {
11267    /// Enable quality gate evaluation.
11268    #[serde(default)]
11269    pub enabled: bool,
11270    /// Gate profile: "strict", "default", "lenient", or "custom".
11271    #[serde(default = "default_gate_profile_name")]
11272    pub profile: String,
11273    /// Whether to fail the generation on gate violations.
11274    #[serde(default)]
11275    pub fail_on_violation: bool,
11276    /// Custom gate definitions (used when profile = "custom").
11277    #[serde(default)]
11278    pub custom_gates: Vec<QualityGateEntry>,
11279}
11280
11281fn default_gate_profile_name() -> String {
11282    "default".to_string()
11283}
11284
11285impl Default for QualityGatesSchemaConfig {
11286    fn default() -> Self {
11287        Self {
11288            enabled: false,
11289            profile: default_gate_profile_name(),
11290            fail_on_violation: false,
11291            custom_gates: Vec::new(),
11292        }
11293    }
11294}
11295
11296/// A single quality gate entry in configuration.
11297#[derive(Debug, Clone, Serialize, Deserialize)]
11298pub struct QualityGateEntry {
11299    /// Gate name.
11300    pub name: String,
11301    /// Metric to check: benford_mad, balance_coherence, document_chain_integrity,
11302    /// correlation_preservation, temporal_consistency, privacy_mia_auc,
11303    /// completion_rate, duplicate_rate, referential_integrity, ic_match_rate.
11304    pub metric: String,
11305    /// Threshold value.
11306    pub threshold: f64,
11307    /// Upper threshold for "between" comparison.
11308    #[serde(default)]
11309    pub upper_threshold: Option<f64>,
11310    /// Comparison operator: "gte", "lte", "eq", "between".
11311    #[serde(default = "default_gate_comparison")]
11312    pub comparison: String,
11313}
11314
11315fn default_gate_comparison() -> String {
11316    "gte".to_string()
11317}
11318
11319/// Compliance configuration for regulatory requirements.
11320///
11321/// ```yaml
11322/// compliance:
11323///   content_marking:
11324///     enabled: true
11325///     format: embedded  # embedded, sidecar, both
11326///   article10_report: true
11327/// ```
11328#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11329pub struct ComplianceSchemaConfig {
11330    /// Synthetic content marking configuration (EU AI Act Article 50).
11331    #[serde(default)]
11332    pub content_marking: ContentMarkingSchemaConfig,
11333    /// Generate Article 10 data governance report.
11334    #[serde(default)]
11335    pub article10_report: bool,
11336    /// Certificate configuration for proving DP guarantees.
11337    #[serde(default)]
11338    pub certificates: CertificateSchemaConfig,
11339}
11340
11341/// Configuration for synthetic data certificates.
11342#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11343pub struct CertificateSchemaConfig {
11344    /// Whether certificate generation is enabled.
11345    #[serde(default)]
11346    pub enabled: bool,
11347    /// Environment variable name for the signing key.
11348    #[serde(default)]
11349    pub signing_key_env: Option<String>,
11350    /// Whether to include quality metrics in the certificate.
11351    #[serde(default)]
11352    pub include_quality_metrics: bool,
11353}
11354
11355/// Content marking configuration for synthetic data output.
11356#[derive(Debug, Clone, Serialize, Deserialize)]
11357pub struct ContentMarkingSchemaConfig {
11358    /// Whether content marking is enabled.
11359    #[serde(default = "default_true")]
11360    pub enabled: bool,
11361    /// Marking format: "embedded", "sidecar", or "both".
11362    #[serde(default = "default_marking_format")]
11363    pub format: String,
11364}
11365
11366fn default_marking_format() -> String {
11367    "embedded".to_string()
11368}
11369
11370impl Default for ContentMarkingSchemaConfig {
11371    fn default() -> Self {
11372        Self {
11373            enabled: true,
11374            format: default_marking_format(),
11375        }
11376    }
11377}
11378
11379/// Webhook notification configuration.
11380#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11381pub struct WebhookSchemaConfig {
11382    /// Whether webhooks are enabled.
11383    #[serde(default)]
11384    pub enabled: bool,
11385    /// Webhook endpoint configurations.
11386    #[serde(default)]
11387    pub endpoints: Vec<WebhookEndpointConfig>,
11388}
11389
11390/// Configuration for a single webhook endpoint.
11391#[derive(Debug, Clone, Serialize, Deserialize)]
11392pub struct WebhookEndpointConfig {
11393    /// Target URL for the webhook.
11394    pub url: String,
11395    /// Event types this endpoint subscribes to.
11396    #[serde(default)]
11397    pub events: Vec<String>,
11398    /// Optional secret for HMAC-SHA256 signature.
11399    #[serde(default)]
11400    pub secret: Option<String>,
11401    /// Maximum retry attempts (default: 3).
11402    #[serde(default = "default_webhook_retries")]
11403    pub max_retries: u32,
11404    /// Timeout in seconds (default: 10).
11405    #[serde(default = "default_webhook_timeout")]
11406    pub timeout_secs: u64,
11407}
11408
11409fn default_webhook_retries() -> u32 {
11410    3
11411}
11412fn default_webhook_timeout() -> u64 {
11413    10
11414}
11415
11416// ===== Enterprise Process Chain Config Structs =====
11417
11418// ----- Source-to-Pay (S2C/S2P) -----
11419
11420/// Source-to-Pay configuration covering the entire sourcing lifecycle.
11421#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11422pub struct SourceToPayConfig {
11423    /// Enable source-to-pay generation
11424    #[serde(default)]
11425    pub enabled: bool,
11426    /// Spend analysis configuration
11427    #[serde(default)]
11428    pub spend_analysis: SpendAnalysisConfig,
11429    /// Sourcing project configuration
11430    #[serde(default)]
11431    pub sourcing: SourcingConfig,
11432    /// Supplier qualification configuration
11433    #[serde(default)]
11434    pub qualification: QualificationConfig,
11435    /// RFx event configuration
11436    #[serde(default)]
11437    pub rfx: RfxConfig,
11438    /// Contract configuration
11439    #[serde(default)]
11440    pub contracts: ContractConfig,
11441    /// Catalog configuration
11442    #[serde(default)]
11443    pub catalog: CatalogConfig,
11444    /// Scorecard configuration
11445    #[serde(default)]
11446    pub scorecards: ScorecardConfig,
11447    /// P2P integration settings
11448    #[serde(default)]
11449    pub p2p_integration: P2PIntegrationConfig,
11450}
11451
11452/// Spend analysis configuration.
11453#[derive(Debug, Clone, Serialize, Deserialize)]
11454pub struct SpendAnalysisConfig {
11455    /// HHI threshold for triggering sourcing project
11456    #[serde(default = "default_hhi_threshold")]
11457    pub hhi_threshold: f64,
11458    /// Target spend coverage under contracts
11459    #[serde(default = "default_contract_coverage_target")]
11460    pub contract_coverage_target: f64,
11461}
11462
11463impl Default for SpendAnalysisConfig {
11464    fn default() -> Self {
11465        Self {
11466            hhi_threshold: default_hhi_threshold(),
11467            contract_coverage_target: default_contract_coverage_target(),
11468        }
11469    }
11470}
11471
11472fn default_hhi_threshold() -> f64 {
11473    2500.0
11474}
11475fn default_contract_coverage_target() -> f64 {
11476    0.80
11477}
11478
11479/// Sourcing project configuration.
11480#[derive(Debug, Clone, Serialize, Deserialize)]
11481pub struct SourcingConfig {
11482    /// Number of sourcing projects per year
11483    #[serde(default = "default_sourcing_projects_per_year")]
11484    pub projects_per_year: u32,
11485    /// Months before expiry to trigger renewal project
11486    #[serde(default = "default_renewal_horizon_months")]
11487    pub renewal_horizon_months: u32,
11488    /// Average project duration in months
11489    #[serde(default = "default_project_duration_months")]
11490    pub project_duration_months: u32,
11491}
11492
11493impl Default for SourcingConfig {
11494    fn default() -> Self {
11495        Self {
11496            projects_per_year: default_sourcing_projects_per_year(),
11497            renewal_horizon_months: default_renewal_horizon_months(),
11498            project_duration_months: default_project_duration_months(),
11499        }
11500    }
11501}
11502
11503fn default_sourcing_projects_per_year() -> u32 {
11504    10
11505}
11506fn default_renewal_horizon_months() -> u32 {
11507    3
11508}
11509fn default_project_duration_months() -> u32 {
11510    4
11511}
11512
11513/// Supplier qualification configuration.
11514#[derive(Debug, Clone, Serialize, Deserialize)]
11515pub struct QualificationConfig {
11516    /// Pass rate for qualification
11517    #[serde(default = "default_qualification_pass_rate")]
11518    pub pass_rate: f64,
11519    /// Qualification validity in days
11520    #[serde(default = "default_qualification_validity_days")]
11521    pub validity_days: u32,
11522    /// Financial stability weight
11523    #[serde(default = "default_financial_weight")]
11524    pub financial_weight: f64,
11525    /// Quality management weight
11526    #[serde(default = "default_quality_weight")]
11527    pub quality_weight: f64,
11528    /// Delivery performance weight
11529    #[serde(default = "default_delivery_weight")]
11530    pub delivery_weight: f64,
11531    /// Compliance weight
11532    #[serde(default = "default_compliance_weight")]
11533    pub compliance_weight: f64,
11534}
11535
11536impl Default for QualificationConfig {
11537    fn default() -> Self {
11538        Self {
11539            pass_rate: default_qualification_pass_rate(),
11540            validity_days: default_qualification_validity_days(),
11541            financial_weight: default_financial_weight(),
11542            quality_weight: default_quality_weight(),
11543            delivery_weight: default_delivery_weight(),
11544            compliance_weight: default_compliance_weight(),
11545        }
11546    }
11547}
11548
11549fn default_qualification_pass_rate() -> f64 {
11550    0.75
11551}
11552fn default_qualification_validity_days() -> u32 {
11553    365
11554}
11555fn default_financial_weight() -> f64 {
11556    0.25
11557}
11558fn default_quality_weight() -> f64 {
11559    0.30
11560}
11561fn default_delivery_weight() -> f64 {
11562    0.25
11563}
11564fn default_compliance_weight() -> f64 {
11565    0.20
11566}
11567
11568/// RFx event configuration.
11569#[derive(Debug, Clone, Serialize, Deserialize)]
11570pub struct RfxConfig {
11571    /// Spend threshold above which RFI is required before RFP
11572    #[serde(default = "default_rfi_threshold")]
11573    pub rfi_threshold: f64,
11574    /// Minimum vendors invited per RFx
11575    #[serde(default = "default_min_invited_vendors")]
11576    pub min_invited_vendors: u32,
11577    /// Maximum vendors invited per RFx
11578    #[serde(default = "default_max_invited_vendors")]
11579    pub max_invited_vendors: u32,
11580    /// Response rate (% of invited vendors that submit bids)
11581    #[serde(default = "default_response_rate")]
11582    pub response_rate: f64,
11583    /// Default price weight in evaluation
11584    #[serde(default = "default_price_weight")]
11585    pub default_price_weight: f64,
11586    /// Default quality weight in evaluation
11587    #[serde(default = "default_rfx_quality_weight")]
11588    pub default_quality_weight: f64,
11589    /// Default delivery weight in evaluation
11590    #[serde(default = "default_rfx_delivery_weight")]
11591    pub default_delivery_weight: f64,
11592}
11593
11594impl Default for RfxConfig {
11595    fn default() -> Self {
11596        Self {
11597            rfi_threshold: default_rfi_threshold(),
11598            min_invited_vendors: default_min_invited_vendors(),
11599            max_invited_vendors: default_max_invited_vendors(),
11600            response_rate: default_response_rate(),
11601            default_price_weight: default_price_weight(),
11602            default_quality_weight: default_rfx_quality_weight(),
11603            default_delivery_weight: default_rfx_delivery_weight(),
11604        }
11605    }
11606}
11607
11608fn default_rfi_threshold() -> f64 {
11609    100_000.0
11610}
11611fn default_min_invited_vendors() -> u32 {
11612    3
11613}
11614fn default_max_invited_vendors() -> u32 {
11615    8
11616}
11617fn default_response_rate() -> f64 {
11618    0.70
11619}
11620fn default_price_weight() -> f64 {
11621    0.40
11622}
11623fn default_rfx_quality_weight() -> f64 {
11624    0.35
11625}
11626fn default_rfx_delivery_weight() -> f64 {
11627    0.25
11628}
11629
11630/// Contract configuration.
11631#[derive(Debug, Clone, Serialize, Deserialize)]
11632pub struct ContractConfig {
11633    /// Minimum contract duration in months
11634    #[serde(default = "default_min_contract_months")]
11635    pub min_duration_months: u32,
11636    /// Maximum contract duration in months
11637    #[serde(default = "default_max_contract_months")]
11638    pub max_duration_months: u32,
11639    /// Auto-renewal rate
11640    #[serde(default = "default_auto_renewal_rate")]
11641    pub auto_renewal_rate: f64,
11642    /// Amendment rate (% of contracts with at least one amendment)
11643    #[serde(default = "default_amendment_rate")]
11644    pub amendment_rate: f64,
11645    /// Distribution of contract types
11646    #[serde(default)]
11647    pub type_distribution: ContractTypeDistribution,
11648}
11649
11650impl Default for ContractConfig {
11651    fn default() -> Self {
11652        Self {
11653            min_duration_months: default_min_contract_months(),
11654            max_duration_months: default_max_contract_months(),
11655            auto_renewal_rate: default_auto_renewal_rate(),
11656            amendment_rate: default_amendment_rate(),
11657            type_distribution: ContractTypeDistribution::default(),
11658        }
11659    }
11660}
11661
11662fn default_min_contract_months() -> u32 {
11663    12
11664}
11665fn default_max_contract_months() -> u32 {
11666    36
11667}
11668fn default_auto_renewal_rate() -> f64 {
11669    0.40
11670}
11671fn default_amendment_rate() -> f64 {
11672    0.20
11673}
11674
11675/// Distribution of contract types.
11676#[derive(Debug, Clone, Serialize, Deserialize)]
11677pub struct ContractTypeDistribution {
11678    /// Fixed price percentage
11679    #[serde(default = "default_fixed_price_pct")]
11680    pub fixed_price: f64,
11681    /// Blanket/framework percentage
11682    #[serde(default = "default_blanket_pct")]
11683    pub blanket: f64,
11684    /// Time and materials percentage
11685    #[serde(default = "default_time_materials_pct")]
11686    pub time_and_materials: f64,
11687    /// Service agreement percentage
11688    #[serde(default = "default_service_agreement_pct")]
11689    pub service_agreement: f64,
11690}
11691
11692impl Default for ContractTypeDistribution {
11693    fn default() -> Self {
11694        Self {
11695            fixed_price: default_fixed_price_pct(),
11696            blanket: default_blanket_pct(),
11697            time_and_materials: default_time_materials_pct(),
11698            service_agreement: default_service_agreement_pct(),
11699        }
11700    }
11701}
11702
11703fn default_fixed_price_pct() -> f64 {
11704    0.40
11705}
11706fn default_blanket_pct() -> f64 {
11707    0.30
11708}
11709fn default_time_materials_pct() -> f64 {
11710    0.15
11711}
11712fn default_service_agreement_pct() -> f64 {
11713    0.15
11714}
11715
11716/// Catalog configuration.
11717#[derive(Debug, Clone, Serialize, Deserialize)]
11718pub struct CatalogConfig {
11719    /// Percentage of catalog items marked as preferred
11720    #[serde(default = "default_preferred_vendor_flag_rate")]
11721    pub preferred_vendor_flag_rate: f64,
11722    /// Rate of materials with multiple sources in catalog
11723    #[serde(default = "default_multi_source_rate")]
11724    pub multi_source_rate: f64,
11725}
11726
11727impl Default for CatalogConfig {
11728    fn default() -> Self {
11729        Self {
11730            preferred_vendor_flag_rate: default_preferred_vendor_flag_rate(),
11731            multi_source_rate: default_multi_source_rate(),
11732        }
11733    }
11734}
11735
11736fn default_preferred_vendor_flag_rate() -> f64 {
11737    0.70
11738}
11739fn default_multi_source_rate() -> f64 {
11740    0.25
11741}
11742
11743/// Scorecard configuration.
11744#[derive(Debug, Clone, Serialize, Deserialize)]
11745pub struct ScorecardConfig {
11746    /// Scorecard review frequency (quarterly, monthly)
11747    #[serde(default = "default_scorecard_frequency")]
11748    pub frequency: String,
11749    /// On-time delivery weight in overall score
11750    #[serde(default = "default_otd_weight")]
11751    pub on_time_delivery_weight: f64,
11752    /// Quality weight in overall score
11753    #[serde(default = "default_quality_score_weight")]
11754    pub quality_weight: f64,
11755    /// Price competitiveness weight
11756    #[serde(default = "default_price_score_weight")]
11757    pub price_weight: f64,
11758    /// Responsiveness weight
11759    #[serde(default = "default_responsiveness_weight")]
11760    pub responsiveness_weight: f64,
11761    /// Grade A threshold (score >= this)
11762    #[serde(default = "default_grade_a_threshold")]
11763    pub grade_a_threshold: f64,
11764    /// Grade B threshold
11765    #[serde(default = "default_grade_b_threshold")]
11766    pub grade_b_threshold: f64,
11767    /// Grade C threshold
11768    #[serde(default = "default_grade_c_threshold")]
11769    pub grade_c_threshold: f64,
11770}
11771
11772impl Default for ScorecardConfig {
11773    fn default() -> Self {
11774        Self {
11775            frequency: default_scorecard_frequency(),
11776            on_time_delivery_weight: default_otd_weight(),
11777            quality_weight: default_quality_score_weight(),
11778            price_weight: default_price_score_weight(),
11779            responsiveness_weight: default_responsiveness_weight(),
11780            grade_a_threshold: default_grade_a_threshold(),
11781            grade_b_threshold: default_grade_b_threshold(),
11782            grade_c_threshold: default_grade_c_threshold(),
11783        }
11784    }
11785}
11786
11787fn default_scorecard_frequency() -> String {
11788    "quarterly".to_string()
11789}
11790fn default_otd_weight() -> f64 {
11791    0.30
11792}
11793fn default_quality_score_weight() -> f64 {
11794    0.30
11795}
11796fn default_price_score_weight() -> f64 {
11797    0.25
11798}
11799fn default_responsiveness_weight() -> f64 {
11800    0.15
11801}
11802fn default_grade_a_threshold() -> f64 {
11803    90.0
11804}
11805fn default_grade_b_threshold() -> f64 {
11806    75.0
11807}
11808fn default_grade_c_threshold() -> f64 {
11809    60.0
11810}
11811
11812/// P2P integration settings for contract enforcement.
11813#[derive(Debug, Clone, Serialize, Deserialize)]
11814pub struct P2PIntegrationConfig {
11815    /// Rate of off-contract (maverick) purchases
11816    #[serde(default = "default_off_contract_rate")]
11817    pub off_contract_rate: f64,
11818    /// Price tolerance for contract price validation
11819    #[serde(default = "default_price_tolerance")]
11820    pub price_tolerance: f64,
11821    /// Whether to enforce catalog ordering
11822    #[serde(default)]
11823    pub catalog_enforcement: bool,
11824}
11825
11826impl Default for P2PIntegrationConfig {
11827    fn default() -> Self {
11828        Self {
11829            off_contract_rate: default_off_contract_rate(),
11830            price_tolerance: default_price_tolerance(),
11831            catalog_enforcement: false,
11832        }
11833    }
11834}
11835
11836fn default_off_contract_rate() -> f64 {
11837    0.15
11838}
11839fn default_price_tolerance() -> f64 {
11840    0.02
11841}
11842
11843// ----- Financial Reporting -----
11844
11845/// Financial reporting configuration.
11846#[derive(Debug, Clone, Serialize, Deserialize)]
11847pub struct FinancialReportingConfig {
11848    /// Enable financial reporting generation
11849    #[serde(default)]
11850    pub enabled: bool,
11851    /// Generate balance sheet
11852    #[serde(default = "default_true")]
11853    pub generate_balance_sheet: bool,
11854    /// Generate income statement
11855    #[serde(default = "default_true")]
11856    pub generate_income_statement: bool,
11857    /// Generate cash flow statement
11858    #[serde(default = "default_true")]
11859    pub generate_cash_flow: bool,
11860    /// Generate changes in equity statement
11861    #[serde(default = "default_true")]
11862    pub generate_changes_in_equity: bool,
11863    /// Number of comparative periods
11864    #[serde(default = "default_comparative_periods")]
11865    pub comparative_periods: u32,
11866    /// Management KPIs configuration
11867    #[serde(default)]
11868    pub management_kpis: ManagementKpisConfig,
11869    /// Budget configuration
11870    #[serde(default)]
11871    pub budgets: BudgetConfig,
11872}
11873
11874impl Default for FinancialReportingConfig {
11875    fn default() -> Self {
11876        Self {
11877            enabled: false,
11878            generate_balance_sheet: true,
11879            generate_income_statement: true,
11880            generate_cash_flow: true,
11881            generate_changes_in_equity: true,
11882            comparative_periods: default_comparative_periods(),
11883            management_kpis: ManagementKpisConfig::default(),
11884            budgets: BudgetConfig::default(),
11885        }
11886    }
11887}
11888
11889fn default_comparative_periods() -> u32 {
11890    1
11891}
11892
11893/// Management KPIs configuration.
11894#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11895pub struct ManagementKpisConfig {
11896    /// Enable KPI generation
11897    #[serde(default)]
11898    pub enabled: bool,
11899    /// KPI calculation frequency (monthly, quarterly)
11900    #[serde(default = "default_kpi_frequency")]
11901    pub frequency: String,
11902}
11903
11904fn default_kpi_frequency() -> String {
11905    "monthly".to_string()
11906}
11907
11908/// Budget configuration.
11909#[derive(Debug, Clone, Serialize, Deserialize)]
11910pub struct BudgetConfig {
11911    /// Enable budget generation
11912    #[serde(default)]
11913    pub enabled: bool,
11914    /// Expected revenue growth rate for budgeting
11915    #[serde(default = "default_revenue_growth_rate")]
11916    pub revenue_growth_rate: f64,
11917    /// Expected expense inflation rate
11918    #[serde(default = "default_expense_inflation_rate")]
11919    pub expense_inflation_rate: f64,
11920    /// Random noise to add to budget vs actual
11921    #[serde(default = "default_variance_noise")]
11922    pub variance_noise: f64,
11923}
11924
11925impl Default for BudgetConfig {
11926    fn default() -> Self {
11927        Self {
11928            enabled: false,
11929            revenue_growth_rate: default_revenue_growth_rate(),
11930            expense_inflation_rate: default_expense_inflation_rate(),
11931            variance_noise: default_variance_noise(),
11932        }
11933    }
11934}
11935
11936fn default_revenue_growth_rate() -> f64 {
11937    0.05
11938}
11939fn default_expense_inflation_rate() -> f64 {
11940    0.03
11941}
11942fn default_variance_noise() -> f64 {
11943    0.10
11944}
11945
11946// ----- HR Configuration -----
11947
11948/// HR (Hire-to-Retire) process configuration.
11949#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11950pub struct HrConfig {
11951    /// Enable HR generation
11952    #[serde(default)]
11953    pub enabled: bool,
11954    /// Payroll configuration
11955    #[serde(default)]
11956    pub payroll: PayrollConfig,
11957    /// Time and attendance configuration
11958    #[serde(default)]
11959    pub time_attendance: TimeAttendanceConfig,
11960    /// Expense management configuration
11961    #[serde(default)]
11962    pub expenses: ExpenseConfig,
11963}
11964
11965/// Payroll configuration.
11966#[derive(Debug, Clone, Serialize, Deserialize)]
11967pub struct PayrollConfig {
11968    /// Enable payroll generation
11969    #[serde(default = "default_true")]
11970    pub enabled: bool,
11971    /// Pay frequency (monthly, biweekly, weekly)
11972    #[serde(default = "default_pay_frequency")]
11973    pub pay_frequency: String,
11974    /// Salary ranges by job level
11975    #[serde(default)]
11976    pub salary_ranges: PayrollSalaryRanges,
11977    /// Effective tax rates
11978    #[serde(default)]
11979    pub tax_rates: PayrollTaxRates,
11980    /// Benefits enrollment rate
11981    #[serde(default = "default_benefits_enrollment_rate")]
11982    pub benefits_enrollment_rate: f64,
11983    /// Retirement plan participation rate
11984    #[serde(default = "default_retirement_participation_rate")]
11985    pub retirement_participation_rate: f64,
11986}
11987
11988impl Default for PayrollConfig {
11989    fn default() -> Self {
11990        Self {
11991            enabled: true,
11992            pay_frequency: default_pay_frequency(),
11993            salary_ranges: PayrollSalaryRanges::default(),
11994            tax_rates: PayrollTaxRates::default(),
11995            benefits_enrollment_rate: default_benefits_enrollment_rate(),
11996            retirement_participation_rate: default_retirement_participation_rate(),
11997        }
11998    }
11999}
12000
12001fn default_pay_frequency() -> String {
12002    "monthly".to_string()
12003}
12004fn default_benefits_enrollment_rate() -> f64 {
12005    0.60
12006}
12007fn default_retirement_participation_rate() -> f64 {
12008    0.45
12009}
12010
12011/// Salary ranges by job level.
12012#[derive(Debug, Clone, Serialize, Deserialize)]
12013pub struct PayrollSalaryRanges {
12014    /// Staff level min/max
12015    #[serde(default = "default_staff_min")]
12016    pub staff_min: f64,
12017    #[serde(default = "default_staff_max")]
12018    pub staff_max: f64,
12019    /// Manager level min/max
12020    #[serde(default = "default_manager_min")]
12021    pub manager_min: f64,
12022    #[serde(default = "default_manager_max")]
12023    pub manager_max: f64,
12024    /// Director level min/max
12025    #[serde(default = "default_director_min")]
12026    pub director_min: f64,
12027    #[serde(default = "default_director_max")]
12028    pub director_max: f64,
12029    /// Executive level min/max
12030    #[serde(default = "default_executive_min")]
12031    pub executive_min: f64,
12032    #[serde(default = "default_executive_max")]
12033    pub executive_max: f64,
12034}
12035
12036impl Default for PayrollSalaryRanges {
12037    fn default() -> Self {
12038        Self {
12039            staff_min: default_staff_min(),
12040            staff_max: default_staff_max(),
12041            manager_min: default_manager_min(),
12042            manager_max: default_manager_max(),
12043            director_min: default_director_min(),
12044            director_max: default_director_max(),
12045            executive_min: default_executive_min(),
12046            executive_max: default_executive_max(),
12047        }
12048    }
12049}
12050
12051fn default_staff_min() -> f64 {
12052    50_000.0
12053}
12054fn default_staff_max() -> f64 {
12055    70_000.0
12056}
12057fn default_manager_min() -> f64 {
12058    80_000.0
12059}
12060fn default_manager_max() -> f64 {
12061    120_000.0
12062}
12063fn default_director_min() -> f64 {
12064    120_000.0
12065}
12066fn default_director_max() -> f64 {
12067    180_000.0
12068}
12069fn default_executive_min() -> f64 {
12070    180_000.0
12071}
12072fn default_executive_max() -> f64 {
12073    350_000.0
12074}
12075
12076/// Effective tax rates for payroll.
12077#[derive(Debug, Clone, Serialize, Deserialize)]
12078pub struct PayrollTaxRates {
12079    /// Federal effective tax rate
12080    #[serde(default = "default_federal_rate")]
12081    pub federal_effective: f64,
12082    /// State effective tax rate
12083    #[serde(default = "default_state_rate")]
12084    pub state_effective: f64,
12085    /// FICA/social security rate
12086    #[serde(default = "default_fica_rate")]
12087    pub fica: f64,
12088}
12089
12090impl Default for PayrollTaxRates {
12091    fn default() -> Self {
12092        Self {
12093            federal_effective: default_federal_rate(),
12094            state_effective: default_state_rate(),
12095            fica: default_fica_rate(),
12096        }
12097    }
12098}
12099
12100fn default_federal_rate() -> f64 {
12101    0.22
12102}
12103fn default_state_rate() -> f64 {
12104    0.05
12105}
12106fn default_fica_rate() -> f64 {
12107    0.0765
12108}
12109
12110/// Time and attendance configuration.
12111#[derive(Debug, Clone, Serialize, Deserialize)]
12112pub struct TimeAttendanceConfig {
12113    /// Enable time tracking
12114    #[serde(default = "default_true")]
12115    pub enabled: bool,
12116    /// Overtime rate (% of employees with overtime in a period)
12117    #[serde(default = "default_overtime_rate")]
12118    pub overtime_rate: f64,
12119}
12120
12121impl Default for TimeAttendanceConfig {
12122    fn default() -> Self {
12123        Self {
12124            enabled: true,
12125            overtime_rate: default_overtime_rate(),
12126        }
12127    }
12128}
12129
12130fn default_overtime_rate() -> f64 {
12131    0.10
12132}
12133
12134/// Expense management configuration.
12135#[derive(Debug, Clone, Serialize, Deserialize)]
12136pub struct ExpenseConfig {
12137    /// Enable expense report generation
12138    #[serde(default = "default_true")]
12139    pub enabled: bool,
12140    /// Rate of employees submitting expenses per month
12141    #[serde(default = "default_expense_submission_rate")]
12142    pub submission_rate: f64,
12143    /// Rate of policy violations
12144    #[serde(default = "default_policy_violation_rate")]
12145    pub policy_violation_rate: f64,
12146}
12147
12148impl Default for ExpenseConfig {
12149    fn default() -> Self {
12150        Self {
12151            enabled: true,
12152            submission_rate: default_expense_submission_rate(),
12153            policy_violation_rate: default_policy_violation_rate(),
12154        }
12155    }
12156}
12157
12158fn default_expense_submission_rate() -> f64 {
12159    0.30
12160}
12161fn default_policy_violation_rate() -> f64 {
12162    0.08
12163}
12164
12165// ----- Manufacturing Configuration -----
12166
12167/// Manufacturing process configuration (production orders, WIP, routing).
12168#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12169pub struct ManufacturingProcessConfig {
12170    /// Enable manufacturing generation
12171    #[serde(default)]
12172    pub enabled: bool,
12173    /// Production order configuration
12174    #[serde(default)]
12175    pub production_orders: ProductionOrderConfig,
12176    /// Costing configuration
12177    #[serde(default)]
12178    pub costing: ManufacturingCostingConfig,
12179    /// Routing configuration
12180    #[serde(default)]
12181    pub routing: RoutingConfig,
12182}
12183
12184/// Production order configuration.
12185#[derive(Debug, Clone, Serialize, Deserialize)]
12186pub struct ProductionOrderConfig {
12187    /// Orders per month
12188    #[serde(default = "default_prod_orders_per_month")]
12189    pub orders_per_month: u32,
12190    /// Average batch size
12191    #[serde(default = "default_prod_avg_batch_size")]
12192    pub avg_batch_size: u32,
12193    /// Yield rate
12194    #[serde(default = "default_prod_yield_rate")]
12195    pub yield_rate: f64,
12196    /// Make-to-order rate (vs make-to-stock)
12197    #[serde(default = "default_prod_make_to_order_rate")]
12198    pub make_to_order_rate: f64,
12199    /// Rework rate
12200    #[serde(default = "default_prod_rework_rate")]
12201    pub rework_rate: f64,
12202}
12203
12204impl Default for ProductionOrderConfig {
12205    fn default() -> Self {
12206        Self {
12207            orders_per_month: default_prod_orders_per_month(),
12208            avg_batch_size: default_prod_avg_batch_size(),
12209            yield_rate: default_prod_yield_rate(),
12210            make_to_order_rate: default_prod_make_to_order_rate(),
12211            rework_rate: default_prod_rework_rate(),
12212        }
12213    }
12214}
12215
12216fn default_prod_orders_per_month() -> u32 {
12217    50
12218}
12219fn default_prod_avg_batch_size() -> u32 {
12220    100
12221}
12222fn default_prod_yield_rate() -> f64 {
12223    0.97
12224}
12225fn default_prod_make_to_order_rate() -> f64 {
12226    0.20
12227}
12228fn default_prod_rework_rate() -> f64 {
12229    0.03
12230}
12231
12232/// Manufacturing costing configuration.
12233#[derive(Debug, Clone, Serialize, Deserialize)]
12234pub struct ManufacturingCostingConfig {
12235    /// Labor rate per hour
12236    #[serde(default = "default_labor_rate")]
12237    pub labor_rate_per_hour: f64,
12238    /// Overhead application rate (multiplier on direct labor)
12239    #[serde(default = "default_overhead_rate")]
12240    pub overhead_rate: f64,
12241    /// Standard cost update frequency
12242    #[serde(default = "default_cost_update_frequency")]
12243    pub standard_cost_update_frequency: String,
12244}
12245
12246impl Default for ManufacturingCostingConfig {
12247    fn default() -> Self {
12248        Self {
12249            labor_rate_per_hour: default_labor_rate(),
12250            overhead_rate: default_overhead_rate(),
12251            standard_cost_update_frequency: default_cost_update_frequency(),
12252        }
12253    }
12254}
12255
12256fn default_labor_rate() -> f64 {
12257    35.0
12258}
12259fn default_overhead_rate() -> f64 {
12260    1.50
12261}
12262fn default_cost_update_frequency() -> String {
12263    "quarterly".to_string()
12264}
12265
12266/// Routing configuration for production operations.
12267#[derive(Debug, Clone, Serialize, Deserialize)]
12268pub struct RoutingConfig {
12269    /// Average number of operations per routing
12270    #[serde(default = "default_avg_operations")]
12271    pub avg_operations: u32,
12272    /// Average setup time in hours
12273    #[serde(default = "default_setup_time")]
12274    pub setup_time_hours: f64,
12275    /// Run time variation coefficient
12276    #[serde(default = "default_run_time_variation")]
12277    pub run_time_variation: f64,
12278}
12279
12280impl Default for RoutingConfig {
12281    fn default() -> Self {
12282        Self {
12283            avg_operations: default_avg_operations(),
12284            setup_time_hours: default_setup_time(),
12285            run_time_variation: default_run_time_variation(),
12286        }
12287    }
12288}
12289
12290fn default_avg_operations() -> u32 {
12291    4
12292}
12293fn default_setup_time() -> f64 {
12294    1.5
12295}
12296fn default_run_time_variation() -> f64 {
12297    0.15
12298}
12299
12300// ----- Sales Quote Configuration -----
12301
12302/// Sales quote (quote-to-order) pipeline configuration.
12303#[derive(Debug, Clone, Serialize, Deserialize)]
12304pub struct SalesQuoteConfig {
12305    /// Enable sales quote generation
12306    #[serde(default)]
12307    pub enabled: bool,
12308    /// Quotes per month
12309    #[serde(default = "default_quotes_per_month")]
12310    pub quotes_per_month: u32,
12311    /// Win rate (fraction of quotes that convert to orders)
12312    #[serde(default = "default_quote_win_rate")]
12313    pub win_rate: f64,
12314    /// Average quote validity in days
12315    #[serde(default = "default_quote_validity_days")]
12316    pub validity_days: u32,
12317}
12318
12319impl Default for SalesQuoteConfig {
12320    fn default() -> Self {
12321        Self {
12322            enabled: false,
12323            quotes_per_month: default_quotes_per_month(),
12324            win_rate: default_quote_win_rate(),
12325            validity_days: default_quote_validity_days(),
12326        }
12327    }
12328}
12329
12330fn default_quotes_per_month() -> u32 {
12331    30
12332}
12333fn default_quote_win_rate() -> f64 {
12334    0.35
12335}
12336fn default_quote_validity_days() -> u32 {
12337    30
12338}
12339
12340// =============================================================================
12341// Tax Accounting Configuration
12342// =============================================================================
12343
12344/// Tax accounting configuration.
12345///
12346/// Controls generation of tax-related data including VAT/GST, sales tax,
12347/// withholding tax, tax provisions, and payroll tax across multiple jurisdictions.
12348#[derive(Debug, Clone, Serialize, Deserialize)]
12349pub struct TaxConfig {
12350    /// Whether tax generation is enabled.
12351    #[serde(default)]
12352    pub enabled: bool,
12353    /// Tax jurisdiction configuration.
12354    #[serde(default)]
12355    pub jurisdictions: TaxJurisdictionConfig,
12356    /// VAT/GST configuration.
12357    #[serde(default)]
12358    pub vat_gst: VatGstConfig,
12359    /// Sales tax configuration.
12360    #[serde(default)]
12361    pub sales_tax: SalesTaxConfig,
12362    /// Withholding tax configuration.
12363    #[serde(default)]
12364    pub withholding: WithholdingTaxSchemaConfig,
12365    /// Tax provision configuration.
12366    #[serde(default)]
12367    pub provisions: TaxProvisionSchemaConfig,
12368    /// Payroll tax configuration.
12369    #[serde(default)]
12370    pub payroll_tax: PayrollTaxSchemaConfig,
12371    /// Anomaly injection rate for tax data (0.0 to 1.0).
12372    #[serde(default = "default_tax_anomaly_rate")]
12373    pub anomaly_rate: f64,
12374}
12375
12376fn default_tax_anomaly_rate() -> f64 {
12377    0.03
12378}
12379
12380impl Default for TaxConfig {
12381    fn default() -> Self {
12382        Self {
12383            enabled: false,
12384            jurisdictions: TaxJurisdictionConfig::default(),
12385            vat_gst: VatGstConfig::default(),
12386            sales_tax: SalesTaxConfig::default(),
12387            withholding: WithholdingTaxSchemaConfig::default(),
12388            provisions: TaxProvisionSchemaConfig::default(),
12389            payroll_tax: PayrollTaxSchemaConfig::default(),
12390            anomaly_rate: default_tax_anomaly_rate(),
12391        }
12392    }
12393}
12394
12395/// Tax jurisdiction configuration.
12396///
12397/// Specifies which countries and subnational jurisdictions to include
12398/// when generating tax data.
12399#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12400pub struct TaxJurisdictionConfig {
12401    /// List of country codes to include (e.g., ["US", "DE", "GB"]).
12402    #[serde(default)]
12403    pub countries: Vec<String>,
12404    /// Whether to include subnational jurisdictions (e.g., US states, Canadian provinces).
12405    #[serde(default)]
12406    pub include_subnational: bool,
12407}
12408
12409/// VAT/GST configuration.
12410///
12411/// Controls generation of Value Added Tax / Goods and Services Tax data,
12412/// including standard and reduced rates, exempt categories, and reverse charge.
12413#[derive(Debug, Clone, Serialize, Deserialize)]
12414pub struct VatGstConfig {
12415    /// Whether VAT/GST generation is enabled.
12416    #[serde(default)]
12417    pub enabled: bool,
12418    /// Standard VAT/GST rates by country code (e.g., {"DE": 0.19, "GB": 0.20}).
12419    #[serde(default)]
12420    pub standard_rates: std::collections::HashMap<String, f64>,
12421    /// Reduced VAT/GST rates by country code (e.g., {"DE": 0.07, "GB": 0.05}).
12422    #[serde(default)]
12423    pub reduced_rates: std::collections::HashMap<String, f64>,
12424    /// Categories exempt from VAT/GST (e.g., ["financial_services", "healthcare"]).
12425    #[serde(default)]
12426    pub exempt_categories: Vec<String>,
12427    /// Whether to apply reverse charge mechanism for cross-border B2B transactions.
12428    #[serde(default = "default_true")]
12429    pub reverse_charge: bool,
12430}
12431
12432impl Default for VatGstConfig {
12433    fn default() -> Self {
12434        Self {
12435            enabled: false,
12436            standard_rates: std::collections::HashMap::new(),
12437            reduced_rates: std::collections::HashMap::new(),
12438            exempt_categories: Vec::new(),
12439            reverse_charge: true,
12440        }
12441    }
12442}
12443
12444/// Sales tax configuration.
12445///
12446/// Controls generation of US-style sales tax data including nexus determination.
12447#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12448pub struct SalesTaxConfig {
12449    /// Whether sales tax generation is enabled.
12450    #[serde(default)]
12451    pub enabled: bool,
12452    /// US states where the company has nexus (e.g., ["CA", "NY", "TX"]).
12453    #[serde(default)]
12454    pub nexus_states: Vec<String>,
12455}
12456
12457/// Withholding tax configuration.
12458///
12459/// Controls generation of withholding tax data for cross-border payments,
12460/// including treaty network and rate overrides.
12461#[derive(Debug, Clone, Serialize, Deserialize)]
12462pub struct WithholdingTaxSchemaConfig {
12463    /// Whether withholding tax generation is enabled.
12464    #[serde(default)]
12465    pub enabled: bool,
12466    /// Whether to simulate a treaty network with reduced rates.
12467    #[serde(default = "default_true")]
12468    pub treaty_network: bool,
12469    /// Default withholding tax rate for non-treaty countries (0.0 to 1.0).
12470    #[serde(default = "default_withholding_rate")]
12471    pub default_rate: f64,
12472    /// Reduced withholding tax rate for treaty countries (0.0 to 1.0).
12473    #[serde(default = "default_treaty_reduced_rate")]
12474    pub treaty_reduced_rate: f64,
12475}
12476
12477fn default_withholding_rate() -> f64 {
12478    0.30
12479}
12480
12481fn default_treaty_reduced_rate() -> f64 {
12482    0.15
12483}
12484
12485impl Default for WithholdingTaxSchemaConfig {
12486    fn default() -> Self {
12487        Self {
12488            enabled: false,
12489            treaty_network: true,
12490            default_rate: default_withholding_rate(),
12491            treaty_reduced_rate: default_treaty_reduced_rate(),
12492        }
12493    }
12494}
12495
12496/// Tax provision configuration.
12497///
12498/// Controls generation of tax provision data including statutory rates
12499/// and uncertain tax positions (ASC 740 / IAS 12).
12500#[derive(Debug, Clone, Serialize, Deserialize)]
12501pub struct TaxProvisionSchemaConfig {
12502    /// Whether tax provision generation is enabled.
12503    /// Defaults to true when tax is enabled, as provisions are typically required.
12504    #[serde(default = "default_true")]
12505    pub enabled: bool,
12506    /// Statutory corporate tax rate (0.0 to 1.0).
12507    #[serde(default = "default_statutory_rate")]
12508    pub statutory_rate: f64,
12509    /// Whether to generate uncertain tax positions (FIN 48 / IFRIC 23).
12510    #[serde(default = "default_true")]
12511    pub uncertain_positions: bool,
12512}
12513
12514fn default_statutory_rate() -> f64 {
12515    0.21
12516}
12517
12518impl Default for TaxProvisionSchemaConfig {
12519    fn default() -> Self {
12520        Self {
12521            enabled: true,
12522            statutory_rate: default_statutory_rate(),
12523            uncertain_positions: true,
12524        }
12525    }
12526}
12527
12528/// Payroll tax configuration.
12529///
12530/// Controls generation of payroll tax data (employer/employee contributions,
12531/// social security, Medicare, etc.).
12532#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12533pub struct PayrollTaxSchemaConfig {
12534    /// Whether payroll tax generation is enabled.
12535    #[serde(default)]
12536    pub enabled: bool,
12537}
12538
12539// ---------------------------------------------------------------------------
12540// Treasury & Cash Management Configuration
12541// ---------------------------------------------------------------------------
12542
12543/// Treasury and cash management configuration.
12544///
12545/// Controls generation of cash positions, forecasts, pooling, hedging
12546/// instruments (ASC 815 / IFRS 9), debt instruments with covenants,
12547/// bank guarantees, and intercompany netting runs.
12548#[derive(Debug, Clone, Serialize, Deserialize)]
12549pub struct TreasuryConfig {
12550    /// Whether treasury generation is enabled.
12551    #[serde(default)]
12552    pub enabled: bool,
12553    /// Cash positioning configuration.
12554    #[serde(default)]
12555    pub cash_positioning: CashPositioningConfig,
12556    /// Cash forecasting configuration.
12557    #[serde(default)]
12558    pub cash_forecasting: CashForecastingConfig,
12559    /// Cash pooling configuration.
12560    #[serde(default)]
12561    pub cash_pooling: CashPoolingConfig,
12562    /// Hedging configuration (FX forwards, IR swaps, etc.).
12563    #[serde(default)]
12564    pub hedging: HedgingSchemaConfig,
12565    /// Debt instrument and covenant configuration.
12566    #[serde(default)]
12567    pub debt: DebtSchemaConfig,
12568    /// Intercompany netting configuration.
12569    #[serde(default)]
12570    pub netting: NettingSchemaConfig,
12571    /// Bank guarantee / letter of credit configuration.
12572    #[serde(default)]
12573    pub bank_guarantees: BankGuaranteeSchemaConfig,
12574    /// Anomaly injection rate for treasury data (0.0 to 1.0).
12575    #[serde(default = "default_treasury_anomaly_rate")]
12576    pub anomaly_rate: f64,
12577}
12578
12579fn default_treasury_anomaly_rate() -> f64 {
12580    0.02
12581}
12582
12583impl Default for TreasuryConfig {
12584    fn default() -> Self {
12585        Self {
12586            enabled: false,
12587            cash_positioning: CashPositioningConfig::default(),
12588            cash_forecasting: CashForecastingConfig::default(),
12589            cash_pooling: CashPoolingConfig::default(),
12590            hedging: HedgingSchemaConfig::default(),
12591            debt: DebtSchemaConfig::default(),
12592            netting: NettingSchemaConfig::default(),
12593            bank_guarantees: BankGuaranteeSchemaConfig::default(),
12594            anomaly_rate: default_treasury_anomaly_rate(),
12595        }
12596    }
12597}
12598
12599/// Cash positioning configuration.
12600///
12601/// Controls daily cash position generation per entity/bank account.
12602#[derive(Debug, Clone, Serialize, Deserialize)]
12603pub struct CashPositioningConfig {
12604    /// Whether cash positioning is enabled.
12605    #[serde(default = "default_true")]
12606    pub enabled: bool,
12607    /// Position generation frequency.
12608    #[serde(default = "default_cash_frequency")]
12609    pub frequency: String,
12610    /// Minimum cash balance policy threshold.
12611    #[serde(default = "default_minimum_balance_policy")]
12612    pub minimum_balance_policy: f64,
12613}
12614
12615fn default_cash_frequency() -> String {
12616    "daily".to_string()
12617}
12618
12619fn default_minimum_balance_policy() -> f64 {
12620    100_000.0
12621}
12622
12623impl Default for CashPositioningConfig {
12624    fn default() -> Self {
12625        Self {
12626            enabled: true,
12627            frequency: default_cash_frequency(),
12628            minimum_balance_policy: default_minimum_balance_policy(),
12629        }
12630    }
12631}
12632
12633/// Cash forecasting configuration.
12634///
12635/// Controls forward-looking cash forecast generation with probability-weighted items.
12636#[derive(Debug, Clone, Serialize, Deserialize)]
12637pub struct CashForecastingConfig {
12638    /// Whether cash forecasting is enabled.
12639    #[serde(default = "default_true")]
12640    pub enabled: bool,
12641    /// Number of days to forecast into the future.
12642    #[serde(default = "default_horizon_days")]
12643    pub horizon_days: u32,
12644    /// AR collection probability curve type ("aging" or "flat").
12645    #[serde(default = "default_ar_probability_curve")]
12646    pub ar_collection_probability_curve: String,
12647    /// Confidence interval for the forecast (0.0 to 1.0).
12648    #[serde(default = "default_confidence_interval")]
12649    pub confidence_interval: f64,
12650}
12651
12652fn default_horizon_days() -> u32 {
12653    90
12654}
12655
12656fn default_ar_probability_curve() -> String {
12657    "aging".to_string()
12658}
12659
12660fn default_confidence_interval() -> f64 {
12661    0.90
12662}
12663
12664impl Default for CashForecastingConfig {
12665    fn default() -> Self {
12666        Self {
12667            enabled: true,
12668            horizon_days: default_horizon_days(),
12669            ar_collection_probability_curve: default_ar_probability_curve(),
12670            confidence_interval: default_confidence_interval(),
12671        }
12672    }
12673}
12674
12675/// Cash pooling configuration.
12676///
12677/// Controls cash pool structure generation (physical, notional, zero-balancing).
12678#[derive(Debug, Clone, Serialize, Deserialize)]
12679pub struct CashPoolingConfig {
12680    /// Whether cash pooling is enabled.
12681    #[serde(default)]
12682    pub enabled: bool,
12683    /// Pool type: "physical_pooling", "notional_pooling", or "zero_balancing".
12684    #[serde(default = "default_pool_type")]
12685    pub pool_type: String,
12686    /// Time of day when sweeps occur (HH:MM format).
12687    #[serde(default = "default_sweep_time")]
12688    pub sweep_time: String,
12689}
12690
12691fn default_pool_type() -> String {
12692    "zero_balancing".to_string()
12693}
12694
12695fn default_sweep_time() -> String {
12696    "16:00".to_string()
12697}
12698
12699impl Default for CashPoolingConfig {
12700    fn default() -> Self {
12701        Self {
12702            enabled: false,
12703            pool_type: default_pool_type(),
12704            sweep_time: default_sweep_time(),
12705        }
12706    }
12707}
12708
12709/// Hedging configuration.
12710///
12711/// Controls generation of hedging instruments and hedge relationship designations
12712/// under ASC 815 / IFRS 9.
12713#[derive(Debug, Clone, Serialize, Deserialize)]
12714pub struct HedgingSchemaConfig {
12715    /// Whether hedging generation is enabled.
12716    #[serde(default)]
12717    pub enabled: bool,
12718    /// Target hedge ratio (0.0 to 1.0). Proportion of FX exposure to hedge.
12719    #[serde(default = "default_hedge_ratio")]
12720    pub hedge_ratio: f64,
12721    /// Types of instruments to generate (e.g., ["fx_forward", "interest_rate_swap"]).
12722    #[serde(default = "default_hedge_instruments")]
12723    pub instruments: Vec<String>,
12724    /// Whether to designate formal hedge accounting relationships.
12725    #[serde(default = "default_true")]
12726    pub hedge_accounting: bool,
12727    /// Effectiveness testing method: "dollar_offset", "regression", or "critical_terms".
12728    #[serde(default = "default_effectiveness_method")]
12729    pub effectiveness_method: String,
12730}
12731
12732fn default_hedge_ratio() -> f64 {
12733    0.75
12734}
12735
12736fn default_hedge_instruments() -> Vec<String> {
12737    vec!["fx_forward".to_string(), "interest_rate_swap".to_string()]
12738}
12739
12740fn default_effectiveness_method() -> String {
12741    "regression".to_string()
12742}
12743
12744impl Default for HedgingSchemaConfig {
12745    fn default() -> Self {
12746        Self {
12747            enabled: false,
12748            hedge_ratio: default_hedge_ratio(),
12749            instruments: default_hedge_instruments(),
12750            hedge_accounting: true,
12751            effectiveness_method: default_effectiveness_method(),
12752        }
12753    }
12754}
12755
12756/// Debt instrument configuration.
12757///
12758/// Controls generation of debt instruments (term loans, revolving credit, bonds)
12759/// with amortization schedules and financial covenants.
12760#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12761pub struct DebtSchemaConfig {
12762    /// Whether debt instrument generation is enabled.
12763    #[serde(default)]
12764    pub enabled: bool,
12765    /// Debt instrument definitions.
12766    #[serde(default)]
12767    pub instruments: Vec<DebtInstrumentDef>,
12768    /// Covenant definitions.
12769    #[serde(default)]
12770    pub covenants: Vec<CovenantDef>,
12771}
12772
12773/// Definition of a debt instrument in configuration.
12774#[derive(Debug, Clone, Serialize, Deserialize)]
12775pub struct DebtInstrumentDef {
12776    /// Instrument type: "term_loan", "revolving_credit", "bond", "commercial_paper", "bridge_loan".
12777    #[serde(rename = "type")]
12778    pub instrument_type: String,
12779    /// Principal amount (for term loans, bonds).
12780    #[serde(default)]
12781    pub principal: Option<f64>,
12782    /// Interest rate (annual, as decimal fraction).
12783    #[serde(default)]
12784    pub rate: Option<f64>,
12785    /// Maturity in months.
12786    #[serde(default)]
12787    pub maturity_months: Option<u32>,
12788    /// Facility limit (for revolving credit).
12789    #[serde(default)]
12790    pub facility: Option<f64>,
12791}
12792
12793/// Definition of a debt covenant in configuration.
12794#[derive(Debug, Clone, Serialize, Deserialize)]
12795pub struct CovenantDef {
12796    /// Covenant type: "debt_to_equity", "interest_coverage", "current_ratio",
12797    /// "net_worth", "debt_to_ebitda", "fixed_charge_coverage".
12798    #[serde(rename = "type")]
12799    pub covenant_type: String,
12800    /// Covenant threshold value.
12801    pub threshold: f64,
12802}
12803
12804/// Intercompany netting configuration.
12805///
12806/// Controls generation of multilateral netting runs.
12807#[derive(Debug, Clone, Serialize, Deserialize)]
12808pub struct NettingSchemaConfig {
12809    /// Whether netting generation is enabled.
12810    #[serde(default)]
12811    pub enabled: bool,
12812    /// Netting cycle: "daily", "weekly", or "monthly".
12813    #[serde(default = "default_netting_cycle")]
12814    pub cycle: String,
12815}
12816
12817fn default_netting_cycle() -> String {
12818    "monthly".to_string()
12819}
12820
12821impl Default for NettingSchemaConfig {
12822    fn default() -> Self {
12823        Self {
12824            enabled: false,
12825            cycle: default_netting_cycle(),
12826        }
12827    }
12828}
12829
12830/// Bank guarantee and letter of credit configuration.
12831///
12832/// Controls generation of bank guarantees, standby LCs, and performance bonds.
12833#[derive(Debug, Clone, Serialize, Deserialize)]
12834pub struct BankGuaranteeSchemaConfig {
12835    /// Whether bank guarantee generation is enabled.
12836    #[serde(default)]
12837    pub enabled: bool,
12838    /// Number of guarantees to generate.
12839    #[serde(default = "default_guarantee_count")]
12840    pub count: u32,
12841}
12842
12843fn default_guarantee_count() -> u32 {
12844    5
12845}
12846
12847impl Default for BankGuaranteeSchemaConfig {
12848    fn default() -> Self {
12849        Self {
12850            enabled: false,
12851            count: default_guarantee_count(),
12852        }
12853    }
12854}
12855
12856// ===========================================================================
12857// Project Accounting Configuration
12858// ===========================================================================
12859
12860/// Project accounting configuration.
12861///
12862/// Controls generation of project cost lines, revenue recognition,
12863/// milestones, change orders, retainage, and earned value metrics.
12864#[derive(Debug, Clone, Serialize, Deserialize)]
12865pub struct ProjectAccountingConfig {
12866    /// Whether project accounting is enabled.
12867    #[serde(default)]
12868    pub enabled: bool,
12869    /// Number of projects to generate.
12870    #[serde(default = "default_project_count")]
12871    pub project_count: u32,
12872    /// Distribution of project types (capital, internal, customer, r_and_d, maintenance, technology).
12873    #[serde(default)]
12874    pub project_types: ProjectTypeDistribution,
12875    /// WBS structure configuration.
12876    #[serde(default)]
12877    pub wbs: WbsSchemaConfig,
12878    /// Cost allocation rates (what % of source documents get project-tagged).
12879    #[serde(default)]
12880    pub cost_allocation: CostAllocationConfig,
12881    /// Revenue recognition configuration for project accounting.
12882    #[serde(default)]
12883    pub revenue_recognition: ProjectRevenueRecognitionConfig,
12884    /// Milestone configuration.
12885    #[serde(default)]
12886    pub milestones: MilestoneSchemaConfig,
12887    /// Change order configuration.
12888    #[serde(default)]
12889    pub change_orders: ChangeOrderSchemaConfig,
12890    /// Retainage configuration.
12891    #[serde(default)]
12892    pub retainage: RetainageSchemaConfig,
12893    /// Earned value management configuration.
12894    #[serde(default)]
12895    pub earned_value: EarnedValueSchemaConfig,
12896    /// Anomaly injection rate for project accounting data (0.0 to 1.0).
12897    #[serde(default = "default_project_anomaly_rate")]
12898    pub anomaly_rate: f64,
12899}
12900
12901fn default_project_count() -> u32 {
12902    10
12903}
12904
12905fn default_project_anomaly_rate() -> f64 {
12906    0.03
12907}
12908
12909impl Default for ProjectAccountingConfig {
12910    fn default() -> Self {
12911        Self {
12912            enabled: false,
12913            project_count: default_project_count(),
12914            project_types: ProjectTypeDistribution::default(),
12915            wbs: WbsSchemaConfig::default(),
12916            cost_allocation: CostAllocationConfig::default(),
12917            revenue_recognition: ProjectRevenueRecognitionConfig::default(),
12918            milestones: MilestoneSchemaConfig::default(),
12919            change_orders: ChangeOrderSchemaConfig::default(),
12920            retainage: RetainageSchemaConfig::default(),
12921            earned_value: EarnedValueSchemaConfig::default(),
12922            anomaly_rate: default_project_anomaly_rate(),
12923        }
12924    }
12925}
12926
12927/// Distribution of project types by weight.
12928#[derive(Debug, Clone, Serialize, Deserialize)]
12929pub struct ProjectTypeDistribution {
12930    /// Weight for capital projects (default 0.25).
12931    #[serde(default = "default_capital_weight")]
12932    pub capital: f64,
12933    /// Weight for internal projects (default 0.20).
12934    #[serde(default = "default_internal_weight")]
12935    pub internal: f64,
12936    /// Weight for customer projects (default 0.30).
12937    #[serde(default = "default_customer_weight")]
12938    pub customer: f64,
12939    /// Weight for R&D projects (default 0.10).
12940    #[serde(default = "default_rnd_weight")]
12941    pub r_and_d: f64,
12942    /// Weight for maintenance projects (default 0.10).
12943    #[serde(default = "default_maintenance_weight")]
12944    pub maintenance: f64,
12945    /// Weight for technology projects (default 0.05).
12946    #[serde(default = "default_technology_weight")]
12947    pub technology: f64,
12948}
12949
12950fn default_capital_weight() -> f64 {
12951    0.25
12952}
12953fn default_internal_weight() -> f64 {
12954    0.20
12955}
12956fn default_customer_weight() -> f64 {
12957    0.30
12958}
12959fn default_rnd_weight() -> f64 {
12960    0.10
12961}
12962fn default_maintenance_weight() -> f64 {
12963    0.10
12964}
12965fn default_technology_weight() -> f64 {
12966    0.05
12967}
12968
12969impl Default for ProjectTypeDistribution {
12970    fn default() -> Self {
12971        Self {
12972            capital: default_capital_weight(),
12973            internal: default_internal_weight(),
12974            customer: default_customer_weight(),
12975            r_and_d: default_rnd_weight(),
12976            maintenance: default_maintenance_weight(),
12977            technology: default_technology_weight(),
12978        }
12979    }
12980}
12981
12982/// WBS structure configuration.
12983#[derive(Debug, Clone, Serialize, Deserialize)]
12984pub struct WbsSchemaConfig {
12985    /// Maximum depth of WBS hierarchy (default 3).
12986    #[serde(default = "default_wbs_max_depth")]
12987    pub max_depth: u32,
12988    /// Minimum elements per level-1 WBS (default 2).
12989    #[serde(default = "default_wbs_min_elements")]
12990    pub min_elements_per_level: u32,
12991    /// Maximum elements per level-1 WBS (default 6).
12992    #[serde(default = "default_wbs_max_elements")]
12993    pub max_elements_per_level: u32,
12994}
12995
12996fn default_wbs_max_depth() -> u32 {
12997    3
12998}
12999fn default_wbs_min_elements() -> u32 {
13000    2
13001}
13002fn default_wbs_max_elements() -> u32 {
13003    6
13004}
13005
13006impl Default for WbsSchemaConfig {
13007    fn default() -> Self {
13008        Self {
13009            max_depth: default_wbs_max_depth(),
13010            min_elements_per_level: default_wbs_min_elements(),
13011            max_elements_per_level: default_wbs_max_elements(),
13012        }
13013    }
13014}
13015
13016/// Cost allocation rates — what fraction of each document type gets linked to a project.
13017#[derive(Debug, Clone, Serialize, Deserialize)]
13018pub struct CostAllocationConfig {
13019    /// Fraction of time entries assigned to projects (0.0 to 1.0).
13020    #[serde(default = "default_time_entry_rate")]
13021    pub time_entry_project_rate: f64,
13022    /// Fraction of expense reports assigned to projects (0.0 to 1.0).
13023    #[serde(default = "default_expense_rate")]
13024    pub expense_project_rate: f64,
13025    /// Fraction of purchase orders assigned to projects (0.0 to 1.0).
13026    #[serde(default = "default_po_rate")]
13027    pub purchase_order_project_rate: f64,
13028    /// Fraction of vendor invoices assigned to projects (0.0 to 1.0).
13029    #[serde(default = "default_vi_rate")]
13030    pub vendor_invoice_project_rate: f64,
13031}
13032
13033fn default_time_entry_rate() -> f64 {
13034    0.60
13035}
13036fn default_expense_rate() -> f64 {
13037    0.30
13038}
13039fn default_po_rate() -> f64 {
13040    0.40
13041}
13042fn default_vi_rate() -> f64 {
13043    0.35
13044}
13045
13046impl Default for CostAllocationConfig {
13047    fn default() -> Self {
13048        Self {
13049            time_entry_project_rate: default_time_entry_rate(),
13050            expense_project_rate: default_expense_rate(),
13051            purchase_order_project_rate: default_po_rate(),
13052            vendor_invoice_project_rate: default_vi_rate(),
13053        }
13054    }
13055}
13056
13057/// Revenue recognition configuration for project accounting.
13058#[derive(Debug, Clone, Serialize, Deserialize)]
13059pub struct ProjectRevenueRecognitionConfig {
13060    /// Whether revenue recognition is enabled for customer projects.
13061    #[serde(default = "default_true")]
13062    pub enabled: bool,
13063    /// Default method: "percentage_of_completion", "completed_contract", "milestone_based".
13064    #[serde(default = "default_revenue_method")]
13065    pub method: String,
13066    /// Default completion measure: "cost_to_cost", "labor_hours", "physical_completion".
13067    #[serde(default = "default_completion_measure")]
13068    pub completion_measure: String,
13069    /// Average contract value for customer projects.
13070    #[serde(default = "default_avg_contract_value")]
13071    pub avg_contract_value: f64,
13072}
13073
13074fn default_revenue_method() -> String {
13075    "percentage_of_completion".to_string()
13076}
13077fn default_completion_measure() -> String {
13078    "cost_to_cost".to_string()
13079}
13080fn default_avg_contract_value() -> f64 {
13081    500_000.0
13082}
13083
13084impl Default for ProjectRevenueRecognitionConfig {
13085    fn default() -> Self {
13086        Self {
13087            enabled: true,
13088            method: default_revenue_method(),
13089            completion_measure: default_completion_measure(),
13090            avg_contract_value: default_avg_contract_value(),
13091        }
13092    }
13093}
13094
13095/// Milestone configuration.
13096#[derive(Debug, Clone, Serialize, Deserialize)]
13097pub struct MilestoneSchemaConfig {
13098    /// Whether milestone generation is enabled.
13099    #[serde(default = "default_true")]
13100    pub enabled: bool,
13101    /// Average number of milestones per project.
13102    #[serde(default = "default_milestones_per_project")]
13103    pub avg_per_project: u32,
13104    /// Fraction of milestones that are payment milestones (0.0 to 1.0).
13105    #[serde(default = "default_payment_milestone_rate")]
13106    pub payment_milestone_rate: f64,
13107}
13108
13109fn default_milestones_per_project() -> u32 {
13110    4
13111}
13112fn default_payment_milestone_rate() -> f64 {
13113    0.50
13114}
13115
13116impl Default for MilestoneSchemaConfig {
13117    fn default() -> Self {
13118        Self {
13119            enabled: true,
13120            avg_per_project: default_milestones_per_project(),
13121            payment_milestone_rate: default_payment_milestone_rate(),
13122        }
13123    }
13124}
13125
13126/// Change order configuration.
13127#[derive(Debug, Clone, Serialize, Deserialize)]
13128pub struct ChangeOrderSchemaConfig {
13129    /// Whether change order generation is enabled.
13130    #[serde(default = "default_true")]
13131    pub enabled: bool,
13132    /// Probability that a project will have at least one change order (0.0 to 1.0).
13133    #[serde(default = "default_change_order_probability")]
13134    pub probability: f64,
13135    /// Maximum change orders per project.
13136    #[serde(default = "default_max_change_orders")]
13137    pub max_per_project: u32,
13138    /// Approval rate for change orders (0.0 to 1.0).
13139    #[serde(default = "default_change_order_approval_rate")]
13140    pub approval_rate: f64,
13141}
13142
13143fn default_change_order_probability() -> f64 {
13144    0.40
13145}
13146fn default_max_change_orders() -> u32 {
13147    3
13148}
13149fn default_change_order_approval_rate() -> f64 {
13150    0.75
13151}
13152
13153impl Default for ChangeOrderSchemaConfig {
13154    fn default() -> Self {
13155        Self {
13156            enabled: true,
13157            probability: default_change_order_probability(),
13158            max_per_project: default_max_change_orders(),
13159            approval_rate: default_change_order_approval_rate(),
13160        }
13161    }
13162}
13163
13164/// Retainage configuration.
13165#[derive(Debug, Clone, Serialize, Deserialize)]
13166pub struct RetainageSchemaConfig {
13167    /// Whether retainage is enabled.
13168    #[serde(default)]
13169    pub enabled: bool,
13170    /// Default retainage percentage (0.0 to 1.0, e.g., 0.10 for 10%).
13171    #[serde(default = "default_retainage_pct")]
13172    pub default_percentage: f64,
13173}
13174
13175fn default_retainage_pct() -> f64 {
13176    0.10
13177}
13178
13179impl Default for RetainageSchemaConfig {
13180    fn default() -> Self {
13181        Self {
13182            enabled: false,
13183            default_percentage: default_retainage_pct(),
13184        }
13185    }
13186}
13187
13188/// Earned value management (EVM) configuration.
13189#[derive(Debug, Clone, Serialize, Deserialize)]
13190pub struct EarnedValueSchemaConfig {
13191    /// Whether EVM metrics are generated.
13192    #[serde(default = "default_true")]
13193    pub enabled: bool,
13194    /// Measurement frequency: "weekly", "biweekly", "monthly".
13195    #[serde(default = "default_evm_frequency")]
13196    pub frequency: String,
13197}
13198
13199fn default_evm_frequency() -> String {
13200    "monthly".to_string()
13201}
13202
13203impl Default for EarnedValueSchemaConfig {
13204    fn default() -> Self {
13205        Self {
13206            enabled: true,
13207            frequency: default_evm_frequency(),
13208        }
13209    }
13210}
13211
13212// =============================================================================
13213// ESG / Sustainability Configuration
13214// =============================================================================
13215
13216/// Top-level ESG / sustainability reporting configuration.
13217#[derive(Debug, Clone, Serialize, Deserialize)]
13218pub struct EsgConfig {
13219    /// Whether ESG generation is enabled.
13220    #[serde(default)]
13221    pub enabled: bool,
13222    /// Environmental metrics (emissions, energy, water, waste).
13223    #[serde(default)]
13224    pub environmental: EnvironmentalConfig,
13225    /// Social metrics (diversity, pay equity, safety).
13226    #[serde(default)]
13227    pub social: SocialConfig,
13228    /// Governance metrics (board composition, ethics, compliance).
13229    #[serde(default)]
13230    pub governance: GovernanceSchemaConfig,
13231    /// Supply-chain ESG assessment settings.
13232    #[serde(default)]
13233    pub supply_chain_esg: SupplyChainEsgConfig,
13234    /// ESG reporting / disclosure framework settings.
13235    #[serde(default)]
13236    pub reporting: EsgReportingConfig,
13237    /// Climate scenario analysis settings.
13238    #[serde(default)]
13239    pub climate_scenarios: ClimateScenarioConfig,
13240    /// Anomaly injection rate for ESG data (0.0 to 1.0).
13241    #[serde(default = "default_esg_anomaly_rate")]
13242    pub anomaly_rate: f64,
13243}
13244
13245fn default_esg_anomaly_rate() -> f64 {
13246    0.02
13247}
13248
13249impl Default for EsgConfig {
13250    fn default() -> Self {
13251        Self {
13252            enabled: false,
13253            environmental: EnvironmentalConfig::default(),
13254            social: SocialConfig::default(),
13255            governance: GovernanceSchemaConfig::default(),
13256            supply_chain_esg: SupplyChainEsgConfig::default(),
13257            reporting: EsgReportingConfig::default(),
13258            climate_scenarios: ClimateScenarioConfig::default(),
13259            anomaly_rate: default_esg_anomaly_rate(),
13260        }
13261    }
13262}
13263
13264/// Country pack configuration.
13265///
13266/// Controls where to load additional country packs and per-country overrides.
13267/// When omitted, only the built-in packs (_default, US, DE, GB) are used.
13268#[derive(Debug, Clone, Serialize, Deserialize, Default)]
13269pub struct CountryPacksSchemaConfig {
13270    /// Optional directory containing additional `*.json` country packs.
13271    #[serde(default)]
13272    pub external_dir: Option<PathBuf>,
13273    /// Per-country overrides applied after loading.
13274    /// Keys are ISO 3166-1 alpha-2 codes; values are partial JSON objects
13275    /// that are deep-merged on top of the loaded pack.
13276    #[serde(default)]
13277    pub overrides: std::collections::HashMap<String, serde_json::Value>,
13278}
13279
13280/// Environmental metrics configuration.
13281#[derive(Debug, Clone, Serialize, Deserialize)]
13282pub struct EnvironmentalConfig {
13283    /// Whether environmental metrics are generated.
13284    #[serde(default = "default_true")]
13285    pub enabled: bool,
13286    /// Scope 1 (direct) emission generation settings.
13287    #[serde(default)]
13288    pub scope1: EmissionScopeConfig,
13289    /// Scope 2 (purchased energy) emission generation settings.
13290    #[serde(default)]
13291    pub scope2: EmissionScopeConfig,
13292    /// Scope 3 (value chain) emission generation settings.
13293    #[serde(default)]
13294    pub scope3: Scope3Config,
13295    /// Energy consumption tracking settings.
13296    #[serde(default)]
13297    pub energy: EnergySchemaConfig,
13298    /// Water usage tracking settings.
13299    #[serde(default)]
13300    pub water: WaterSchemaConfig,
13301    /// Waste management tracking settings.
13302    #[serde(default)]
13303    pub waste: WasteSchemaConfig,
13304}
13305
13306impl Default for EnvironmentalConfig {
13307    fn default() -> Self {
13308        Self {
13309            enabled: true,
13310            scope1: EmissionScopeConfig::default(),
13311            scope2: EmissionScopeConfig::default(),
13312            scope3: Scope3Config::default(),
13313            energy: EnergySchemaConfig::default(),
13314            water: WaterSchemaConfig::default(),
13315            waste: WasteSchemaConfig::default(),
13316        }
13317    }
13318}
13319
13320/// Configuration for a single emission scope (Scope 1 or 2).
13321#[derive(Debug, Clone, Serialize, Deserialize)]
13322pub struct EmissionScopeConfig {
13323    /// Whether this scope is enabled.
13324    #[serde(default = "default_true")]
13325    pub enabled: bool,
13326    /// Emission factor region (e.g., "US", "EU", "global").
13327    #[serde(default = "default_emission_region")]
13328    pub factor_region: String,
13329}
13330
13331fn default_emission_region() -> String {
13332    "US".to_string()
13333}
13334
13335impl Default for EmissionScopeConfig {
13336    fn default() -> Self {
13337        Self {
13338            enabled: true,
13339            factor_region: default_emission_region(),
13340        }
13341    }
13342}
13343
13344/// Scope 3 (value chain) emission configuration.
13345#[derive(Debug, Clone, Serialize, Deserialize)]
13346pub struct Scope3Config {
13347    /// Whether Scope 3 emissions are generated.
13348    #[serde(default = "default_true")]
13349    pub enabled: bool,
13350    /// Categories to include (e.g., "purchased_goods", "business_travel", "commuting").
13351    #[serde(default = "default_scope3_categories")]
13352    pub categories: Vec<String>,
13353    /// Spend-based emission intensity (kg CO2e per USD).
13354    #[serde(default = "default_spend_intensity")]
13355    pub default_spend_intensity_kg_per_usd: f64,
13356}
13357
13358fn default_scope3_categories() -> Vec<String> {
13359    vec![
13360        "purchased_goods".to_string(),
13361        "business_travel".to_string(),
13362        "employee_commuting".to_string(),
13363    ]
13364}
13365
13366fn default_spend_intensity() -> f64 {
13367    0.5
13368}
13369
13370impl Default for Scope3Config {
13371    fn default() -> Self {
13372        Self {
13373            enabled: true,
13374            categories: default_scope3_categories(),
13375            default_spend_intensity_kg_per_usd: default_spend_intensity(),
13376        }
13377    }
13378}
13379
13380/// Energy consumption configuration.
13381#[derive(Debug, Clone, Serialize, Deserialize)]
13382pub struct EnergySchemaConfig {
13383    /// Whether energy consumption tracking is enabled.
13384    #[serde(default = "default_true")]
13385    pub enabled: bool,
13386    /// Number of facilities to generate.
13387    #[serde(default = "default_facility_count")]
13388    pub facility_count: u32,
13389    /// Target percentage of energy from renewable sources (0.0 to 1.0).
13390    #[serde(default = "default_renewable_target")]
13391    pub renewable_target: f64,
13392}
13393
13394fn default_facility_count() -> u32 {
13395    5
13396}
13397
13398fn default_renewable_target() -> f64 {
13399    0.30
13400}
13401
13402impl Default for EnergySchemaConfig {
13403    fn default() -> Self {
13404        Self {
13405            enabled: true,
13406            facility_count: default_facility_count(),
13407            renewable_target: default_renewable_target(),
13408        }
13409    }
13410}
13411
13412/// Water usage configuration.
13413#[derive(Debug, Clone, Serialize, Deserialize)]
13414pub struct WaterSchemaConfig {
13415    /// Whether water usage tracking is enabled.
13416    #[serde(default = "default_true")]
13417    pub enabled: bool,
13418    /// Number of facilities with water tracking.
13419    #[serde(default = "default_water_facility_count")]
13420    pub facility_count: u32,
13421}
13422
13423fn default_water_facility_count() -> u32 {
13424    3
13425}
13426
13427impl Default for WaterSchemaConfig {
13428    fn default() -> Self {
13429        Self {
13430            enabled: true,
13431            facility_count: default_water_facility_count(),
13432        }
13433    }
13434}
13435
13436/// Waste management configuration.
13437#[derive(Debug, Clone, Serialize, Deserialize)]
13438pub struct WasteSchemaConfig {
13439    /// Whether waste tracking is enabled.
13440    #[serde(default = "default_true")]
13441    pub enabled: bool,
13442    /// Target diversion rate (0.0 to 1.0).
13443    #[serde(default = "default_diversion_target")]
13444    pub diversion_target: f64,
13445}
13446
13447fn default_diversion_target() -> f64 {
13448    0.50
13449}
13450
13451impl Default for WasteSchemaConfig {
13452    fn default() -> Self {
13453        Self {
13454            enabled: true,
13455            diversion_target: default_diversion_target(),
13456        }
13457    }
13458}
13459
13460/// Social metrics configuration.
13461#[derive(Debug, Clone, Serialize, Deserialize)]
13462pub struct SocialConfig {
13463    /// Whether social metrics are generated.
13464    #[serde(default = "default_true")]
13465    pub enabled: bool,
13466    /// Workforce diversity tracking settings.
13467    #[serde(default)]
13468    pub diversity: DiversitySchemaConfig,
13469    /// Pay equity analysis settings.
13470    #[serde(default)]
13471    pub pay_equity: PayEquitySchemaConfig,
13472    /// Safety incident and metrics settings.
13473    #[serde(default)]
13474    pub safety: SafetySchemaConfig,
13475}
13476
13477impl Default for SocialConfig {
13478    fn default() -> Self {
13479        Self {
13480            enabled: true,
13481            diversity: DiversitySchemaConfig::default(),
13482            pay_equity: PayEquitySchemaConfig::default(),
13483            safety: SafetySchemaConfig::default(),
13484        }
13485    }
13486}
13487
13488/// Workforce diversity configuration.
13489#[derive(Debug, Clone, Serialize, Deserialize)]
13490pub struct DiversitySchemaConfig {
13491    /// Whether diversity metrics are generated.
13492    #[serde(default = "default_true")]
13493    pub enabled: bool,
13494    /// Dimensions to track (e.g., "gender", "ethnicity", "age_group").
13495    #[serde(default = "default_diversity_dimensions")]
13496    pub dimensions: Vec<String>,
13497}
13498
13499fn default_diversity_dimensions() -> Vec<String> {
13500    vec![
13501        "gender".to_string(),
13502        "ethnicity".to_string(),
13503        "age_group".to_string(),
13504    ]
13505}
13506
13507impl Default for DiversitySchemaConfig {
13508    fn default() -> Self {
13509        Self {
13510            enabled: true,
13511            dimensions: default_diversity_dimensions(),
13512        }
13513    }
13514}
13515
13516/// Pay equity analysis configuration.
13517#[derive(Debug, Clone, Serialize, Deserialize)]
13518pub struct PayEquitySchemaConfig {
13519    /// Whether pay equity analysis is generated.
13520    #[serde(default = "default_true")]
13521    pub enabled: bool,
13522    /// Target pay gap threshold for flagging (e.g., 0.05 = 5% gap).
13523    #[serde(default = "default_pay_gap_threshold")]
13524    pub gap_threshold: f64,
13525}
13526
13527fn default_pay_gap_threshold() -> f64 {
13528    0.05
13529}
13530
13531impl Default for PayEquitySchemaConfig {
13532    fn default() -> Self {
13533        Self {
13534            enabled: true,
13535            gap_threshold: default_pay_gap_threshold(),
13536        }
13537    }
13538}
13539
13540/// Safety metrics configuration.
13541#[derive(Debug, Clone, Serialize, Deserialize)]
13542pub struct SafetySchemaConfig {
13543    /// Whether safety metrics are generated.
13544    #[serde(default = "default_true")]
13545    pub enabled: bool,
13546    /// Average annual recordable incidents per 200,000 hours.
13547    #[serde(default = "default_trir_target")]
13548    pub target_trir: f64,
13549    /// Number of safety incidents to generate.
13550    #[serde(default = "default_incident_count")]
13551    pub incident_count: u32,
13552}
13553
13554fn default_trir_target() -> f64 {
13555    2.5
13556}
13557
13558fn default_incident_count() -> u32 {
13559    20
13560}
13561
13562impl Default for SafetySchemaConfig {
13563    fn default() -> Self {
13564        Self {
13565            enabled: true,
13566            target_trir: default_trir_target(),
13567            incident_count: default_incident_count(),
13568        }
13569    }
13570}
13571
13572/// Governance metrics configuration.
13573#[derive(Debug, Clone, Serialize, Deserialize)]
13574pub struct GovernanceSchemaConfig {
13575    /// Whether governance metrics are generated.
13576    #[serde(default = "default_true")]
13577    pub enabled: bool,
13578    /// Number of board members.
13579    #[serde(default = "default_board_size")]
13580    pub board_size: u32,
13581    /// Target independent director ratio (0.0 to 1.0).
13582    #[serde(default = "default_independence_target")]
13583    pub independence_target: f64,
13584}
13585
13586fn default_board_size() -> u32 {
13587    11
13588}
13589
13590fn default_independence_target() -> f64 {
13591    0.67
13592}
13593
13594impl Default for GovernanceSchemaConfig {
13595    fn default() -> Self {
13596        Self {
13597            enabled: true,
13598            board_size: default_board_size(),
13599            independence_target: default_independence_target(),
13600        }
13601    }
13602}
13603
13604/// Supply-chain ESG assessment configuration.
13605#[derive(Debug, Clone, Serialize, Deserialize)]
13606pub struct SupplyChainEsgConfig {
13607    /// Whether supply chain ESG assessments are generated.
13608    #[serde(default = "default_true")]
13609    pub enabled: bool,
13610    /// Proportion of vendors to assess (0.0 to 1.0).
13611    #[serde(default = "default_assessment_coverage")]
13612    pub assessment_coverage: f64,
13613    /// High-risk country codes for automatic flagging.
13614    #[serde(default = "default_high_risk_countries")]
13615    pub high_risk_countries: Vec<String>,
13616}
13617
13618fn default_assessment_coverage() -> f64 {
13619    0.80
13620}
13621
13622fn default_high_risk_countries() -> Vec<String> {
13623    vec!["CN".to_string(), "BD".to_string(), "MM".to_string()]
13624}
13625
13626impl Default for SupplyChainEsgConfig {
13627    fn default() -> Self {
13628        Self {
13629            enabled: true,
13630            assessment_coverage: default_assessment_coverage(),
13631            high_risk_countries: default_high_risk_countries(),
13632        }
13633    }
13634}
13635
13636/// ESG reporting / disclosure framework configuration.
13637#[derive(Debug, Clone, Serialize, Deserialize)]
13638pub struct EsgReportingConfig {
13639    /// Whether ESG disclosures are generated.
13640    #[serde(default = "default_true")]
13641    pub enabled: bool,
13642    /// Frameworks to generate disclosures for.
13643    #[serde(default = "default_esg_frameworks")]
13644    pub frameworks: Vec<String>,
13645    /// Whether materiality assessment is performed.
13646    #[serde(default = "default_true")]
13647    pub materiality_assessment: bool,
13648    /// Materiality threshold for impact dimension (0.0 to 1.0).
13649    #[serde(default = "default_materiality_threshold")]
13650    pub impact_threshold: f64,
13651    /// Materiality threshold for financial dimension (0.0 to 1.0).
13652    #[serde(default = "default_materiality_threshold")]
13653    pub financial_threshold: f64,
13654}
13655
13656fn default_esg_frameworks() -> Vec<String> {
13657    vec!["GRI".to_string(), "ESRS".to_string()]
13658}
13659
13660fn default_materiality_threshold() -> f64 {
13661    0.6
13662}
13663
13664impl Default for EsgReportingConfig {
13665    fn default() -> Self {
13666        Self {
13667            enabled: true,
13668            frameworks: default_esg_frameworks(),
13669            materiality_assessment: true,
13670            impact_threshold: default_materiality_threshold(),
13671            financial_threshold: default_materiality_threshold(),
13672        }
13673    }
13674}
13675
13676/// Climate scenario analysis configuration.
13677#[derive(Debug, Clone, Serialize, Deserialize)]
13678pub struct ClimateScenarioConfig {
13679    /// Whether climate scenario analysis is generated.
13680    #[serde(default)]
13681    pub enabled: bool,
13682    /// Scenarios to model (e.g., "net_zero_2050", "stated_policies", "current_trajectory").
13683    #[serde(default = "default_climate_scenarios")]
13684    pub scenarios: Vec<String>,
13685    /// Time horizons in years to project.
13686    #[serde(default = "default_time_horizons")]
13687    pub time_horizons: Vec<u32>,
13688}
13689
13690fn default_climate_scenarios() -> Vec<String> {
13691    vec![
13692        "net_zero_2050".to_string(),
13693        "stated_policies".to_string(),
13694        "current_trajectory".to_string(),
13695    ]
13696}
13697
13698fn default_time_horizons() -> Vec<u32> {
13699    vec![5, 10, 30]
13700}
13701
13702impl Default for ClimateScenarioConfig {
13703    fn default() -> Self {
13704        Self {
13705            enabled: false,
13706            scenarios: default_climate_scenarios(),
13707            time_horizons: default_time_horizons(),
13708        }
13709    }
13710}
13711
13712// ===== Counterfactual Simulation Scenarios =====
13713
13714/// Configuration for counterfactual simulation scenarios.
13715#[derive(Debug, Clone, Serialize, Deserialize, Default)]
13716pub struct ScenariosConfig {
13717    /// Whether scenario generation is enabled.
13718    #[serde(default)]
13719    pub enabled: bool,
13720    /// List of scenario definitions.
13721    #[serde(default)]
13722    pub scenarios: Vec<ScenarioSchemaConfig>,
13723    /// Causal model configuration.
13724    #[serde(default)]
13725    pub causal_model: CausalModelSchemaConfig,
13726    /// Default settings applied to all scenarios.
13727    #[serde(default)]
13728    pub defaults: ScenarioDefaultsConfig,
13729    /// Generate counterfactual (original, mutated) JE pairs for ML training.
13730    /// When true, the orchestrator produces paired clean/anomalous journal entries.
13731    #[serde(default)]
13732    pub generate_counterfactuals: bool,
13733}
13734
13735/// A single scenario definition in the config.
13736#[derive(Debug, Clone, Serialize, Deserialize)]
13737pub struct ScenarioSchemaConfig {
13738    /// Scenario name (must be unique).
13739    pub name: String,
13740    /// Human-readable description.
13741    #[serde(default)]
13742    pub description: String,
13743    /// Tags for categorization.
13744    #[serde(default)]
13745    pub tags: Vec<String>,
13746    /// Base scenario name (None = default config).
13747    pub base: Option<String>,
13748    /// IFRS 9-style probability weight.
13749    pub probability_weight: Option<f64>,
13750    /// List of interventions to apply.
13751    #[serde(default)]
13752    pub interventions: Vec<InterventionSchemaConfig>,
13753    /// Constraint overrides for this scenario.
13754    #[serde(default)]
13755    pub constraints: ScenarioConstraintsSchemaConfig,
13756    /// Output configuration for this scenario.
13757    #[serde(default)]
13758    pub output: ScenarioOutputSchemaConfig,
13759    /// Arbitrary metadata.
13760    #[serde(default)]
13761    pub metadata: std::collections::HashMap<String, String>,
13762}
13763
13764/// An intervention definition in the config.
13765#[derive(Debug, Clone, Serialize, Deserialize)]
13766pub struct InterventionSchemaConfig {
13767    /// Intervention type and parameters (flattened tagged enum).
13768    #[serde(flatten)]
13769    pub intervention_type: serde_json::Value,
13770    /// Timing configuration.
13771    #[serde(default)]
13772    pub timing: InterventionTimingSchemaConfig,
13773    /// Human-readable label.
13774    pub label: Option<String>,
13775    /// Priority for conflict resolution (higher wins).
13776    #[serde(default)]
13777    pub priority: u32,
13778}
13779
13780/// Timing configuration for an intervention.
13781#[derive(Debug, Clone, Serialize, Deserialize)]
13782pub struct InterventionTimingSchemaConfig {
13783    /// Month offset from start (1-indexed).
13784    #[serde(default = "default_start_month")]
13785    pub start_month: u32,
13786    /// Duration in months.
13787    pub duration_months: Option<u32>,
13788    /// Onset type: "sudden", "gradual", "oscillating", "custom".
13789    #[serde(default = "default_onset")]
13790    pub onset: String,
13791    /// Ramp period in months.
13792    pub ramp_months: Option<u32>,
13793}
13794
13795fn default_start_month() -> u32 {
13796    1
13797}
13798
13799fn default_onset() -> String {
13800    "sudden".to_string()
13801}
13802
13803impl Default for InterventionTimingSchemaConfig {
13804    fn default() -> Self {
13805        Self {
13806            start_month: 1,
13807            duration_months: None,
13808            onset: "sudden".to_string(),
13809            ramp_months: None,
13810        }
13811    }
13812}
13813
13814/// Scenario constraint overrides.
13815#[derive(Debug, Clone, Serialize, Deserialize)]
13816pub struct ScenarioConstraintsSchemaConfig {
13817    #[serde(default = "default_true")]
13818    pub preserve_accounting_identity: bool,
13819    #[serde(default = "default_true")]
13820    pub preserve_document_chains: bool,
13821    #[serde(default = "default_true")]
13822    pub preserve_period_close: bool,
13823    #[serde(default = "default_true")]
13824    pub preserve_balance_coherence: bool,
13825    #[serde(default)]
13826    pub custom: Vec<CustomConstraintSchemaConfig>,
13827}
13828
13829impl Default for ScenarioConstraintsSchemaConfig {
13830    fn default() -> Self {
13831        Self {
13832            preserve_accounting_identity: true,
13833            preserve_document_chains: true,
13834            preserve_period_close: true,
13835            preserve_balance_coherence: true,
13836            custom: Vec::new(),
13837        }
13838    }
13839}
13840
13841/// Custom constraint in config.
13842#[derive(Debug, Clone, Serialize, Deserialize)]
13843pub struct CustomConstraintSchemaConfig {
13844    pub config_path: String,
13845    pub min: Option<f64>,
13846    pub max: Option<f64>,
13847    #[serde(default)]
13848    pub description: String,
13849}
13850
13851/// Output configuration for a scenario.
13852#[derive(Debug, Clone, Serialize, Deserialize)]
13853pub struct ScenarioOutputSchemaConfig {
13854    #[serde(default = "default_true")]
13855    pub paired: bool,
13856    #[serde(default = "default_diff_formats_schema")]
13857    pub diff_formats: Vec<String>,
13858    #[serde(default)]
13859    pub diff_scope: Vec<String>,
13860}
13861
13862fn default_diff_formats_schema() -> Vec<String> {
13863    vec!["summary".to_string(), "aggregate".to_string()]
13864}
13865
13866impl Default for ScenarioOutputSchemaConfig {
13867    fn default() -> Self {
13868        Self {
13869            paired: true,
13870            diff_formats: default_diff_formats_schema(),
13871            diff_scope: Vec::new(),
13872        }
13873    }
13874}
13875
13876/// Causal model configuration.
13877#[derive(Debug, Clone, Serialize, Deserialize)]
13878pub struct CausalModelSchemaConfig {
13879    /// Preset name: "default", "minimal", or "custom".
13880    #[serde(default = "default_causal_preset")]
13881    pub preset: String,
13882    /// Custom nodes (merged with preset).
13883    #[serde(default)]
13884    pub nodes: Vec<serde_json::Value>,
13885    /// Custom edges (merged with preset).
13886    #[serde(default)]
13887    pub edges: Vec<serde_json::Value>,
13888}
13889
13890fn default_causal_preset() -> String {
13891    "default".to_string()
13892}
13893
13894impl Default for CausalModelSchemaConfig {
13895    fn default() -> Self {
13896        Self {
13897            preset: "default".to_string(),
13898            nodes: Vec::new(),
13899            edges: Vec::new(),
13900        }
13901    }
13902}
13903
13904/// Default settings applied to all scenarios.
13905#[derive(Debug, Clone, Serialize, Deserialize, Default)]
13906pub struct ScenarioDefaultsConfig {
13907    #[serde(default)]
13908    pub constraints: ScenarioConstraintsSchemaConfig,
13909    #[serde(default)]
13910    pub output: ScenarioOutputSchemaConfig,
13911}
13912
13913// =====================================================================
13914// Compliance Regulations Framework Configuration
13915// =====================================================================
13916
13917/// Top-level configuration for the compliance regulations framework.
13918///
13919/// Controls standards registry, jurisdiction profiles, temporal versioning,
13920/// audit procedure templates, compliance graph integration, and output settings.
13921///
13922/// # Example
13923///
13924/// ```yaml
13925/// compliance_regulations:
13926///   enabled: true
13927///   jurisdictions: [US, DE, GB]
13928///   reference_date: "2025-06-30"
13929///   standards_selection:
13930///     categories: [accounting, auditing, regulatory]
13931///     include: ["IFRS-16", "ASC-606"]
13932///   audit_procedures:
13933///     enabled: true
13934///     procedures_per_standard: 3
13935///   findings:
13936///     enabled: true
13937///     finding_rate: 0.05
13938///   filings:
13939///     enabled: true
13940///   graph:
13941///     enabled: true
13942///     include_compliance_nodes: true
13943///     include_compliance_edges: true
13944/// ```
13945#[derive(Debug, Clone, Default, Serialize, Deserialize)]
13946pub struct ComplianceRegulationsConfig {
13947    /// Master switch for the compliance regulations framework.
13948    #[serde(default)]
13949    pub enabled: bool,
13950    /// Jurisdictions to generate compliance data for (ISO 3166-1 alpha-2 codes).
13951    /// If empty, inferred from company countries in the config.
13952    #[serde(default)]
13953    pub jurisdictions: Vec<String>,
13954    /// Reference date for temporal standard resolution (YYYY-MM-DD).
13955    /// Defaults to the global start_date if not set.
13956    #[serde(default)]
13957    pub reference_date: Option<String>,
13958    /// Standards selection filters.
13959    #[serde(default)]
13960    pub standards_selection: StandardsSelectionConfig,
13961    /// Audit procedure generation settings.
13962    #[serde(default)]
13963    pub audit_procedures: AuditProcedureGenConfig,
13964    /// Compliance finding generation settings.
13965    #[serde(default)]
13966    pub findings: ComplianceFindingGenConfig,
13967    /// Regulatory filing generation settings.
13968    #[serde(default)]
13969    pub filings: ComplianceFilingGenConfig,
13970    /// Compliance graph integration settings.
13971    #[serde(default)]
13972    pub graph: ComplianceGraphConfig,
13973    /// Output settings for compliance-specific files.
13974    #[serde(default)]
13975    pub output: ComplianceOutputConfig,
13976    /// v3.3.0: legal-document generation (engagement letters,
13977    /// management reps, legal opinions, regulatory filings, board
13978    /// resolutions). Requires `compliance_regulations.enabled = true`
13979    /// AND `legal_documents.enabled = true` to take effect.
13980    #[serde(default)]
13981    pub legal_documents: LegalDocumentsConfig,
13982}
13983
13984/// Legal-document generation settings (v3.3.0+).
13985///
13986/// Wires `LegalDocumentGenerator` into the orchestrator. Generates one
13987/// batch per audit engagement when enabled.
13988#[derive(Debug, Clone, Serialize, Deserialize)]
13989pub struct LegalDocumentsConfig {
13990    /// Master switch.
13991    #[serde(default)]
13992    pub enabled: bool,
13993    /// Probability of including a legal-opinion document in an engagement.
13994    #[serde(default = "default_legal_opinion_probability")]
13995    pub legal_opinion_probability: f64,
13996}
13997
13998fn default_legal_opinion_probability() -> f64 {
13999    0.40
14000}
14001
14002impl Default for LegalDocumentsConfig {
14003    fn default() -> Self {
14004        Self {
14005            enabled: false,
14006            legal_opinion_probability: default_legal_opinion_probability(),
14007        }
14008    }
14009}
14010
14011/// Filters which standards are included in the generation.
14012#[derive(Debug, Clone, Default, Serialize, Deserialize)]
14013pub struct StandardsSelectionConfig {
14014    /// Standard categories to include (accounting, auditing, regulatory, tax, esg).
14015    /// Empty = all categories.
14016    #[serde(default)]
14017    pub categories: Vec<String>,
14018    /// Explicit standard IDs to include (e.g., ["IFRS-16", "ASC-606"]).
14019    /// When non-empty, only these standards (plus mandatory ones for selected jurisdictions) are used.
14020    #[serde(default)]
14021    pub include: Vec<String>,
14022    /// Standard IDs to exclude.
14023    #[serde(default)]
14024    pub exclude: Vec<String>,
14025    /// Include superseded standards in the output (for historical analysis).
14026    #[serde(default)]
14027    pub include_superseded: bool,
14028}
14029
14030/// Configuration for audit procedure template generation.
14031#[derive(Debug, Clone, Serialize, Deserialize)]
14032pub struct AuditProcedureGenConfig {
14033    /// Whether audit procedure generation is enabled.
14034    #[serde(default)]
14035    pub enabled: bool,
14036    /// Number of procedures to generate per applicable standard.
14037    #[serde(default = "default_procedures_per_standard")]
14038    pub procedures_per_standard: usize,
14039    /// Sampling methodology: "statistical", "non_statistical", "mixed".
14040    #[serde(default = "default_sampling_method")]
14041    pub sampling_method: String,
14042    /// Confidence level for statistical sampling (0.0-1.0).
14043    #[serde(default = "default_confidence_level")]
14044    pub confidence_level: f64,
14045    /// Tolerable misstatement rate for sampling (0.0-1.0).
14046    #[serde(default = "default_tolerable_misstatement")]
14047    pub tolerable_misstatement: f64,
14048}
14049
14050fn default_procedures_per_standard() -> usize {
14051    3
14052}
14053
14054fn default_sampling_method() -> String {
14055    "statistical".to_string()
14056}
14057
14058fn default_confidence_level() -> f64 {
14059    0.95
14060}
14061
14062fn default_tolerable_misstatement() -> f64 {
14063    0.05
14064}
14065
14066impl Default for AuditProcedureGenConfig {
14067    fn default() -> Self {
14068        Self {
14069            enabled: false,
14070            procedures_per_standard: default_procedures_per_standard(),
14071            sampling_method: default_sampling_method(),
14072            confidence_level: default_confidence_level(),
14073            tolerable_misstatement: default_tolerable_misstatement(),
14074        }
14075    }
14076}
14077
14078/// Configuration for compliance finding generation.
14079#[derive(Debug, Clone, Serialize, Deserialize)]
14080pub struct ComplianceFindingGenConfig {
14081    /// Whether finding generation is enabled.
14082    #[serde(default)]
14083    pub enabled: bool,
14084    /// Rate of findings per audit procedure (0.0-1.0).
14085    #[serde(default = "default_finding_rate")]
14086    pub finding_rate: f64,
14087    /// Rate of material weakness findings among all findings (0.0-1.0).
14088    #[serde(default = "default_cr_material_weakness_rate")]
14089    pub material_weakness_rate: f64,
14090    /// Rate of significant deficiency findings among all findings (0.0-1.0).
14091    #[serde(default = "default_cr_significant_deficiency_rate")]
14092    pub significant_deficiency_rate: f64,
14093    /// Whether to generate remediation plans for findings.
14094    #[serde(default = "default_true")]
14095    pub generate_remediation: bool,
14096}
14097
14098fn default_finding_rate() -> f64 {
14099    0.05
14100}
14101
14102fn default_cr_material_weakness_rate() -> f64 {
14103    0.02
14104}
14105
14106fn default_cr_significant_deficiency_rate() -> f64 {
14107    0.08
14108}
14109
14110impl Default for ComplianceFindingGenConfig {
14111    fn default() -> Self {
14112        Self {
14113            enabled: false,
14114            finding_rate: default_finding_rate(),
14115            material_weakness_rate: default_cr_material_weakness_rate(),
14116            significant_deficiency_rate: default_cr_significant_deficiency_rate(),
14117            generate_remediation: true,
14118        }
14119    }
14120}
14121
14122/// Configuration for regulatory filing generation.
14123#[derive(Debug, Clone, Serialize, Deserialize)]
14124pub struct ComplianceFilingGenConfig {
14125    /// Whether filing generation is enabled.
14126    #[serde(default)]
14127    pub enabled: bool,
14128    /// Filing types to include (e.g., ["10-K", "10-Q", "Jahresabschluss"]).
14129    /// Empty = all applicable filings for the selected jurisdictions.
14130    #[serde(default)]
14131    pub filing_types: Vec<String>,
14132    /// Generate filing status progression (draft → filed → accepted).
14133    #[serde(default = "default_true")]
14134    pub generate_status_progression: bool,
14135}
14136
14137impl Default for ComplianceFilingGenConfig {
14138    fn default() -> Self {
14139        Self {
14140            enabled: false,
14141            filing_types: Vec::new(),
14142            generate_status_progression: true,
14143        }
14144    }
14145}
14146
14147/// Configuration for compliance graph integration.
14148#[derive(Debug, Clone, Serialize, Deserialize)]
14149pub struct ComplianceGraphConfig {
14150    /// Whether compliance graph integration is enabled.
14151    #[serde(default)]
14152    pub enabled: bool,
14153    /// Include compliance nodes (Standard, Regulation, Jurisdiction, etc.).
14154    #[serde(default = "default_true")]
14155    pub include_compliance_nodes: bool,
14156    /// Include compliance edges (MapsToStandard, TestsControl, etc.).
14157    #[serde(default = "default_true")]
14158    pub include_compliance_edges: bool,
14159    /// Include cross-reference edges between standards.
14160    #[serde(default = "default_true")]
14161    pub include_cross_references: bool,
14162    /// Include temporal supersession edges.
14163    #[serde(default)]
14164    pub include_supersession_edges: bool,
14165    /// Include edges linking standards to the GL account types they govern.
14166    #[serde(default = "default_true")]
14167    pub include_account_links: bool,
14168    /// Include edges linking standards to the internal controls that implement them.
14169    #[serde(default = "default_true")]
14170    pub include_control_links: bool,
14171    /// Include edges linking filings and jurisdictions to the originating company.
14172    #[serde(default = "default_true")]
14173    pub include_company_links: bool,
14174}
14175
14176impl Default for ComplianceGraphConfig {
14177    fn default() -> Self {
14178        Self {
14179            enabled: false,
14180            include_compliance_nodes: true,
14181            include_compliance_edges: true,
14182            include_cross_references: true,
14183            include_supersession_edges: false,
14184            include_account_links: true,
14185            include_control_links: true,
14186            include_company_links: true,
14187        }
14188    }
14189}
14190
14191/// Output settings for compliance-specific data files.
14192#[derive(Debug, Clone, Serialize, Deserialize)]
14193pub struct ComplianceOutputConfig {
14194    /// Export the standards registry catalog.
14195    #[serde(default = "default_true")]
14196    pub export_registry: bool,
14197    /// Export jurisdiction profiles.
14198    #[serde(default = "default_true")]
14199    pub export_jurisdictions: bool,
14200    /// Export cross-reference map.
14201    #[serde(default = "default_true")]
14202    pub export_cross_references: bool,
14203    /// Export temporal version history.
14204    #[serde(default)]
14205    pub export_version_history: bool,
14206}
14207
14208impl Default for ComplianceOutputConfig {
14209    fn default() -> Self {
14210        Self {
14211            export_registry: true,
14212            export_jurisdictions: true,
14213            export_cross_references: true,
14214            export_version_history: false,
14215        }
14216    }
14217}
14218
14219#[cfg(test)]
14220#[allow(clippy::unwrap_used)]
14221mod tests {
14222    use super::*;
14223    use crate::presets::demo_preset;
14224
14225    // ==========================================================================
14226    // Serialization/Deserialization Tests
14227    // ==========================================================================
14228
14229    #[test]
14230    fn test_config_yaml_roundtrip() {
14231        let config = demo_preset();
14232        let yaml = serde_yaml::to_string(&config).expect("Failed to serialize to YAML");
14233        let deserialized: GeneratorConfig =
14234            serde_yaml::from_str(&yaml).expect("Failed to deserialize from YAML");
14235
14236        assert_eq!(
14237            config.global.period_months,
14238            deserialized.global.period_months
14239        );
14240        assert_eq!(config.global.industry, deserialized.global.industry);
14241        assert_eq!(config.companies.len(), deserialized.companies.len());
14242        assert_eq!(config.companies[0].code, deserialized.companies[0].code);
14243    }
14244
14245    #[test]
14246    fn test_config_json_roundtrip() {
14247        // Create a config without infinity values (JSON can't serialize f64::INFINITY)
14248        let mut config = demo_preset();
14249        // Replace infinity with a large but finite value for JSON compatibility
14250        config.master_data.employees.approval_limits.executive = 1e12;
14251
14252        let json = serde_json::to_string(&config).expect("Failed to serialize to JSON");
14253        let deserialized: GeneratorConfig =
14254            serde_json::from_str(&json).expect("Failed to deserialize from JSON");
14255
14256        assert_eq!(
14257            config.global.period_months,
14258            deserialized.global.period_months
14259        );
14260        assert_eq!(config.global.industry, deserialized.global.industry);
14261        assert_eq!(config.companies.len(), deserialized.companies.len());
14262    }
14263
14264    #[test]
14265    fn test_transaction_volume_serialization() {
14266        // Test various transaction volumes serialize correctly
14267        let volumes = vec![
14268            (TransactionVolume::TenK, "ten_k"),
14269            (TransactionVolume::HundredK, "hundred_k"),
14270            (TransactionVolume::OneM, "one_m"),
14271            (TransactionVolume::TenM, "ten_m"),
14272            (TransactionVolume::HundredM, "hundred_m"),
14273        ];
14274
14275        for (volume, expected_key) in volumes {
14276            let json = serde_json::to_string(&volume).expect("Failed to serialize");
14277            assert!(
14278                json.contains(expected_key),
14279                "Expected {} in JSON: {}",
14280                expected_key,
14281                json
14282            );
14283        }
14284    }
14285
14286    #[test]
14287    fn test_transaction_volume_custom_serialization() {
14288        let volume = TransactionVolume::Custom(12345);
14289        let json = serde_json::to_string(&volume).expect("Failed to serialize");
14290        let deserialized: TransactionVolume =
14291            serde_json::from_str(&json).expect("Failed to deserialize");
14292        assert_eq!(deserialized.count(), 12345);
14293    }
14294
14295    #[test]
14296    fn test_output_mode_serialization() {
14297        let modes = vec![
14298            OutputMode::Streaming,
14299            OutputMode::FlatFile,
14300            OutputMode::Both,
14301        ];
14302
14303        for mode in modes {
14304            let json = serde_json::to_string(&mode).expect("Failed to serialize");
14305            let deserialized: OutputMode =
14306                serde_json::from_str(&json).expect("Failed to deserialize");
14307            assert!(format!("{:?}", mode) == format!("{:?}", deserialized));
14308        }
14309    }
14310
14311    #[test]
14312    fn test_file_format_serialization() {
14313        let formats = vec![
14314            FileFormat::Csv,
14315            FileFormat::Parquet,
14316            FileFormat::Json,
14317            FileFormat::JsonLines,
14318        ];
14319
14320        for format in formats {
14321            let json = serde_json::to_string(&format).expect("Failed to serialize");
14322            let deserialized: FileFormat =
14323                serde_json::from_str(&json).expect("Failed to deserialize");
14324            assert!(format!("{:?}", format) == format!("{:?}", deserialized));
14325        }
14326    }
14327
14328    #[test]
14329    fn test_compression_algorithm_serialization() {
14330        let algos = vec![
14331            CompressionAlgorithm::Gzip,
14332            CompressionAlgorithm::Zstd,
14333            CompressionAlgorithm::Lz4,
14334            CompressionAlgorithm::Snappy,
14335        ];
14336
14337        for algo in algos {
14338            let json = serde_json::to_string(&algo).expect("Failed to serialize");
14339            let deserialized: CompressionAlgorithm =
14340                serde_json::from_str(&json).expect("Failed to deserialize");
14341            assert!(format!("{:?}", algo) == format!("{:?}", deserialized));
14342        }
14343    }
14344
14345    #[test]
14346    fn test_transfer_pricing_method_serialization() {
14347        let methods = vec![
14348            TransferPricingMethod::CostPlus,
14349            TransferPricingMethod::ComparableUncontrolled,
14350            TransferPricingMethod::ResalePrice,
14351            TransferPricingMethod::TransactionalNetMargin,
14352            TransferPricingMethod::ProfitSplit,
14353        ];
14354
14355        for method in methods {
14356            let json = serde_json::to_string(&method).expect("Failed to serialize");
14357            let deserialized: TransferPricingMethod =
14358                serde_json::from_str(&json).expect("Failed to deserialize");
14359            assert!(format!("{:?}", method) == format!("{:?}", deserialized));
14360        }
14361    }
14362
14363    #[test]
14364    fn test_benford_exemption_serialization() {
14365        let exemptions = vec![
14366            BenfordExemption::Recurring,
14367            BenfordExemption::Payroll,
14368            BenfordExemption::FixedFees,
14369            BenfordExemption::RoundAmounts,
14370        ];
14371
14372        for exemption in exemptions {
14373            let json = serde_json::to_string(&exemption).expect("Failed to serialize");
14374            let deserialized: BenfordExemption =
14375                serde_json::from_str(&json).expect("Failed to deserialize");
14376            assert!(format!("{:?}", exemption) == format!("{:?}", deserialized));
14377        }
14378    }
14379
14380    // ==========================================================================
14381    // Default Value Tests
14382    // ==========================================================================
14383
14384    #[test]
14385    fn test_global_config_defaults() {
14386        let yaml = r#"
14387            industry: manufacturing
14388            start_date: "2024-01-01"
14389            period_months: 6
14390        "#;
14391        let config: GlobalConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14392        assert_eq!(config.group_currency, "USD");
14393        assert!(config.parallel);
14394        assert_eq!(config.worker_threads, 0);
14395        assert_eq!(config.memory_limit_mb, 0);
14396    }
14397
14398    #[test]
14399    fn test_fraud_config_defaults() {
14400        let config = FraudConfig::default();
14401        assert!(!config.enabled);
14402        assert_eq!(config.fraud_rate, 0.005);
14403        assert!(!config.clustering_enabled);
14404    }
14405
14406    #[test]
14407    fn test_internal_controls_config_defaults() {
14408        let config = InternalControlsConfig::default();
14409        assert!(!config.enabled);
14410        assert_eq!(config.exception_rate, 0.02);
14411        assert_eq!(config.sod_violation_rate, 0.01);
14412        assert!(config.export_control_master_data);
14413        assert_eq!(config.sox_materiality_threshold, 10000.0);
14414        // COSO fields
14415        assert!(config.coso_enabled);
14416        assert!(!config.include_entity_level_controls);
14417        assert_eq!(config.target_maturity_level, "mixed");
14418    }
14419
14420    #[test]
14421    fn test_output_config_defaults() {
14422        let config = OutputConfig::default();
14423        assert!(matches!(config.mode, OutputMode::FlatFile));
14424        assert_eq!(config.formats, vec![FileFormat::Parquet]);
14425        assert!(config.compression.enabled);
14426        assert!(matches!(
14427            config.compression.algorithm,
14428            CompressionAlgorithm::Zstd
14429        ));
14430        assert!(config.include_acdoca);
14431        assert!(!config.include_bseg);
14432        assert!(config.partition_by_period);
14433        assert!(!config.partition_by_company);
14434    }
14435
14436    #[test]
14437    fn test_approval_config_defaults() {
14438        let config = ApprovalConfig::default();
14439        assert!(!config.enabled);
14440        assert_eq!(config.auto_approve_threshold, 1000.0);
14441        assert_eq!(config.rejection_rate, 0.02);
14442        assert_eq!(config.revision_rate, 0.05);
14443        assert_eq!(config.average_approval_delay_hours, 4.0);
14444        assert_eq!(config.thresholds.len(), 4);
14445    }
14446
14447    #[test]
14448    fn test_p2p_flow_config_defaults() {
14449        let config = P2PFlowConfig::default();
14450        assert!(config.enabled);
14451        assert_eq!(config.three_way_match_rate, 0.95);
14452        assert_eq!(config.partial_delivery_rate, 0.15);
14453        assert_eq!(config.average_po_to_gr_days, 14);
14454    }
14455
14456    #[test]
14457    fn test_o2c_flow_config_defaults() {
14458        let config = O2CFlowConfig::default();
14459        assert!(config.enabled);
14460        assert_eq!(config.credit_check_failure_rate, 0.02);
14461        assert_eq!(config.return_rate, 0.03);
14462        assert_eq!(config.bad_debt_rate, 0.01);
14463    }
14464
14465    #[test]
14466    fn test_balance_config_defaults() {
14467        let config = BalanceConfig::default();
14468        assert!(!config.generate_opening_balances);
14469        assert!(config.generate_trial_balances);
14470        assert_eq!(config.target_gross_margin, 0.35);
14471        assert!(config.validate_balance_equation);
14472        assert!(config.reconcile_subledgers);
14473    }
14474
14475    // ==========================================================================
14476    // Partial Config Deserialization Tests
14477    // ==========================================================================
14478
14479    #[test]
14480    fn test_partial_config_with_defaults() {
14481        // Minimal config that should use all defaults
14482        let yaml = r#"
14483            global:
14484              industry: manufacturing
14485              start_date: "2024-01-01"
14486              period_months: 3
14487            companies:
14488              - code: "TEST"
14489                name: "Test Company"
14490                currency: "USD"
14491                country: "US"
14492                annual_transaction_volume: ten_k
14493            chart_of_accounts:
14494              complexity: small
14495            output:
14496              output_directory: "./output"
14497        "#;
14498
14499        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14500        assert_eq!(config.global.period_months, 3);
14501        assert_eq!(config.companies.len(), 1);
14502        assert!(!config.fraud.enabled); // Default
14503        assert!(!config.internal_controls.enabled); // Default
14504    }
14505
14506    #[test]
14507    fn test_config_with_fraud_enabled() {
14508        let yaml = r#"
14509            global:
14510              industry: retail
14511              start_date: "2024-01-01"
14512              period_months: 12
14513            companies:
14514              - code: "RETAIL"
14515                name: "Retail Co"
14516                currency: "USD"
14517                country: "US"
14518                annual_transaction_volume: hundred_k
14519            chart_of_accounts:
14520              complexity: medium
14521            output:
14522              output_directory: "./output"
14523            fraud:
14524              enabled: true
14525              fraud_rate: 0.05
14526              clustering_enabled: true
14527        "#;
14528
14529        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14530        assert!(config.fraud.enabled);
14531        assert_eq!(config.fraud.fraud_rate, 0.05);
14532        assert!(config.fraud.clustering_enabled);
14533    }
14534
14535    #[test]
14536    fn test_config_with_multiple_companies() {
14537        let yaml = r#"
14538            global:
14539              industry: manufacturing
14540              start_date: "2024-01-01"
14541              period_months: 6
14542            companies:
14543              - code: "HQ"
14544                name: "Headquarters"
14545                currency: "USD"
14546                country: "US"
14547                annual_transaction_volume: hundred_k
14548                volume_weight: 1.0
14549              - code: "EU"
14550                name: "European Subsidiary"
14551                currency: "EUR"
14552                country: "DE"
14553                annual_transaction_volume: hundred_k
14554                volume_weight: 0.5
14555              - code: "APAC"
14556                name: "Asia Pacific"
14557                currency: "JPY"
14558                country: "JP"
14559                annual_transaction_volume: ten_k
14560                volume_weight: 0.3
14561            chart_of_accounts:
14562              complexity: large
14563            output:
14564              output_directory: "./output"
14565        "#;
14566
14567        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14568        assert_eq!(config.companies.len(), 3);
14569        assert_eq!(config.companies[0].code, "HQ");
14570        assert_eq!(config.companies[1].currency, "EUR");
14571        assert_eq!(config.companies[2].volume_weight, 0.3);
14572    }
14573
14574    #[test]
14575    fn test_intercompany_config() {
14576        let yaml = r#"
14577            enabled: true
14578            ic_transaction_rate: 0.20
14579            transfer_pricing_method: cost_plus
14580            markup_percent: 0.08
14581            generate_matched_pairs: true
14582            generate_eliminations: true
14583        "#;
14584
14585        let config: IntercompanyConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14586        assert!(config.enabled);
14587        assert_eq!(config.ic_transaction_rate, 0.20);
14588        assert!(matches!(
14589            config.transfer_pricing_method,
14590            TransferPricingMethod::CostPlus
14591        ));
14592        assert_eq!(config.markup_percent, 0.08);
14593        assert!(config.generate_eliminations);
14594    }
14595
14596    // ==========================================================================
14597    // Company Config Tests
14598    // ==========================================================================
14599
14600    #[test]
14601    fn test_company_config_defaults() {
14602        let yaml = r#"
14603            code: "TEST"
14604            name: "Test Company"
14605            currency: "USD"
14606            country: "US"
14607            annual_transaction_volume: ten_k
14608        "#;
14609
14610        let config: CompanyConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14611        assert_eq!(config.fiscal_year_variant, "K4"); // Default
14612        assert_eq!(config.volume_weight, 1.0); // Default
14613    }
14614
14615    // ==========================================================================
14616    // Chart of Accounts Config Tests
14617    // ==========================================================================
14618
14619    #[test]
14620    fn test_coa_config_defaults() {
14621        let yaml = r#"
14622            complexity: medium
14623        "#;
14624
14625        let config: ChartOfAccountsConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14626        assert!(config.industry_specific); // Default true
14627        assert!(config.custom_accounts.is_none());
14628        assert_eq!(config.min_hierarchy_depth, 2); // Default
14629        assert_eq!(config.max_hierarchy_depth, 5); // Default
14630    }
14631
14632    // ==========================================================================
14633    // Accounting Standards Config Tests
14634    // ==========================================================================
14635
14636    #[test]
14637    fn test_accounting_standards_config_defaults() {
14638        let config = AccountingStandardsConfig::default();
14639        assert!(!config.enabled);
14640        assert!(config.framework.is_none());
14641        assert!(!config.revenue_recognition.enabled);
14642        assert!(!config.leases.enabled);
14643        assert!(!config.fair_value.enabled);
14644        assert!(!config.impairment.enabled);
14645        assert!(!config.generate_differences);
14646    }
14647
14648    #[test]
14649    fn test_accounting_standards_config_yaml() {
14650        let yaml = r#"
14651            enabled: true
14652            framework: ifrs
14653            revenue_recognition:
14654              enabled: true
14655              generate_contracts: true
14656              avg_obligations_per_contract: 2.5
14657              variable_consideration_rate: 0.20
14658              over_time_recognition_rate: 0.35
14659              contract_count: 150
14660            leases:
14661              enabled: true
14662              lease_count: 75
14663              finance_lease_percent: 0.25
14664              avg_lease_term_months: 48
14665            generate_differences: true
14666        "#;
14667
14668        let config: AccountingStandardsConfig =
14669            serde_yaml::from_str(yaml).expect("Failed to parse");
14670        assert!(config.enabled);
14671        assert!(matches!(
14672            config.framework,
14673            Some(AccountingFrameworkConfig::Ifrs)
14674        ));
14675        assert!(config.revenue_recognition.enabled);
14676        assert_eq!(config.revenue_recognition.contract_count, 150);
14677        assert_eq!(config.revenue_recognition.avg_obligations_per_contract, 2.5);
14678        assert!(config.leases.enabled);
14679        assert_eq!(config.leases.lease_count, 75);
14680        assert_eq!(config.leases.finance_lease_percent, 0.25);
14681        assert!(config.generate_differences);
14682    }
14683
14684    #[test]
14685    fn test_accounting_framework_serialization() {
14686        let frameworks = [
14687            AccountingFrameworkConfig::UsGaap,
14688            AccountingFrameworkConfig::Ifrs,
14689            AccountingFrameworkConfig::DualReporting,
14690            AccountingFrameworkConfig::FrenchGaap,
14691            AccountingFrameworkConfig::GermanGaap,
14692        ];
14693
14694        for framework in frameworks {
14695            let json = serde_json::to_string(&framework).expect("Failed to serialize");
14696            let deserialized: AccountingFrameworkConfig =
14697                serde_json::from_str(&json).expect("Failed to deserialize");
14698            assert!(format!("{:?}", framework) == format!("{:?}", deserialized));
14699        }
14700    }
14701
14702    #[test]
14703    fn test_revenue_recognition_config_defaults() {
14704        let config = RevenueRecognitionConfig::default();
14705        assert!(!config.enabled);
14706        assert!(config.generate_contracts);
14707        assert_eq!(config.avg_obligations_per_contract, 2.0);
14708        assert_eq!(config.variable_consideration_rate, 0.15);
14709        assert_eq!(config.over_time_recognition_rate, 0.30);
14710        assert_eq!(config.contract_count, 100);
14711    }
14712
14713    #[test]
14714    fn test_lease_accounting_config_defaults() {
14715        let config = LeaseAccountingConfig::default();
14716        assert!(!config.enabled);
14717        assert_eq!(config.lease_count, 50);
14718        assert_eq!(config.finance_lease_percent, 0.30);
14719        assert_eq!(config.avg_lease_term_months, 60);
14720        assert!(config.generate_amortization);
14721        assert_eq!(config.real_estate_percent, 0.40);
14722    }
14723
14724    #[test]
14725    fn test_fair_value_config_defaults() {
14726        let config = FairValueConfig::default();
14727        assert!(!config.enabled);
14728        assert_eq!(config.measurement_count, 25);
14729        assert_eq!(config.level1_percent, 0.40);
14730        assert_eq!(config.level2_percent, 0.35);
14731        assert_eq!(config.level3_percent, 0.25);
14732        assert!(!config.include_sensitivity_analysis);
14733    }
14734
14735    #[test]
14736    fn test_impairment_config_defaults() {
14737        let config = ImpairmentConfig::default();
14738        assert!(!config.enabled);
14739        assert_eq!(config.test_count, 15);
14740        assert_eq!(config.impairment_rate, 0.10);
14741        assert!(config.generate_projections);
14742        assert!(!config.include_goodwill);
14743    }
14744
14745    // ==========================================================================
14746    // Audit Standards Config Tests
14747    // ==========================================================================
14748
14749    #[test]
14750    fn test_audit_standards_config_defaults() {
14751        let config = AuditStandardsConfig::default();
14752        assert!(!config.enabled);
14753        assert!(!config.isa_compliance.enabled);
14754        assert!(!config.analytical_procedures.enabled);
14755        assert!(!config.confirmations.enabled);
14756        assert!(!config.opinion.enabled);
14757        assert!(!config.generate_audit_trail);
14758        assert!(!config.sox.enabled);
14759        assert!(!config.pcaob.enabled);
14760    }
14761
14762    #[test]
14763    fn test_audit_standards_config_yaml() {
14764        let yaml = r#"
14765            enabled: true
14766            isa_compliance:
14767              enabled: true
14768              compliance_level: comprehensive
14769              generate_isa_mappings: true
14770              include_pcaob: true
14771              framework: dual
14772            analytical_procedures:
14773              enabled: true
14774              procedures_per_account: 5
14775              variance_probability: 0.25
14776            confirmations:
14777              enabled: true
14778              confirmation_count: 75
14779              positive_response_rate: 0.90
14780              exception_rate: 0.08
14781            opinion:
14782              enabled: true
14783              generate_kam: true
14784              average_kam_count: 4
14785            sox:
14786              enabled: true
14787              generate_302_certifications: true
14788              generate_404_assessments: true
14789              material_weakness_rate: 0.03
14790            pcaob:
14791              enabled: true
14792              is_pcaob_audit: true
14793              include_icfr_opinion: true
14794            generate_audit_trail: true
14795        "#;
14796
14797        let config: AuditStandardsConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14798        assert!(config.enabled);
14799        assert!(config.isa_compliance.enabled);
14800        assert_eq!(config.isa_compliance.compliance_level, "comprehensive");
14801        assert!(config.isa_compliance.include_pcaob);
14802        assert_eq!(config.isa_compliance.framework, "dual");
14803        assert!(config.analytical_procedures.enabled);
14804        assert_eq!(config.analytical_procedures.procedures_per_account, 5);
14805        assert!(config.confirmations.enabled);
14806        assert_eq!(config.confirmations.confirmation_count, 75);
14807        assert!(config.opinion.enabled);
14808        assert_eq!(config.opinion.average_kam_count, 4);
14809        assert!(config.sox.enabled);
14810        assert!(config.sox.generate_302_certifications);
14811        assert_eq!(config.sox.material_weakness_rate, 0.03);
14812        assert!(config.pcaob.enabled);
14813        assert!(config.pcaob.is_pcaob_audit);
14814        assert!(config.pcaob.include_icfr_opinion);
14815        assert!(config.generate_audit_trail);
14816    }
14817
14818    #[test]
14819    fn test_isa_compliance_config_defaults() {
14820        let config = IsaComplianceConfig::default();
14821        assert!(!config.enabled);
14822        assert_eq!(config.compliance_level, "standard");
14823        assert!(config.generate_isa_mappings);
14824        assert!(config.generate_coverage_summary);
14825        assert!(!config.include_pcaob);
14826        assert_eq!(config.framework, "isa");
14827    }
14828
14829    #[test]
14830    fn test_sox_compliance_config_defaults() {
14831        let config = SoxComplianceConfig::default();
14832        assert!(!config.enabled);
14833        assert!(config.generate_302_certifications);
14834        assert!(config.generate_404_assessments);
14835        assert_eq!(config.materiality_threshold, 10000.0);
14836        assert_eq!(config.material_weakness_rate, 0.02);
14837        assert_eq!(config.significant_deficiency_rate, 0.08);
14838    }
14839
14840    #[test]
14841    fn test_pcaob_config_defaults() {
14842        let config = PcaobConfig::default();
14843        assert!(!config.enabled);
14844        assert!(!config.is_pcaob_audit);
14845        assert!(config.generate_cam);
14846        assert!(!config.include_icfr_opinion);
14847        assert!(!config.generate_standard_mappings);
14848    }
14849
14850    #[test]
14851    fn test_config_with_standards_enabled() {
14852        let yaml = r#"
14853            global:
14854              industry: financial_services
14855              start_date: "2024-01-01"
14856              period_months: 12
14857            companies:
14858              - code: "BANK"
14859                name: "Test Bank"
14860                currency: "USD"
14861                country: "US"
14862                annual_transaction_volume: hundred_k
14863            chart_of_accounts:
14864              complexity: large
14865            output:
14866              output_directory: "./output"
14867            accounting_standards:
14868              enabled: true
14869              framework: us_gaap
14870              revenue_recognition:
14871                enabled: true
14872              leases:
14873                enabled: true
14874            audit_standards:
14875              enabled: true
14876              isa_compliance:
14877                enabled: true
14878              sox:
14879                enabled: true
14880        "#;
14881
14882        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14883        assert!(config.accounting_standards.enabled);
14884        assert!(matches!(
14885            config.accounting_standards.framework,
14886            Some(AccountingFrameworkConfig::UsGaap)
14887        ));
14888        assert!(config.accounting_standards.revenue_recognition.enabled);
14889        assert!(config.accounting_standards.leases.enabled);
14890        assert!(config.audit_standards.enabled);
14891        assert!(config.audit_standards.isa_compliance.enabled);
14892        assert!(config.audit_standards.sox.enabled);
14893    }
14894
14895    // ==========================================================================
14896    // Industry-Specific Config Tests
14897    // ==========================================================================
14898
14899    #[test]
14900    fn test_industry_specific_config_defaults() {
14901        let config = IndustrySpecificConfig::default();
14902        assert!(!config.enabled);
14903        assert!(!config.manufacturing.enabled);
14904        assert!(!config.retail.enabled);
14905        assert!(!config.healthcare.enabled);
14906        assert!(!config.technology.enabled);
14907        assert!(!config.financial_services.enabled);
14908        assert!(!config.professional_services.enabled);
14909    }
14910
14911    #[test]
14912    fn test_manufacturing_config_defaults() {
14913        let config = ManufacturingConfig::default();
14914        assert!(!config.enabled);
14915        assert_eq!(config.bom_depth, 4);
14916        assert!(!config.just_in_time);
14917        assert_eq!(config.supplier_tiers, 2);
14918        assert_eq!(config.target_yield_rate, 0.97);
14919        assert_eq!(config.scrap_alert_threshold, 0.03);
14920    }
14921
14922    #[test]
14923    fn test_retail_config_defaults() {
14924        let config = RetailConfig::default();
14925        assert!(!config.enabled);
14926        assert_eq!(config.avg_daily_transactions, 500);
14927        assert!(config.loss_prevention);
14928        assert_eq!(config.shrinkage_rate, 0.015);
14929    }
14930
14931    #[test]
14932    fn test_healthcare_config_defaults() {
14933        let config = HealthcareConfig::default();
14934        assert!(!config.enabled);
14935        assert_eq!(config.facility_type, "hospital");
14936        assert_eq!(config.avg_daily_encounters, 150);
14937        assert!(config.compliance.hipaa);
14938        assert!(config.compliance.stark_law);
14939        assert!(config.coding_systems.icd10);
14940        assert!(config.coding_systems.cpt);
14941    }
14942
14943    #[test]
14944    fn test_technology_config_defaults() {
14945        let config = TechnologyConfig::default();
14946        assert!(!config.enabled);
14947        assert_eq!(config.revenue_model, "saas");
14948        assert_eq!(config.subscription_revenue_pct, 0.60);
14949        assert!(config.rd_capitalization.enabled);
14950    }
14951
14952    #[test]
14953    fn test_config_with_industry_specific() {
14954        let yaml = r#"
14955            global:
14956              industry: healthcare
14957              start_date: "2024-01-01"
14958              period_months: 12
14959            companies:
14960              - code: "HOSP"
14961                name: "Test Hospital"
14962                currency: "USD"
14963                country: "US"
14964                annual_transaction_volume: hundred_k
14965            chart_of_accounts:
14966              complexity: medium
14967            output:
14968              output_directory: "./output"
14969            industry_specific:
14970              enabled: true
14971              healthcare:
14972                enabled: true
14973                facility_type: hospital
14974                payer_mix:
14975                  medicare: 0.45
14976                  medicaid: 0.15
14977                  commercial: 0.35
14978                  self_pay: 0.05
14979                coding_systems:
14980                  icd10: true
14981                  cpt: true
14982                  drg: true
14983                compliance:
14984                  hipaa: true
14985                  stark_law: true
14986                anomaly_rates:
14987                  upcoding: 0.03
14988                  unbundling: 0.02
14989        "#;
14990
14991        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14992        assert!(config.industry_specific.enabled);
14993        assert!(config.industry_specific.healthcare.enabled);
14994        assert_eq!(
14995            config.industry_specific.healthcare.facility_type,
14996            "hospital"
14997        );
14998        assert_eq!(config.industry_specific.healthcare.payer_mix.medicare, 0.45);
14999        assert_eq!(config.industry_specific.healthcare.payer_mix.self_pay, 0.05);
15000        assert!(config.industry_specific.healthcare.coding_systems.icd10);
15001        assert!(config.industry_specific.healthcare.compliance.hipaa);
15002        assert_eq!(
15003            config.industry_specific.healthcare.anomaly_rates.upcoding,
15004            0.03
15005        );
15006    }
15007
15008    #[test]
15009    fn test_config_with_manufacturing_specific() {
15010        let yaml = r#"
15011            global:
15012              industry: manufacturing
15013              start_date: "2024-01-01"
15014              period_months: 12
15015            companies:
15016              - code: "MFG"
15017                name: "Test Manufacturing"
15018                currency: "USD"
15019                country: "US"
15020                annual_transaction_volume: hundred_k
15021            chart_of_accounts:
15022              complexity: medium
15023            output:
15024              output_directory: "./output"
15025            industry_specific:
15026              enabled: true
15027              manufacturing:
15028                enabled: true
15029                bom_depth: 5
15030                just_in_time: true
15031                supplier_tiers: 3
15032                target_yield_rate: 0.98
15033                anomaly_rates:
15034                  yield_manipulation: 0.02
15035                  phantom_production: 0.01
15036        "#;
15037
15038        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
15039        assert!(config.industry_specific.enabled);
15040        assert!(config.industry_specific.manufacturing.enabled);
15041        assert_eq!(config.industry_specific.manufacturing.bom_depth, 5);
15042        assert!(config.industry_specific.manufacturing.just_in_time);
15043        assert_eq!(config.industry_specific.manufacturing.supplier_tiers, 3);
15044        assert_eq!(
15045            config.industry_specific.manufacturing.target_yield_rate,
15046            0.98
15047        );
15048        assert_eq!(
15049            config
15050                .industry_specific
15051                .manufacturing
15052                .anomaly_rates
15053                .yield_manipulation,
15054            0.02
15055        );
15056    }
15057
15058    // ==========================================================================
15059    // Tax Configuration Tests
15060    // ==========================================================================
15061
15062    #[test]
15063    fn test_tax_config_defaults() {
15064        let tax = TaxConfig::default();
15065        assert!(!tax.enabled);
15066        assert!(tax.jurisdictions.countries.is_empty());
15067        assert!(!tax.jurisdictions.include_subnational);
15068        assert!(!tax.vat_gst.enabled);
15069        assert!(tax.vat_gst.standard_rates.is_empty());
15070        assert!(tax.vat_gst.reduced_rates.is_empty());
15071        assert!(tax.vat_gst.exempt_categories.is_empty());
15072        assert!(tax.vat_gst.reverse_charge);
15073        assert!(!tax.sales_tax.enabled);
15074        assert!(tax.sales_tax.nexus_states.is_empty());
15075        assert!(!tax.withholding.enabled);
15076        assert!(tax.withholding.treaty_network);
15077        assert_eq!(tax.withholding.default_rate, 0.30);
15078        assert_eq!(tax.withholding.treaty_reduced_rate, 0.15);
15079        assert!(tax.provisions.enabled);
15080        assert_eq!(tax.provisions.statutory_rate, 0.21);
15081        assert!(tax.provisions.uncertain_positions);
15082        assert!(!tax.payroll_tax.enabled);
15083        assert_eq!(tax.anomaly_rate, 0.03);
15084    }
15085
15086    #[test]
15087    fn test_tax_config_from_yaml() {
15088        let yaml = r#"
15089            global:
15090              seed: 42
15091              start_date: "2024-01-01"
15092              period_months: 12
15093              industry: retail
15094            companies:
15095              - code: C001
15096                name: Test Corp
15097                currency: USD
15098                country: US
15099                annual_transaction_volume: ten_k
15100            chart_of_accounts:
15101              complexity: small
15102            output:
15103              output_directory: ./output
15104            tax:
15105              enabled: true
15106              anomaly_rate: 0.05
15107              jurisdictions:
15108                countries: ["US", "DE", "GB"]
15109                include_subnational: true
15110              vat_gst:
15111                enabled: true
15112                standard_rates:
15113                  DE: 0.19
15114                  GB: 0.20
15115                reduced_rates:
15116                  DE: 0.07
15117                  GB: 0.05
15118                exempt_categories:
15119                  - financial_services
15120                  - healthcare
15121                reverse_charge: false
15122              sales_tax:
15123                enabled: true
15124                nexus_states: ["CA", "NY", "TX"]
15125              withholding:
15126                enabled: true
15127                treaty_network: false
15128                default_rate: 0.25
15129                treaty_reduced_rate: 0.10
15130              provisions:
15131                enabled: false
15132                statutory_rate: 0.28
15133                uncertain_positions: false
15134              payroll_tax:
15135                enabled: true
15136        "#;
15137
15138        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
15139        assert!(config.tax.enabled);
15140        assert_eq!(config.tax.anomaly_rate, 0.05);
15141
15142        // Jurisdictions
15143        assert_eq!(config.tax.jurisdictions.countries.len(), 3);
15144        assert!(config
15145            .tax
15146            .jurisdictions
15147            .countries
15148            .contains(&"DE".to_string()));
15149        assert!(config.tax.jurisdictions.include_subnational);
15150
15151        // VAT/GST
15152        assert!(config.tax.vat_gst.enabled);
15153        assert_eq!(config.tax.vat_gst.standard_rates.get("DE"), Some(&0.19));
15154        assert_eq!(config.tax.vat_gst.standard_rates.get("GB"), Some(&0.20));
15155        assert_eq!(config.tax.vat_gst.reduced_rates.get("DE"), Some(&0.07));
15156        assert_eq!(config.tax.vat_gst.exempt_categories.len(), 2);
15157        assert!(!config.tax.vat_gst.reverse_charge);
15158
15159        // Sales tax
15160        assert!(config.tax.sales_tax.enabled);
15161        assert_eq!(config.tax.sales_tax.nexus_states.len(), 3);
15162        assert!(config
15163            .tax
15164            .sales_tax
15165            .nexus_states
15166            .contains(&"CA".to_string()));
15167
15168        // Withholding
15169        assert!(config.tax.withholding.enabled);
15170        assert!(!config.tax.withholding.treaty_network);
15171        assert_eq!(config.tax.withholding.default_rate, 0.25);
15172        assert_eq!(config.tax.withholding.treaty_reduced_rate, 0.10);
15173
15174        // Provisions
15175        assert!(!config.tax.provisions.enabled);
15176        assert_eq!(config.tax.provisions.statutory_rate, 0.28);
15177        assert!(!config.tax.provisions.uncertain_positions);
15178
15179        // Payroll tax
15180        assert!(config.tax.payroll_tax.enabled);
15181    }
15182
15183    #[test]
15184    fn test_generator_config_with_tax_default() {
15185        let yaml = r#"
15186            global:
15187              seed: 42
15188              start_date: "2024-01-01"
15189              period_months: 12
15190              industry: retail
15191            companies:
15192              - code: C001
15193                name: Test Corp
15194                currency: USD
15195                country: US
15196                annual_transaction_volume: ten_k
15197            chart_of_accounts:
15198              complexity: small
15199            output:
15200              output_directory: ./output
15201        "#;
15202
15203        let config: GeneratorConfig =
15204            serde_yaml::from_str(yaml).expect("Failed to parse config without tax section");
15205        // Tax should be present with defaults when not specified in YAML
15206        assert!(!config.tax.enabled);
15207        assert!(config.tax.jurisdictions.countries.is_empty());
15208        assert_eq!(config.tax.anomaly_rate, 0.03);
15209        assert!(config.tax.provisions.enabled); // provisions default to enabled=true
15210        assert_eq!(config.tax.provisions.statutory_rate, 0.21);
15211    }
15212
15213    // ==========================================================================
15214    // SessionSchemaConfig Tests
15215    // ==========================================================================
15216
15217    #[test]
15218    fn test_session_config_default_disabled() {
15219        let yaml = "{}";
15220        let config: SessionSchemaConfig =
15221            serde_yaml::from_str(yaml).expect("Failed to parse empty session config");
15222        assert!(!config.enabled);
15223        assert!(config.checkpoint_path.is_none());
15224        assert!(config.per_period_output);
15225        assert!(config.consolidated_output);
15226    }
15227
15228    #[test]
15229    fn test_config_backward_compatible_without_session() {
15230        let yaml = r#"
15231            global:
15232              seed: 42
15233              start_date: "2024-01-01"
15234              period_months: 12
15235              industry: retail
15236            companies:
15237              - code: C001
15238                name: Test Corp
15239                currency: USD
15240                country: US
15241                annual_transaction_volume: ten_k
15242            chart_of_accounts:
15243              complexity: small
15244            output:
15245              output_directory: ./output
15246        "#;
15247
15248        let config: GeneratorConfig =
15249            serde_yaml::from_str(yaml).expect("Failed to parse config without session");
15250        // Session should default to disabled
15251        assert!(!config.session.enabled);
15252        assert!(config.session.per_period_output);
15253        assert!(config.session.consolidated_output);
15254        // fiscal_year_months should be None
15255        assert!(config.global.fiscal_year_months.is_none());
15256    }
15257
15258    #[test]
15259    fn test_fiscal_year_months_parsed() {
15260        let yaml = r#"
15261            global:
15262              seed: 42
15263              start_date: "2024-01-01"
15264              period_months: 24
15265              industry: retail
15266              fiscal_year_months: 12
15267            companies:
15268              - code: C001
15269                name: Test Corp
15270                currency: USD
15271                country: US
15272                annual_transaction_volume: ten_k
15273            chart_of_accounts:
15274              complexity: small
15275            output:
15276              output_directory: ./output
15277            session:
15278              enabled: true
15279              checkpoint_path: /tmp/checkpoints
15280              per_period_output: true
15281              consolidated_output: false
15282        "#;
15283
15284        let config: GeneratorConfig =
15285            serde_yaml::from_str(yaml).expect("Failed to parse config with fiscal_year_months");
15286        assert_eq!(config.global.fiscal_year_months, Some(12));
15287        assert!(config.session.enabled);
15288        assert_eq!(
15289            config.session.checkpoint_path,
15290            Some("/tmp/checkpoints".to_string())
15291        );
15292        assert!(config.session.per_period_output);
15293        assert!(!config.session.consolidated_output);
15294    }
15295}