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#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct GeneratorConfig {
14    /// Global settings
15    pub global: GlobalConfig,
16    /// Company configuration
17    pub companies: Vec<CompanyConfig>,
18    /// Chart of Accounts configuration
19    pub chart_of_accounts: ChartOfAccountsConfig,
20    /// Transaction generation settings
21    #[serde(default)]
22    pub transactions: TransactionConfig,
23    /// Output configuration
24    pub output: OutputConfig,
25    /// Fraud simulation settings
26    #[serde(default)]
27    pub fraud: FraudConfig,
28    /// Data quality variation settings
29    #[serde(default)]
30    pub data_quality: DataQualitySchemaConfig,
31    /// Internal Controls System settings
32    #[serde(default)]
33    pub internal_controls: InternalControlsConfig,
34    /// Business process mix
35    #[serde(default)]
36    pub business_processes: BusinessProcessConfig,
37    /// User persona distribution
38    #[serde(default)]
39    pub user_personas: UserPersonaConfig,
40    /// Template configuration for realistic data
41    #[serde(default)]
42    pub templates: TemplateConfig,
43    /// Approval workflow configuration
44    #[serde(default)]
45    pub approval: ApprovalConfig,
46    /// Department structure configuration
47    #[serde(default)]
48    pub departments: DepartmentConfig,
49    /// Master data generation settings
50    #[serde(default)]
51    pub master_data: MasterDataConfig,
52    /// Document flow generation settings
53    #[serde(default)]
54    pub document_flows: DocumentFlowConfig,
55    /// Intercompany transaction settings
56    #[serde(default)]
57    pub intercompany: IntercompanyConfig,
58    /// Balance and trial balance settings
59    #[serde(default)]
60    pub balance: BalanceConfig,
61    /// OCPM (Object-Centric Process Mining) settings
62    #[serde(default)]
63    pub ocpm: OcpmConfig,
64    /// Audit engagement and workpaper generation settings
65    #[serde(default)]
66    pub audit: AuditGenerationConfig,
67    /// Banking KYC/AML transaction generation settings
68    #[serde(default)]
69    pub banking: datasynth_banking::BankingConfig,
70    /// Scenario configuration for metadata and tagging (Phase 1.3)
71    #[serde(default)]
72    pub scenario: ScenarioConfig,
73    /// Temporal drift configuration for simulating distribution changes over time (Phase 2.2)
74    #[serde(default)]
75    pub temporal: TemporalDriftConfig,
76    /// Graph export configuration for accounting network export
77    #[serde(default)]
78    pub graph_export: GraphExportConfig,
79    /// Streaming output API configuration
80    #[serde(default)]
81    pub streaming: StreamingSchemaConfig,
82    /// Rate limiting configuration
83    #[serde(default)]
84    pub rate_limit: RateLimitSchemaConfig,
85    /// Temporal attribute generation configuration
86    #[serde(default)]
87    pub temporal_attributes: TemporalAttributeSchemaConfig,
88    /// Relationship generation configuration
89    #[serde(default)]
90    pub relationships: RelationshipSchemaConfig,
91    /// Accounting standards framework configuration (IFRS, US GAAP)
92    #[serde(default)]
93    pub accounting_standards: AccountingStandardsConfig,
94    /// Audit standards framework configuration (ISA, PCAOB)
95    #[serde(default)]
96    pub audit_standards: AuditStandardsConfig,
97}
98
99/// Graph export configuration for accounting network and ML training exports.
100///
101/// This section enables exporting generated data as graphs for:
102/// - Network reconstruction algorithms
103/// - Graph neural network training
104/// - Neo4j graph database import
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct GraphExportConfig {
107    /// Enable graph export.
108    #[serde(default)]
109    pub enabled: bool,
110
111    /// Graph types to generate.
112    #[serde(default = "default_graph_types")]
113    pub graph_types: Vec<GraphTypeConfig>,
114
115    /// Export formats to generate.
116    #[serde(default = "default_graph_formats")]
117    pub formats: Vec<GraphExportFormat>,
118
119    /// Train split ratio for ML datasets.
120    #[serde(default = "default_train_ratio")]
121    pub train_ratio: f64,
122
123    /// Validation split ratio for ML datasets.
124    #[serde(default = "default_val_ratio")]
125    pub validation_ratio: f64,
126
127    /// Random seed for train/val/test splits.
128    #[serde(default)]
129    pub split_seed: Option<u64>,
130
131    /// Output subdirectory for graph exports (relative to output directory).
132    #[serde(default = "default_graph_subdir")]
133    pub output_subdirectory: String,
134}
135
136fn default_graph_types() -> Vec<GraphTypeConfig> {
137    vec![GraphTypeConfig::default()]
138}
139
140fn default_graph_formats() -> Vec<GraphExportFormat> {
141    vec![GraphExportFormat::PytorchGeometric]
142}
143
144fn default_train_ratio() -> f64 {
145    0.7
146}
147
148fn default_val_ratio() -> f64 {
149    0.15
150}
151
152fn default_graph_subdir() -> String {
153    "graphs".to_string()
154}
155
156impl Default for GraphExportConfig {
157    fn default() -> Self {
158        Self {
159            enabled: false,
160            graph_types: default_graph_types(),
161            formats: default_graph_formats(),
162            train_ratio: 0.7,
163            validation_ratio: 0.15,
164            split_seed: None,
165            output_subdirectory: "graphs".to_string(),
166        }
167    }
168}
169
170/// Configuration for a specific graph type to export.
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct GraphTypeConfig {
173    /// Name identifier for this graph configuration.
174    #[serde(default = "default_graph_name")]
175    pub name: String,
176
177    /// Whether to aggregate parallel edges between the same nodes.
178    #[serde(default)]
179    pub aggregate_edges: bool,
180
181    /// Minimum edge weight to include (filters out small transactions).
182    #[serde(default)]
183    pub min_edge_weight: f64,
184
185    /// Whether to include document nodes (creates hub-and-spoke structure).
186    #[serde(default)]
187    pub include_document_nodes: bool,
188}
189
190fn default_graph_name() -> String {
191    "accounting_network".to_string()
192}
193
194impl Default for GraphTypeConfig {
195    fn default() -> Self {
196        Self {
197            name: "accounting_network".to_string(),
198            aggregate_edges: false,
199            min_edge_weight: 0.0,
200            include_document_nodes: false,
201        }
202    }
203}
204
205/// Export format for graph data.
206#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
207#[serde(rename_all = "snake_case")]
208pub enum GraphExportFormat {
209    /// PyTorch Geometric format (.npy files + metadata.json).
210    PytorchGeometric,
211    /// Neo4j format (CSV files + Cypher import scripts).
212    Neo4j,
213    /// Deep Graph Library format.
214    Dgl,
215    /// RustGraph/RustAssureTwin JSON format.
216    RustGraph,
217}
218
219/// Scenario configuration for metadata, tagging, and ML training setup.
220///
221/// This section enables tracking the purpose and characteristics of a generation run.
222#[derive(Debug, Clone, Default, Serialize, Deserialize)]
223pub struct ScenarioConfig {
224    /// Tags for categorizing and filtering datasets.
225    /// Examples: "fraud_detection", "retail", "month_end_stress", "ml_training"
226    #[serde(default)]
227    pub tags: Vec<String>,
228
229    /// Data quality profile preset.
230    /// - "clean": Minimal data quality issues (0.1% missing, 0.05% typos)
231    /// - "noisy": Moderate issues (5% missing, 2% typos, 1% duplicates)
232    /// - "legacy": Heavy issues simulating legacy system data (10% missing, 5% typos)
233    #[serde(default)]
234    pub profile: Option<String>,
235
236    /// Human-readable description of the scenario purpose.
237    #[serde(default)]
238    pub description: Option<String>,
239
240    /// Whether this run is for ML training (enables balanced labeling).
241    #[serde(default)]
242    pub ml_training: bool,
243
244    /// Target anomaly class balance for ML training.
245    /// If set, anomalies will be injected to achieve this ratio.
246    #[serde(default)]
247    pub target_anomaly_ratio: Option<f64>,
248
249    /// Custom metadata key-value pairs.
250    #[serde(default)]
251    pub metadata: std::collections::HashMap<String, String>,
252}
253
254/// Temporal drift configuration for simulating distribution changes over time.
255///
256/// This enables generation of data that shows realistic temporal evolution,
257/// useful for training drift detection models and testing temporal robustness.
258#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct TemporalDriftConfig {
260    /// Enable temporal drift simulation.
261    #[serde(default)]
262    pub enabled: bool,
263
264    /// Amount mean drift per period (e.g., 0.02 = 2% mean shift per month).
265    /// Simulates gradual inflation or business growth.
266    #[serde(default = "default_amount_drift")]
267    pub amount_mean_drift: f64,
268
269    /// Amount variance drift per period (e.g., 0.01 = 1% variance increase per month).
270    /// Simulates increasing volatility over time.
271    #[serde(default)]
272    pub amount_variance_drift: f64,
273
274    /// Anomaly rate drift per period (e.g., 0.001 = 0.1% increase per month).
275    /// Simulates increasing fraud attempts or degrading controls.
276    #[serde(default)]
277    pub anomaly_rate_drift: f64,
278
279    /// Concept drift rate - how quickly feature distributions change (0.0-1.0).
280    /// Higher values cause more rapid distribution shifts.
281    #[serde(default = "default_concept_drift")]
282    pub concept_drift_rate: f64,
283
284    /// Sudden drift events - probability of a sudden distribution shift in any period.
285    #[serde(default)]
286    pub sudden_drift_probability: f64,
287
288    /// Magnitude of sudden drift events when they occur (multiplier).
289    #[serde(default = "default_sudden_drift_magnitude")]
290    pub sudden_drift_magnitude: f64,
291
292    /// Seasonal drift - enable cyclic patterns that repeat annually.
293    #[serde(default)]
294    pub seasonal_drift: bool,
295
296    /// Drift start period (0 = from beginning). Use to simulate stable baseline before drift.
297    #[serde(default)]
298    pub drift_start_period: u32,
299
300    /// Drift type: "gradual", "sudden", "recurring", "mixed"
301    #[serde(default = "default_drift_type")]
302    pub drift_type: DriftType,
303}
304
305fn default_amount_drift() -> f64 {
306    0.02
307}
308
309fn default_concept_drift() -> f64 {
310    0.01
311}
312
313fn default_sudden_drift_magnitude() -> f64 {
314    2.0
315}
316
317fn default_drift_type() -> DriftType {
318    DriftType::Gradual
319}
320
321impl Default for TemporalDriftConfig {
322    fn default() -> Self {
323        Self {
324            enabled: false,
325            amount_mean_drift: 0.02,
326            amount_variance_drift: 0.0,
327            anomaly_rate_drift: 0.0,
328            concept_drift_rate: 0.01,
329            sudden_drift_probability: 0.0,
330            sudden_drift_magnitude: 2.0,
331            seasonal_drift: false,
332            drift_start_period: 0,
333            drift_type: DriftType::Gradual,
334        }
335    }
336}
337
338impl TemporalDriftConfig {
339    /// Convert to core DriftConfig for use in generators.
340    pub fn to_core_config(&self) -> datasynth_core::distributions::DriftConfig {
341        datasynth_core::distributions::DriftConfig {
342            enabled: self.enabled,
343            amount_mean_drift: self.amount_mean_drift,
344            amount_variance_drift: self.amount_variance_drift,
345            anomaly_rate_drift: self.anomaly_rate_drift,
346            concept_drift_rate: self.concept_drift_rate,
347            sudden_drift_probability: self.sudden_drift_probability,
348            sudden_drift_magnitude: self.sudden_drift_magnitude,
349            seasonal_drift: self.seasonal_drift,
350            drift_start_period: self.drift_start_period,
351            drift_type: match self.drift_type {
352                DriftType::Gradual => datasynth_core::distributions::DriftType::Gradual,
353                DriftType::Sudden => datasynth_core::distributions::DriftType::Sudden,
354                DriftType::Recurring => datasynth_core::distributions::DriftType::Recurring,
355                DriftType::Mixed => datasynth_core::distributions::DriftType::Mixed,
356            },
357        }
358    }
359}
360
361/// Types of temporal drift patterns.
362#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
363#[serde(rename_all = "snake_case")]
364pub enum DriftType {
365    /// Gradual, continuous drift over time (like inflation).
366    #[default]
367    Gradual,
368    /// Sudden, point-in-time shifts (like policy changes).
369    Sudden,
370    /// Recurring patterns that cycle (like seasonal variations).
371    Recurring,
372    /// Combination of gradual background drift with occasional sudden shifts.
373    Mixed,
374}
375
376// ============================================================================
377// Streaming Output API Configuration (Phase 2)
378// ============================================================================
379
380/// Configuration for streaming output API.
381#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct StreamingSchemaConfig {
383    /// Enable streaming output.
384    #[serde(default)]
385    pub enabled: bool,
386    /// Buffer size for streaming (number of items).
387    #[serde(default = "default_buffer_size")]
388    pub buffer_size: usize,
389    /// Enable progress reporting.
390    #[serde(default = "default_true")]
391    pub enable_progress: bool,
392    /// Progress reporting interval (number of items).
393    #[serde(default = "default_progress_interval")]
394    pub progress_interval: u64,
395    /// Backpressure strategy.
396    #[serde(default)]
397    pub backpressure: BackpressureSchemaStrategy,
398}
399
400fn default_buffer_size() -> usize {
401    1000
402}
403
404fn default_progress_interval() -> u64 {
405    100
406}
407
408impl Default for StreamingSchemaConfig {
409    fn default() -> Self {
410        Self {
411            enabled: false,
412            buffer_size: 1000,
413            enable_progress: true,
414            progress_interval: 100,
415            backpressure: BackpressureSchemaStrategy::Block,
416        }
417    }
418}
419
420/// Backpressure strategy for streaming output.
421#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
422#[serde(rename_all = "snake_case")]
423pub enum BackpressureSchemaStrategy {
424    /// Block until space is available in the buffer.
425    #[default]
426    Block,
427    /// Drop oldest items when buffer is full.
428    DropOldest,
429    /// Drop newest items when buffer is full.
430    DropNewest,
431    /// Buffer overflow items up to a limit, then block.
432    Buffer,
433}
434
435// ============================================================================
436// Rate Limiting Configuration (Phase 5)
437// ============================================================================
438
439/// Configuration for rate limiting.
440#[derive(Debug, Clone, Serialize, Deserialize)]
441pub struct RateLimitSchemaConfig {
442    /// Enable rate limiting.
443    #[serde(default)]
444    pub enabled: bool,
445    /// Entities per second limit.
446    #[serde(default = "default_entities_per_second")]
447    pub entities_per_second: f64,
448    /// Burst size (number of tokens in bucket).
449    #[serde(default = "default_burst_size")]
450    pub burst_size: u32,
451    /// Backpressure strategy for rate limiting.
452    #[serde(default)]
453    pub backpressure: RateLimitBackpressureSchema,
454}
455
456fn default_entities_per_second() -> f64 {
457    1000.0
458}
459
460fn default_burst_size() -> u32 {
461    100
462}
463
464impl Default for RateLimitSchemaConfig {
465    fn default() -> Self {
466        Self {
467            enabled: false,
468            entities_per_second: 1000.0,
469            burst_size: 100,
470            backpressure: RateLimitBackpressureSchema::Block,
471        }
472    }
473}
474
475/// Backpressure strategy for rate limiting.
476#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
477#[serde(rename_all = "snake_case")]
478pub enum RateLimitBackpressureSchema {
479    /// Block until rate allows.
480    #[default]
481    Block,
482    /// Drop items that exceed rate.
483    Drop,
484    /// Buffer items and process when rate allows.
485    Buffer,
486}
487
488// ============================================================================
489// Temporal Attribute Generation Configuration (Phase 3)
490// ============================================================================
491
492/// Configuration for temporal attribute generation.
493#[derive(Debug, Clone, Serialize, Deserialize)]
494pub struct TemporalAttributeSchemaConfig {
495    /// Enable temporal attribute generation.
496    #[serde(default)]
497    pub enabled: bool,
498    /// Valid time configuration.
499    #[serde(default)]
500    pub valid_time: ValidTimeSchemaConfig,
501    /// Transaction time configuration.
502    #[serde(default)]
503    pub transaction_time: TransactionTimeSchemaConfig,
504    /// Generate version chains for entities.
505    #[serde(default)]
506    pub generate_version_chains: bool,
507    /// Average number of versions per entity.
508    #[serde(default = "default_avg_versions")]
509    pub avg_versions_per_entity: f64,
510}
511
512fn default_avg_versions() -> f64 {
513    1.5
514}
515
516impl Default for TemporalAttributeSchemaConfig {
517    fn default() -> Self {
518        Self {
519            enabled: false,
520            valid_time: ValidTimeSchemaConfig::default(),
521            transaction_time: TransactionTimeSchemaConfig::default(),
522            generate_version_chains: false,
523            avg_versions_per_entity: 1.5,
524        }
525    }
526}
527
528/// Configuration for valid time (business time) generation.
529#[derive(Debug, Clone, Serialize, Deserialize)]
530pub struct ValidTimeSchemaConfig {
531    /// Probability that valid_to is set (entity has ended validity).
532    #[serde(default = "default_closed_probability")]
533    pub closed_probability: f64,
534    /// Average validity duration in days.
535    #[serde(default = "default_avg_validity_days")]
536    pub avg_validity_days: u32,
537    /// Standard deviation of validity duration in days.
538    #[serde(default = "default_validity_stddev")]
539    pub validity_stddev_days: u32,
540}
541
542fn default_closed_probability() -> f64 {
543    0.1
544}
545
546fn default_avg_validity_days() -> u32 {
547    365
548}
549
550fn default_validity_stddev() -> u32 {
551    90
552}
553
554impl Default for ValidTimeSchemaConfig {
555    fn default() -> Self {
556        Self {
557            closed_probability: 0.1,
558            avg_validity_days: 365,
559            validity_stddev_days: 90,
560        }
561    }
562}
563
564/// Configuration for transaction time (system time) generation.
565#[derive(Debug, Clone, Serialize, Deserialize)]
566pub struct TransactionTimeSchemaConfig {
567    /// Average recording delay in seconds (0 = immediate).
568    #[serde(default)]
569    pub avg_recording_delay_seconds: u32,
570    /// Allow backdating (recording time before valid time).
571    #[serde(default)]
572    pub allow_backdating: bool,
573    /// Probability of backdating if allowed.
574    #[serde(default = "default_backdating_probability")]
575    pub backdating_probability: f64,
576    /// Maximum backdate days.
577    #[serde(default = "default_max_backdate_days")]
578    pub max_backdate_days: u32,
579}
580
581fn default_backdating_probability() -> f64 {
582    0.01
583}
584
585fn default_max_backdate_days() -> u32 {
586    30
587}
588
589impl Default for TransactionTimeSchemaConfig {
590    fn default() -> Self {
591        Self {
592            avg_recording_delay_seconds: 0,
593            allow_backdating: false,
594            backdating_probability: 0.01,
595            max_backdate_days: 30,
596        }
597    }
598}
599
600// ============================================================================
601// Relationship Generation Configuration (Phase 4)
602// ============================================================================
603
604/// Configuration for relationship generation.
605#[derive(Debug, Clone, Serialize, Deserialize)]
606pub struct RelationshipSchemaConfig {
607    /// Relationship type definitions.
608    #[serde(default)]
609    pub relationship_types: Vec<RelationshipTypeSchemaConfig>,
610    /// Allow orphan entities (entities with no relationships).
611    #[serde(default = "default_true")]
612    pub allow_orphans: bool,
613    /// Probability of creating an orphan entity.
614    #[serde(default = "default_orphan_probability")]
615    pub orphan_probability: f64,
616    /// Allow circular relationships.
617    #[serde(default)]
618    pub allow_circular: bool,
619    /// Maximum depth for circular relationship detection.
620    #[serde(default = "default_max_circular_depth")]
621    pub max_circular_depth: u32,
622}
623
624fn default_orphan_probability() -> f64 {
625    0.01
626}
627
628fn default_max_circular_depth() -> u32 {
629    3
630}
631
632impl Default for RelationshipSchemaConfig {
633    fn default() -> Self {
634        Self {
635            relationship_types: Vec::new(),
636            allow_orphans: true,
637            orphan_probability: 0.01,
638            allow_circular: false,
639            max_circular_depth: 3,
640        }
641    }
642}
643
644/// Configuration for a specific relationship type.
645#[derive(Debug, Clone, Serialize, Deserialize)]
646pub struct RelationshipTypeSchemaConfig {
647    /// Name of the relationship type (e.g., "debits", "credits", "created").
648    pub name: String,
649    /// Source entity type (e.g., "journal_entry").
650    pub source_type: String,
651    /// Target entity type (e.g., "account").
652    pub target_type: String,
653    /// Cardinality rule for this relationship.
654    #[serde(default)]
655    pub cardinality: CardinalitySchemaRule,
656    /// Weight for this relationship in random selection.
657    #[serde(default = "default_relationship_weight")]
658    pub weight: f64,
659    /// Whether this relationship is required.
660    #[serde(default)]
661    pub required: bool,
662    /// Whether this relationship is directed.
663    #[serde(default = "default_true")]
664    pub directed: bool,
665}
666
667fn default_relationship_weight() -> f64 {
668    1.0
669}
670
671impl Default for RelationshipTypeSchemaConfig {
672    fn default() -> Self {
673        Self {
674            name: String::new(),
675            source_type: String::new(),
676            target_type: String::new(),
677            cardinality: CardinalitySchemaRule::default(),
678            weight: 1.0,
679            required: false,
680            directed: true,
681        }
682    }
683}
684
685/// Cardinality rule for relationships in schema config.
686#[derive(Debug, Clone, Serialize, Deserialize)]
687#[serde(rename_all = "snake_case")]
688pub enum CardinalitySchemaRule {
689    /// One source to one target.
690    OneToOne,
691    /// One source to many targets.
692    OneToMany {
693        /// Minimum number of targets.
694        min: u32,
695        /// Maximum number of targets.
696        max: u32,
697    },
698    /// Many sources to one target.
699    ManyToOne {
700        /// Minimum number of sources.
701        min: u32,
702        /// Maximum number of sources.
703        max: u32,
704    },
705    /// Many sources to many targets.
706    ManyToMany {
707        /// Minimum targets per source.
708        min_per_source: u32,
709        /// Maximum targets per source.
710        max_per_source: u32,
711    },
712}
713
714impl Default for CardinalitySchemaRule {
715    fn default() -> Self {
716        Self::OneToMany { min: 1, max: 5 }
717    }
718}
719
720/// Global configuration settings.
721#[derive(Debug, Clone, Serialize, Deserialize)]
722pub struct GlobalConfig {
723    /// Random seed for reproducibility
724    pub seed: Option<u64>,
725    /// Industry sector
726    pub industry: IndustrySector,
727    /// Simulation start date (YYYY-MM-DD)
728    pub start_date: String,
729    /// Simulation period in months
730    pub period_months: u32,
731    /// Base currency for group reporting
732    #[serde(default = "default_currency")]
733    pub group_currency: String,
734    /// Enable parallel generation
735    #[serde(default = "default_true")]
736    pub parallel: bool,
737    /// Number of worker threads (0 = auto-detect)
738    #[serde(default)]
739    pub worker_threads: usize,
740    /// Memory limit in MB (0 = unlimited)
741    #[serde(default)]
742    pub memory_limit_mb: usize,
743}
744
745fn default_currency() -> String {
746    "USD".to_string()
747}
748fn default_true() -> bool {
749    true
750}
751
752/// Company code configuration.
753#[derive(Debug, Clone, Serialize, Deserialize)]
754pub struct CompanyConfig {
755    /// Company code identifier
756    pub code: String,
757    /// Company name
758    pub name: String,
759    /// Local currency (ISO 4217)
760    pub currency: String,
761    /// Country code (ISO 3166-1 alpha-2)
762    pub country: String,
763    /// Fiscal year variant
764    #[serde(default = "default_fiscal_variant")]
765    pub fiscal_year_variant: String,
766    /// Transaction volume per year
767    pub annual_transaction_volume: TransactionVolume,
768    /// Company-specific transaction weight
769    #[serde(default = "default_weight")]
770    pub volume_weight: f64,
771}
772
773fn default_fiscal_variant() -> String {
774    "K4".to_string()
775}
776fn default_weight() -> f64 {
777    1.0
778}
779
780/// Transaction volume presets.
781#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
782#[serde(rename_all = "snake_case")]
783pub enum TransactionVolume {
784    /// 10,000 transactions per year
785    TenK,
786    /// 100,000 transactions per year
787    HundredK,
788    /// 1,000,000 transactions per year
789    OneM,
790    /// 10,000,000 transactions per year
791    TenM,
792    /// 100,000,000 transactions per year
793    HundredM,
794    /// Custom count
795    Custom(u64),
796}
797
798impl TransactionVolume {
799    /// Get the transaction count.
800    pub fn count(&self) -> u64 {
801        match self {
802            Self::TenK => 10_000,
803            Self::HundredK => 100_000,
804            Self::OneM => 1_000_000,
805            Self::TenM => 10_000_000,
806            Self::HundredM => 100_000_000,
807            Self::Custom(n) => *n,
808        }
809    }
810}
811
812/// Chart of Accounts configuration.
813#[derive(Debug, Clone, Serialize, Deserialize)]
814pub struct ChartOfAccountsConfig {
815    /// CoA complexity level
816    pub complexity: CoAComplexity,
817    /// Use industry-specific accounts
818    #[serde(default = "default_true")]
819    pub industry_specific: bool,
820    /// Custom account definitions file
821    pub custom_accounts: Option<PathBuf>,
822    /// Minimum hierarchy depth
823    #[serde(default = "default_min_depth")]
824    pub min_hierarchy_depth: u8,
825    /// Maximum hierarchy depth
826    #[serde(default = "default_max_depth")]
827    pub max_hierarchy_depth: u8,
828}
829
830fn default_min_depth() -> u8 {
831    2
832}
833fn default_max_depth() -> u8 {
834    5
835}
836
837impl Default for ChartOfAccountsConfig {
838    fn default() -> Self {
839        Self {
840            complexity: CoAComplexity::Small,
841            industry_specific: true,
842            custom_accounts: None,
843            min_hierarchy_depth: default_min_depth(),
844            max_hierarchy_depth: default_max_depth(),
845        }
846    }
847}
848
849/// Transaction generation configuration.
850#[derive(Debug, Clone, Serialize, Deserialize, Default)]
851pub struct TransactionConfig {
852    /// Line item distribution
853    #[serde(default)]
854    pub line_item_distribution: LineItemDistributionConfig,
855    /// Debit/credit balance distribution
856    #[serde(default)]
857    pub debit_credit_distribution: DebitCreditDistributionConfig,
858    /// Even/odd line count distribution
859    #[serde(default)]
860    pub even_odd_distribution: EvenOddDistributionConfig,
861    /// Transaction source distribution
862    #[serde(default)]
863    pub source_distribution: SourceDistribution,
864    /// Seasonality configuration
865    #[serde(default)]
866    pub seasonality: SeasonalityConfig,
867    /// Amount distribution
868    #[serde(default)]
869    pub amounts: AmountDistributionConfig,
870    /// Benford's Law compliance configuration
871    #[serde(default)]
872    pub benford: BenfordConfig,
873}
874
875/// Benford's Law compliance configuration.
876#[derive(Debug, Clone, Serialize, Deserialize)]
877pub struct BenfordConfig {
878    /// Enable Benford's Law compliance for amount generation
879    #[serde(default = "default_true")]
880    pub enabled: bool,
881    /// Tolerance for deviation from ideal Benford distribution (0.0-1.0)
882    #[serde(default = "default_benford_tolerance")]
883    pub tolerance: f64,
884    /// Transaction sources exempt from Benford's Law (fixed amounts)
885    #[serde(default)]
886    pub exempt_sources: Vec<BenfordExemption>,
887}
888
889fn default_benford_tolerance() -> f64 {
890    0.05
891}
892
893impl Default for BenfordConfig {
894    fn default() -> Self {
895        Self {
896            enabled: true,
897            tolerance: default_benford_tolerance(),
898            exempt_sources: vec![BenfordExemption::Recurring, BenfordExemption::Payroll],
899        }
900    }
901}
902
903/// Types of transactions exempt from Benford's Law.
904#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
905#[serde(rename_all = "snake_case")]
906pub enum BenfordExemption {
907    /// Recurring fixed amounts (rent, subscriptions)
908    Recurring,
909    /// Payroll (standardized salaries)
910    Payroll,
911    /// Fixed fees and charges
912    FixedFees,
913    /// Round number purchases (often legitimate)
914    RoundAmounts,
915}
916
917/// Distribution of transaction sources.
918#[derive(Debug, Clone, Serialize, Deserialize)]
919pub struct SourceDistribution {
920    /// Manual entries percentage
921    pub manual: f64,
922    /// Automated system entries
923    pub automated: f64,
924    /// Recurring entries
925    pub recurring: f64,
926    /// Adjustment entries
927    pub adjustment: f64,
928}
929
930impl Default for SourceDistribution {
931    fn default() -> Self {
932        Self {
933            manual: 0.20,
934            automated: 0.70,
935            recurring: 0.07,
936            adjustment: 0.03,
937        }
938    }
939}
940
941/// Output configuration.
942#[derive(Debug, Clone, Serialize, Deserialize)]
943pub struct OutputConfig {
944    /// Output mode
945    #[serde(default)]
946    pub mode: OutputMode,
947    /// Output directory
948    pub output_directory: PathBuf,
949    /// File formats to generate
950    #[serde(default = "default_formats")]
951    pub formats: Vec<FileFormat>,
952    /// Compression settings
953    #[serde(default)]
954    pub compression: CompressionConfig,
955    /// Batch size for writes
956    #[serde(default = "default_batch_size")]
957    pub batch_size: usize,
958    /// Include ACDOCA format
959    #[serde(default = "default_true")]
960    pub include_acdoca: bool,
961    /// Include BSEG format
962    #[serde(default)]
963    pub include_bseg: bool,
964    /// Partition by fiscal period
965    #[serde(default = "default_true")]
966    pub partition_by_period: bool,
967    /// Partition by company code
968    #[serde(default)]
969    pub partition_by_company: bool,
970}
971
972fn default_formats() -> Vec<FileFormat> {
973    vec![FileFormat::Parquet]
974}
975fn default_batch_size() -> usize {
976    100_000
977}
978
979impl Default for OutputConfig {
980    fn default() -> Self {
981        Self {
982            mode: OutputMode::FlatFile,
983            output_directory: PathBuf::from("./output"),
984            formats: default_formats(),
985            compression: CompressionConfig::default(),
986            batch_size: default_batch_size(),
987            include_acdoca: true,
988            include_bseg: false,
989            partition_by_period: true,
990            partition_by_company: false,
991        }
992    }
993}
994
995/// Output mode.
996#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
997#[serde(rename_all = "snake_case")]
998pub enum OutputMode {
999    /// Stream records as generated
1000    Streaming,
1001    /// Write to flat files
1002    #[default]
1003    FlatFile,
1004    /// Both streaming and flat file
1005    Both,
1006}
1007
1008/// Supported file formats.
1009#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1010#[serde(rename_all = "snake_case")]
1011pub enum FileFormat {
1012    Csv,
1013    Parquet,
1014    Json,
1015    JsonLines,
1016}
1017
1018/// Compression configuration.
1019#[derive(Debug, Clone, Serialize, Deserialize)]
1020pub struct CompressionConfig {
1021    /// Enable compression
1022    #[serde(default = "default_true")]
1023    pub enabled: bool,
1024    /// Compression algorithm
1025    #[serde(default)]
1026    pub algorithm: CompressionAlgorithm,
1027    /// Compression level (1-9)
1028    #[serde(default = "default_compression_level")]
1029    pub level: u8,
1030}
1031
1032fn default_compression_level() -> u8 {
1033    3
1034}
1035
1036impl Default for CompressionConfig {
1037    fn default() -> Self {
1038        Self {
1039            enabled: true,
1040            algorithm: CompressionAlgorithm::default(),
1041            level: default_compression_level(),
1042        }
1043    }
1044}
1045
1046/// Compression algorithms.
1047#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
1048#[serde(rename_all = "snake_case")]
1049pub enum CompressionAlgorithm {
1050    Gzip,
1051    #[default]
1052    Zstd,
1053    Lz4,
1054    Snappy,
1055}
1056
1057/// Fraud simulation configuration.
1058#[derive(Debug, Clone, Serialize, Deserialize)]
1059pub struct FraudConfig {
1060    /// Enable fraud scenario generation
1061    #[serde(default)]
1062    pub enabled: bool,
1063    /// Overall fraud rate (0.0 to 1.0)
1064    #[serde(default = "default_fraud_rate")]
1065    pub fraud_rate: f64,
1066    /// Fraud type distribution
1067    #[serde(default)]
1068    pub fraud_type_distribution: FraudTypeDistribution,
1069    /// Enable fraud clustering
1070    #[serde(default)]
1071    pub clustering_enabled: bool,
1072    /// Clustering factor
1073    #[serde(default = "default_clustering_factor")]
1074    pub clustering_factor: f64,
1075    /// Approval thresholds for threshold-adjacent fraud pattern
1076    #[serde(default = "default_approval_thresholds")]
1077    pub approval_thresholds: Vec<f64>,
1078}
1079
1080fn default_approval_thresholds() -> Vec<f64> {
1081    vec![1000.0, 5000.0, 10000.0, 25000.0, 50000.0, 100000.0]
1082}
1083
1084fn default_fraud_rate() -> f64 {
1085    0.005
1086}
1087fn default_clustering_factor() -> f64 {
1088    3.0
1089}
1090
1091impl Default for FraudConfig {
1092    fn default() -> Self {
1093        Self {
1094            enabled: false,
1095            fraud_rate: default_fraud_rate(),
1096            fraud_type_distribution: FraudTypeDistribution::default(),
1097            clustering_enabled: false,
1098            clustering_factor: default_clustering_factor(),
1099            approval_thresholds: default_approval_thresholds(),
1100        }
1101    }
1102}
1103
1104/// Distribution of fraud types.
1105#[derive(Debug, Clone, Serialize, Deserialize)]
1106pub struct FraudTypeDistribution {
1107    pub suspense_account_abuse: f64,
1108    pub fictitious_transaction: f64,
1109    pub revenue_manipulation: f64,
1110    pub expense_capitalization: f64,
1111    pub split_transaction: f64,
1112    pub timing_anomaly: f64,
1113    pub unauthorized_access: f64,
1114    pub duplicate_payment: f64,
1115}
1116
1117impl Default for FraudTypeDistribution {
1118    fn default() -> Self {
1119        Self {
1120            suspense_account_abuse: 0.25,
1121            fictitious_transaction: 0.15,
1122            revenue_manipulation: 0.10,
1123            expense_capitalization: 0.10,
1124            split_transaction: 0.15,
1125            timing_anomaly: 0.10,
1126            unauthorized_access: 0.10,
1127            duplicate_payment: 0.05,
1128        }
1129    }
1130}
1131
1132/// Internal Controls System (ICS) configuration.
1133#[derive(Debug, Clone, Serialize, Deserialize)]
1134pub struct InternalControlsConfig {
1135    /// Enable internal controls system
1136    #[serde(default)]
1137    pub enabled: bool,
1138    /// Rate at which controls result in exceptions (0.0 - 1.0)
1139    #[serde(default = "default_exception_rate")]
1140    pub exception_rate: f64,
1141    /// Rate at which SoD violations occur (0.0 - 1.0)
1142    #[serde(default = "default_sod_violation_rate")]
1143    pub sod_violation_rate: f64,
1144    /// Export control master data to separate files
1145    #[serde(default = "default_true")]
1146    pub export_control_master_data: bool,
1147    /// SOX materiality threshold for marking transactions as SOX-relevant
1148    #[serde(default = "default_sox_materiality_threshold")]
1149    pub sox_materiality_threshold: f64,
1150    /// Enable COSO 2013 framework integration
1151    #[serde(default = "default_true")]
1152    pub coso_enabled: bool,
1153    /// Include entity-level controls in generation
1154    #[serde(default)]
1155    pub include_entity_level_controls: bool,
1156    /// Target maturity level for controls
1157    /// Valid values: "ad_hoc", "repeatable", "defined", "managed", "optimized", "mixed"
1158    #[serde(default = "default_target_maturity_level")]
1159    pub target_maturity_level: String,
1160}
1161
1162fn default_exception_rate() -> f64 {
1163    0.02
1164}
1165
1166fn default_sod_violation_rate() -> f64 {
1167    0.01
1168}
1169
1170fn default_sox_materiality_threshold() -> f64 {
1171    10000.0
1172}
1173
1174fn default_target_maturity_level() -> String {
1175    "mixed".to_string()
1176}
1177
1178impl Default for InternalControlsConfig {
1179    fn default() -> Self {
1180        Self {
1181            enabled: false,
1182            exception_rate: default_exception_rate(),
1183            sod_violation_rate: default_sod_violation_rate(),
1184            export_control_master_data: true,
1185            sox_materiality_threshold: default_sox_materiality_threshold(),
1186            coso_enabled: true,
1187            include_entity_level_controls: false,
1188            target_maturity_level: default_target_maturity_level(),
1189        }
1190    }
1191}
1192
1193/// Business process configuration.
1194#[derive(Debug, Clone, Serialize, Deserialize)]
1195pub struct BusinessProcessConfig {
1196    /// Order-to-Cash weight
1197    #[serde(default = "default_o2c")]
1198    pub o2c_weight: f64,
1199    /// Procure-to-Pay weight
1200    #[serde(default = "default_p2p")]
1201    pub p2p_weight: f64,
1202    /// Record-to-Report weight
1203    #[serde(default = "default_r2r")]
1204    pub r2r_weight: f64,
1205    /// Hire-to-Retire weight
1206    #[serde(default = "default_h2r")]
1207    pub h2r_weight: f64,
1208    /// Acquire-to-Retire weight
1209    #[serde(default = "default_a2r")]
1210    pub a2r_weight: f64,
1211}
1212
1213fn default_o2c() -> f64 {
1214    0.35
1215}
1216fn default_p2p() -> f64 {
1217    0.30
1218}
1219fn default_r2r() -> f64 {
1220    0.20
1221}
1222fn default_h2r() -> f64 {
1223    0.10
1224}
1225fn default_a2r() -> f64 {
1226    0.05
1227}
1228
1229impl Default for BusinessProcessConfig {
1230    fn default() -> Self {
1231        Self {
1232            o2c_weight: default_o2c(),
1233            p2p_weight: default_p2p(),
1234            r2r_weight: default_r2r(),
1235            h2r_weight: default_h2r(),
1236            a2r_weight: default_a2r(),
1237        }
1238    }
1239}
1240
1241/// User persona configuration.
1242#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1243pub struct UserPersonaConfig {
1244    /// Distribution of user personas
1245    #[serde(default)]
1246    pub persona_distribution: PersonaDistribution,
1247    /// Users per persona type
1248    #[serde(default)]
1249    pub users_per_persona: UsersPerPersona,
1250}
1251
1252/// Distribution of user personas for transaction generation.
1253#[derive(Debug, Clone, Serialize, Deserialize)]
1254pub struct PersonaDistribution {
1255    pub junior_accountant: f64,
1256    pub senior_accountant: f64,
1257    pub controller: f64,
1258    pub manager: f64,
1259    pub automated_system: f64,
1260}
1261
1262impl Default for PersonaDistribution {
1263    fn default() -> Self {
1264        Self {
1265            junior_accountant: 0.15,
1266            senior_accountant: 0.15,
1267            controller: 0.05,
1268            manager: 0.05,
1269            automated_system: 0.60,
1270        }
1271    }
1272}
1273
1274/// Number of users per persona type.
1275#[derive(Debug, Clone, Serialize, Deserialize)]
1276pub struct UsersPerPersona {
1277    pub junior_accountant: usize,
1278    pub senior_accountant: usize,
1279    pub controller: usize,
1280    pub manager: usize,
1281    pub automated_system: usize,
1282}
1283
1284impl Default for UsersPerPersona {
1285    fn default() -> Self {
1286        Self {
1287            junior_accountant: 10,
1288            senior_accountant: 5,
1289            controller: 2,
1290            manager: 3,
1291            automated_system: 20,
1292        }
1293    }
1294}
1295
1296/// Template configuration for realistic data generation.
1297#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1298pub struct TemplateConfig {
1299    /// Name generation settings
1300    #[serde(default)]
1301    pub names: NameTemplateConfig,
1302    /// Description generation settings
1303    #[serde(default)]
1304    pub descriptions: DescriptionTemplateConfig,
1305    /// Reference number settings
1306    #[serde(default)]
1307    pub references: ReferenceTemplateConfig,
1308}
1309
1310/// Name template configuration.
1311#[derive(Debug, Clone, Serialize, Deserialize)]
1312pub struct NameTemplateConfig {
1313    /// Distribution of name cultures
1314    #[serde(default)]
1315    pub culture_distribution: CultureDistribution,
1316    /// Email domain for generated users
1317    #[serde(default = "default_email_domain")]
1318    pub email_domain: String,
1319    /// Generate realistic display names
1320    #[serde(default = "default_true")]
1321    pub generate_realistic_names: bool,
1322}
1323
1324fn default_email_domain() -> String {
1325    "company.com".to_string()
1326}
1327
1328impl Default for NameTemplateConfig {
1329    fn default() -> Self {
1330        Self {
1331            culture_distribution: CultureDistribution::default(),
1332            email_domain: default_email_domain(),
1333            generate_realistic_names: true,
1334        }
1335    }
1336}
1337
1338/// Distribution of name cultures for generation.
1339#[derive(Debug, Clone, Serialize, Deserialize)]
1340pub struct CultureDistribution {
1341    pub western_us: f64,
1342    pub hispanic: f64,
1343    pub german: f64,
1344    pub french: f64,
1345    pub chinese: f64,
1346    pub japanese: f64,
1347    pub indian: f64,
1348}
1349
1350impl Default for CultureDistribution {
1351    fn default() -> Self {
1352        Self {
1353            western_us: 0.40,
1354            hispanic: 0.20,
1355            german: 0.10,
1356            french: 0.05,
1357            chinese: 0.10,
1358            japanese: 0.05,
1359            indian: 0.10,
1360        }
1361    }
1362}
1363
1364/// Description template configuration.
1365#[derive(Debug, Clone, Serialize, Deserialize)]
1366pub struct DescriptionTemplateConfig {
1367    /// Generate header text for journal entries
1368    #[serde(default = "default_true")]
1369    pub generate_header_text: bool,
1370    /// Generate line text for journal entry lines
1371    #[serde(default = "default_true")]
1372    pub generate_line_text: bool,
1373}
1374
1375impl Default for DescriptionTemplateConfig {
1376    fn default() -> Self {
1377        Self {
1378            generate_header_text: true,
1379            generate_line_text: true,
1380        }
1381    }
1382}
1383
1384/// Reference number template configuration.
1385#[derive(Debug, Clone, Serialize, Deserialize)]
1386pub struct ReferenceTemplateConfig {
1387    /// Generate reference numbers
1388    #[serde(default = "default_true")]
1389    pub generate_references: bool,
1390    /// Invoice prefix
1391    #[serde(default = "default_invoice_prefix")]
1392    pub invoice_prefix: String,
1393    /// Purchase order prefix
1394    #[serde(default = "default_po_prefix")]
1395    pub po_prefix: String,
1396    /// Sales order prefix
1397    #[serde(default = "default_so_prefix")]
1398    pub so_prefix: String,
1399}
1400
1401fn default_invoice_prefix() -> String {
1402    "INV".to_string()
1403}
1404fn default_po_prefix() -> String {
1405    "PO".to_string()
1406}
1407fn default_so_prefix() -> String {
1408    "SO".to_string()
1409}
1410
1411impl Default for ReferenceTemplateConfig {
1412    fn default() -> Self {
1413        Self {
1414            generate_references: true,
1415            invoice_prefix: default_invoice_prefix(),
1416            po_prefix: default_po_prefix(),
1417            so_prefix: default_so_prefix(),
1418        }
1419    }
1420}
1421
1422/// Approval workflow configuration.
1423#[derive(Debug, Clone, Serialize, Deserialize)]
1424pub struct ApprovalConfig {
1425    /// Enable approval workflow generation
1426    #[serde(default)]
1427    pub enabled: bool,
1428    /// Threshold below which transactions are auto-approved
1429    #[serde(default = "default_auto_approve_threshold")]
1430    pub auto_approve_threshold: f64,
1431    /// Rate at which approvals are rejected (0.0 to 1.0)
1432    #[serde(default = "default_rejection_rate")]
1433    pub rejection_rate: f64,
1434    /// Rate at which approvals require revision (0.0 to 1.0)
1435    #[serde(default = "default_revision_rate")]
1436    pub revision_rate: f64,
1437    /// Average delay in hours for approval processing
1438    #[serde(default = "default_approval_delay_hours")]
1439    pub average_approval_delay_hours: f64,
1440    /// Approval chain thresholds
1441    #[serde(default)]
1442    pub thresholds: Vec<ApprovalThresholdConfig>,
1443}
1444
1445fn default_auto_approve_threshold() -> f64 {
1446    1000.0
1447}
1448fn default_rejection_rate() -> f64 {
1449    0.02
1450}
1451fn default_revision_rate() -> f64 {
1452    0.05
1453}
1454fn default_approval_delay_hours() -> f64 {
1455    4.0
1456}
1457
1458impl Default for ApprovalConfig {
1459    fn default() -> Self {
1460        Self {
1461            enabled: false,
1462            auto_approve_threshold: default_auto_approve_threshold(),
1463            rejection_rate: default_rejection_rate(),
1464            revision_rate: default_revision_rate(),
1465            average_approval_delay_hours: default_approval_delay_hours(),
1466            thresholds: vec![
1467                ApprovalThresholdConfig {
1468                    amount: 1000.0,
1469                    level: 1,
1470                    roles: vec!["senior_accountant".to_string()],
1471                },
1472                ApprovalThresholdConfig {
1473                    amount: 10000.0,
1474                    level: 2,
1475                    roles: vec!["senior_accountant".to_string(), "controller".to_string()],
1476                },
1477                ApprovalThresholdConfig {
1478                    amount: 100000.0,
1479                    level: 3,
1480                    roles: vec![
1481                        "senior_accountant".to_string(),
1482                        "controller".to_string(),
1483                        "manager".to_string(),
1484                    ],
1485                },
1486                ApprovalThresholdConfig {
1487                    amount: 500000.0,
1488                    level: 4,
1489                    roles: vec![
1490                        "senior_accountant".to_string(),
1491                        "controller".to_string(),
1492                        "manager".to_string(),
1493                        "executive".to_string(),
1494                    ],
1495                },
1496            ],
1497        }
1498    }
1499}
1500
1501/// Configuration for a single approval threshold.
1502#[derive(Debug, Clone, Serialize, Deserialize)]
1503pub struct ApprovalThresholdConfig {
1504    /// Amount threshold
1505    pub amount: f64,
1506    /// Approval level required
1507    pub level: u8,
1508    /// Roles that can approve at this level
1509    pub roles: Vec<String>,
1510}
1511
1512/// Department configuration.
1513#[derive(Debug, Clone, Serialize, Deserialize)]
1514pub struct DepartmentConfig {
1515    /// Enable department assignment
1516    #[serde(default)]
1517    pub enabled: bool,
1518    /// Multiplier for department headcounts
1519    #[serde(default = "default_headcount_multiplier")]
1520    pub headcount_multiplier: f64,
1521    /// Custom department definitions (optional)
1522    #[serde(default)]
1523    pub custom_departments: Vec<CustomDepartmentConfig>,
1524}
1525
1526fn default_headcount_multiplier() -> f64 {
1527    1.0
1528}
1529
1530impl Default for DepartmentConfig {
1531    fn default() -> Self {
1532        Self {
1533            enabled: false,
1534            headcount_multiplier: default_headcount_multiplier(),
1535            custom_departments: Vec::new(),
1536        }
1537    }
1538}
1539
1540/// Custom department definition.
1541#[derive(Debug, Clone, Serialize, Deserialize)]
1542pub struct CustomDepartmentConfig {
1543    /// Department code
1544    pub code: String,
1545    /// Department name
1546    pub name: String,
1547    /// Associated cost center
1548    #[serde(default)]
1549    pub cost_center: Option<String>,
1550    /// Primary business processes
1551    #[serde(default)]
1552    pub primary_processes: Vec<String>,
1553    /// Parent department code
1554    #[serde(default)]
1555    pub parent_code: Option<String>,
1556}
1557
1558// ============================================================================
1559// Master Data Configuration
1560// ============================================================================
1561
1562/// Master data generation configuration.
1563#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1564pub struct MasterDataConfig {
1565    /// Vendor master data settings
1566    #[serde(default)]
1567    pub vendors: VendorMasterConfig,
1568    /// Customer master data settings
1569    #[serde(default)]
1570    pub customers: CustomerMasterConfig,
1571    /// Material master data settings
1572    #[serde(default)]
1573    pub materials: MaterialMasterConfig,
1574    /// Fixed asset master data settings
1575    #[serde(default)]
1576    pub fixed_assets: FixedAssetMasterConfig,
1577    /// Employee master data settings
1578    #[serde(default)]
1579    pub employees: EmployeeMasterConfig,
1580    /// Cost center master data settings
1581    #[serde(default)]
1582    pub cost_centers: CostCenterMasterConfig,
1583}
1584
1585/// Vendor master data configuration.
1586#[derive(Debug, Clone, Serialize, Deserialize)]
1587pub struct VendorMasterConfig {
1588    /// Number of vendors to generate
1589    #[serde(default = "default_vendor_count")]
1590    pub count: usize,
1591    /// Percentage of vendors that are intercompany (0.0 to 1.0)
1592    #[serde(default = "default_intercompany_percent")]
1593    pub intercompany_percent: f64,
1594    /// Payment terms distribution
1595    #[serde(default)]
1596    pub payment_terms_distribution: PaymentTermsDistribution,
1597    /// Vendor behavior distribution
1598    #[serde(default)]
1599    pub behavior_distribution: VendorBehaviorDistribution,
1600    /// Generate bank account details
1601    #[serde(default = "default_true")]
1602    pub generate_bank_accounts: bool,
1603    /// Generate tax IDs
1604    #[serde(default = "default_true")]
1605    pub generate_tax_ids: bool,
1606}
1607
1608fn default_vendor_count() -> usize {
1609    500
1610}
1611
1612fn default_intercompany_percent() -> f64 {
1613    0.05
1614}
1615
1616impl Default for VendorMasterConfig {
1617    fn default() -> Self {
1618        Self {
1619            count: default_vendor_count(),
1620            intercompany_percent: default_intercompany_percent(),
1621            payment_terms_distribution: PaymentTermsDistribution::default(),
1622            behavior_distribution: VendorBehaviorDistribution::default(),
1623            generate_bank_accounts: true,
1624            generate_tax_ids: true,
1625        }
1626    }
1627}
1628
1629/// Payment terms distribution for vendors.
1630#[derive(Debug, Clone, Serialize, Deserialize)]
1631pub struct PaymentTermsDistribution {
1632    /// Net 30 days
1633    pub net_30: f64,
1634    /// Net 60 days
1635    pub net_60: f64,
1636    /// Net 90 days
1637    pub net_90: f64,
1638    /// 2% 10 Net 30 (early payment discount)
1639    pub two_ten_net_30: f64,
1640    /// Due on receipt
1641    pub due_on_receipt: f64,
1642    /// End of month
1643    pub end_of_month: f64,
1644}
1645
1646impl Default for PaymentTermsDistribution {
1647    fn default() -> Self {
1648        Self {
1649            net_30: 0.40,
1650            net_60: 0.20,
1651            net_90: 0.10,
1652            two_ten_net_30: 0.15,
1653            due_on_receipt: 0.05,
1654            end_of_month: 0.10,
1655        }
1656    }
1657}
1658
1659/// Vendor behavior distribution.
1660#[derive(Debug, Clone, Serialize, Deserialize)]
1661pub struct VendorBehaviorDistribution {
1662    /// Reliable vendors (consistent delivery, quality)
1663    pub reliable: f64,
1664    /// Sometimes late vendors
1665    pub sometimes_late: f64,
1666    /// Inconsistent quality vendors
1667    pub inconsistent_quality: f64,
1668    /// Premium vendors (high quality, premium pricing)
1669    pub premium: f64,
1670    /// Budget vendors (lower quality, lower pricing)
1671    pub budget: f64,
1672}
1673
1674impl Default for VendorBehaviorDistribution {
1675    fn default() -> Self {
1676        Self {
1677            reliable: 0.50,
1678            sometimes_late: 0.20,
1679            inconsistent_quality: 0.10,
1680            premium: 0.10,
1681            budget: 0.10,
1682        }
1683    }
1684}
1685
1686/// Customer master data configuration.
1687#[derive(Debug, Clone, Serialize, Deserialize)]
1688pub struct CustomerMasterConfig {
1689    /// Number of customers to generate
1690    #[serde(default = "default_customer_count")]
1691    pub count: usize,
1692    /// Percentage of customers that are intercompany (0.0 to 1.0)
1693    #[serde(default = "default_intercompany_percent")]
1694    pub intercompany_percent: f64,
1695    /// Credit rating distribution
1696    #[serde(default)]
1697    pub credit_rating_distribution: CreditRatingDistribution,
1698    /// Payment behavior distribution
1699    #[serde(default)]
1700    pub payment_behavior_distribution: PaymentBehaviorDistribution,
1701    /// Generate credit limits based on rating
1702    #[serde(default = "default_true")]
1703    pub generate_credit_limits: bool,
1704}
1705
1706fn default_customer_count() -> usize {
1707    2000
1708}
1709
1710impl Default for CustomerMasterConfig {
1711    fn default() -> Self {
1712        Self {
1713            count: default_customer_count(),
1714            intercompany_percent: default_intercompany_percent(),
1715            credit_rating_distribution: CreditRatingDistribution::default(),
1716            payment_behavior_distribution: PaymentBehaviorDistribution::default(),
1717            generate_credit_limits: true,
1718        }
1719    }
1720}
1721
1722/// Credit rating distribution for customers.
1723#[derive(Debug, Clone, Serialize, Deserialize)]
1724pub struct CreditRatingDistribution {
1725    /// AAA rating
1726    pub aaa: f64,
1727    /// AA rating
1728    pub aa: f64,
1729    /// A rating
1730    pub a: f64,
1731    /// BBB rating
1732    pub bbb: f64,
1733    /// BB rating
1734    pub bb: f64,
1735    /// B rating
1736    pub b: f64,
1737    /// Below B rating
1738    pub below_b: f64,
1739}
1740
1741impl Default for CreditRatingDistribution {
1742    fn default() -> Self {
1743        Self {
1744            aaa: 0.05,
1745            aa: 0.10,
1746            a: 0.20,
1747            bbb: 0.30,
1748            bb: 0.20,
1749            b: 0.10,
1750            below_b: 0.05,
1751        }
1752    }
1753}
1754
1755/// Payment behavior distribution for customers.
1756#[derive(Debug, Clone, Serialize, Deserialize)]
1757pub struct PaymentBehaviorDistribution {
1758    /// Always pays early
1759    pub early_payer: f64,
1760    /// Pays on time
1761    pub on_time: f64,
1762    /// Occasionally late
1763    pub occasional_late: f64,
1764    /// Frequently late
1765    pub frequent_late: f64,
1766    /// Takes early payment discounts
1767    pub discount_taker: f64,
1768}
1769
1770impl Default for PaymentBehaviorDistribution {
1771    fn default() -> Self {
1772        Self {
1773            early_payer: 0.10,
1774            on_time: 0.50,
1775            occasional_late: 0.25,
1776            frequent_late: 0.10,
1777            discount_taker: 0.05,
1778        }
1779    }
1780}
1781
1782/// Material master data configuration.
1783#[derive(Debug, Clone, Serialize, Deserialize)]
1784pub struct MaterialMasterConfig {
1785    /// Number of materials to generate
1786    #[serde(default = "default_material_count")]
1787    pub count: usize,
1788    /// Material type distribution
1789    #[serde(default)]
1790    pub type_distribution: MaterialTypeDistribution,
1791    /// Valuation method distribution
1792    #[serde(default)]
1793    pub valuation_distribution: ValuationMethodDistribution,
1794    /// Percentage of materials with BOM (bill of materials)
1795    #[serde(default = "default_bom_percent")]
1796    pub bom_percent: f64,
1797    /// Maximum BOM depth
1798    #[serde(default = "default_max_bom_depth")]
1799    pub max_bom_depth: u8,
1800}
1801
1802fn default_material_count() -> usize {
1803    5000
1804}
1805
1806fn default_bom_percent() -> f64 {
1807    0.20
1808}
1809
1810fn default_max_bom_depth() -> u8 {
1811    3
1812}
1813
1814impl Default for MaterialMasterConfig {
1815    fn default() -> Self {
1816        Self {
1817            count: default_material_count(),
1818            type_distribution: MaterialTypeDistribution::default(),
1819            valuation_distribution: ValuationMethodDistribution::default(),
1820            bom_percent: default_bom_percent(),
1821            max_bom_depth: default_max_bom_depth(),
1822        }
1823    }
1824}
1825
1826/// Material type distribution.
1827#[derive(Debug, Clone, Serialize, Deserialize)]
1828pub struct MaterialTypeDistribution {
1829    /// Raw materials
1830    pub raw_material: f64,
1831    /// Semi-finished goods
1832    pub semi_finished: f64,
1833    /// Finished goods
1834    pub finished_good: f64,
1835    /// Trading goods (purchased for resale)
1836    pub trading_good: f64,
1837    /// Operating supplies
1838    pub operating_supply: f64,
1839    /// Services
1840    pub service: f64,
1841}
1842
1843impl Default for MaterialTypeDistribution {
1844    fn default() -> Self {
1845        Self {
1846            raw_material: 0.30,
1847            semi_finished: 0.15,
1848            finished_good: 0.25,
1849            trading_good: 0.15,
1850            operating_supply: 0.10,
1851            service: 0.05,
1852        }
1853    }
1854}
1855
1856/// Valuation method distribution for materials.
1857#[derive(Debug, Clone, Serialize, Deserialize)]
1858pub struct ValuationMethodDistribution {
1859    /// Standard cost
1860    pub standard_cost: f64,
1861    /// Moving average
1862    pub moving_average: f64,
1863    /// FIFO (First In, First Out)
1864    pub fifo: f64,
1865    /// LIFO (Last In, First Out)
1866    pub lifo: f64,
1867}
1868
1869impl Default for ValuationMethodDistribution {
1870    fn default() -> Self {
1871        Self {
1872            standard_cost: 0.50,
1873            moving_average: 0.30,
1874            fifo: 0.15,
1875            lifo: 0.05,
1876        }
1877    }
1878}
1879
1880/// Fixed asset master data configuration.
1881#[derive(Debug, Clone, Serialize, Deserialize)]
1882pub struct FixedAssetMasterConfig {
1883    /// Number of fixed assets to generate
1884    #[serde(default = "default_asset_count")]
1885    pub count: usize,
1886    /// Asset class distribution
1887    #[serde(default)]
1888    pub class_distribution: AssetClassDistribution,
1889    /// Depreciation method distribution
1890    #[serde(default)]
1891    pub depreciation_distribution: DepreciationMethodDistribution,
1892    /// Percentage of assets that are fully depreciated
1893    #[serde(default = "default_fully_depreciated_percent")]
1894    pub fully_depreciated_percent: f64,
1895    /// Generate acquisition history
1896    #[serde(default = "default_true")]
1897    pub generate_acquisition_history: bool,
1898}
1899
1900fn default_asset_count() -> usize {
1901    800
1902}
1903
1904fn default_fully_depreciated_percent() -> f64 {
1905    0.15
1906}
1907
1908impl Default for FixedAssetMasterConfig {
1909    fn default() -> Self {
1910        Self {
1911            count: default_asset_count(),
1912            class_distribution: AssetClassDistribution::default(),
1913            depreciation_distribution: DepreciationMethodDistribution::default(),
1914            fully_depreciated_percent: default_fully_depreciated_percent(),
1915            generate_acquisition_history: true,
1916        }
1917    }
1918}
1919
1920/// Asset class distribution.
1921#[derive(Debug, Clone, Serialize, Deserialize)]
1922pub struct AssetClassDistribution {
1923    /// Buildings and structures
1924    pub buildings: f64,
1925    /// Machinery and equipment
1926    pub machinery: f64,
1927    /// Vehicles
1928    pub vehicles: f64,
1929    /// IT equipment
1930    pub it_equipment: f64,
1931    /// Furniture and fixtures
1932    pub furniture: f64,
1933    /// Land (non-depreciable)
1934    pub land: f64,
1935    /// Leasehold improvements
1936    pub leasehold: f64,
1937}
1938
1939impl Default for AssetClassDistribution {
1940    fn default() -> Self {
1941        Self {
1942            buildings: 0.15,
1943            machinery: 0.30,
1944            vehicles: 0.15,
1945            it_equipment: 0.20,
1946            furniture: 0.10,
1947            land: 0.05,
1948            leasehold: 0.05,
1949        }
1950    }
1951}
1952
1953/// Depreciation method distribution.
1954#[derive(Debug, Clone, Serialize, Deserialize)]
1955pub struct DepreciationMethodDistribution {
1956    /// Straight line
1957    pub straight_line: f64,
1958    /// Declining balance
1959    pub declining_balance: f64,
1960    /// Double declining balance
1961    pub double_declining: f64,
1962    /// Sum of years' digits
1963    pub sum_of_years: f64,
1964    /// Units of production
1965    pub units_of_production: f64,
1966}
1967
1968impl Default for DepreciationMethodDistribution {
1969    fn default() -> Self {
1970        Self {
1971            straight_line: 0.60,
1972            declining_balance: 0.20,
1973            double_declining: 0.10,
1974            sum_of_years: 0.05,
1975            units_of_production: 0.05,
1976        }
1977    }
1978}
1979
1980/// Employee master data configuration.
1981#[derive(Debug, Clone, Serialize, Deserialize)]
1982pub struct EmployeeMasterConfig {
1983    /// Number of employees to generate
1984    #[serde(default = "default_employee_count")]
1985    pub count: usize,
1986    /// Generate organizational hierarchy
1987    #[serde(default = "default_true")]
1988    pub generate_hierarchy: bool,
1989    /// Maximum hierarchy depth
1990    #[serde(default = "default_hierarchy_depth")]
1991    pub max_hierarchy_depth: u8,
1992    /// Average span of control (direct reports per manager)
1993    #[serde(default = "default_span_of_control")]
1994    pub average_span_of_control: f64,
1995    /// Approval limit distribution by job level
1996    #[serde(default)]
1997    pub approval_limits: ApprovalLimitDistribution,
1998    /// Department distribution
1999    #[serde(default)]
2000    pub department_distribution: EmployeeDepartmentDistribution,
2001}
2002
2003fn default_employee_count() -> usize {
2004    1500
2005}
2006
2007fn default_hierarchy_depth() -> u8 {
2008    6
2009}
2010
2011fn default_span_of_control() -> f64 {
2012    5.0
2013}
2014
2015impl Default for EmployeeMasterConfig {
2016    fn default() -> Self {
2017        Self {
2018            count: default_employee_count(),
2019            generate_hierarchy: true,
2020            max_hierarchy_depth: default_hierarchy_depth(),
2021            average_span_of_control: default_span_of_control(),
2022            approval_limits: ApprovalLimitDistribution::default(),
2023            department_distribution: EmployeeDepartmentDistribution::default(),
2024        }
2025    }
2026}
2027
2028/// Approval limit distribution by job level.
2029#[derive(Debug, Clone, Serialize, Deserialize)]
2030pub struct ApprovalLimitDistribution {
2031    /// Staff level approval limit
2032    #[serde(default = "default_staff_limit")]
2033    pub staff: f64,
2034    /// Senior staff approval limit
2035    #[serde(default = "default_senior_limit")]
2036    pub senior: f64,
2037    /// Manager approval limit
2038    #[serde(default = "default_manager_limit")]
2039    pub manager: f64,
2040    /// Director approval limit
2041    #[serde(default = "default_director_limit")]
2042    pub director: f64,
2043    /// VP approval limit
2044    #[serde(default = "default_vp_limit")]
2045    pub vp: f64,
2046    /// Executive approval limit
2047    #[serde(default = "default_executive_limit")]
2048    pub executive: f64,
2049}
2050
2051fn default_staff_limit() -> f64 {
2052    1000.0
2053}
2054fn default_senior_limit() -> f64 {
2055    5000.0
2056}
2057fn default_manager_limit() -> f64 {
2058    25000.0
2059}
2060fn default_director_limit() -> f64 {
2061    100000.0
2062}
2063fn default_vp_limit() -> f64 {
2064    500000.0
2065}
2066fn default_executive_limit() -> f64 {
2067    f64::INFINITY
2068}
2069
2070impl Default for ApprovalLimitDistribution {
2071    fn default() -> Self {
2072        Self {
2073            staff: default_staff_limit(),
2074            senior: default_senior_limit(),
2075            manager: default_manager_limit(),
2076            director: default_director_limit(),
2077            vp: default_vp_limit(),
2078            executive: default_executive_limit(),
2079        }
2080    }
2081}
2082
2083/// Employee distribution across departments.
2084#[derive(Debug, Clone, Serialize, Deserialize)]
2085pub struct EmployeeDepartmentDistribution {
2086    /// Finance and Accounting
2087    pub finance: f64,
2088    /// Procurement
2089    pub procurement: f64,
2090    /// Sales
2091    pub sales: f64,
2092    /// Warehouse and Logistics
2093    pub warehouse: f64,
2094    /// IT
2095    pub it: f64,
2096    /// Human Resources
2097    pub hr: f64,
2098    /// Operations
2099    pub operations: f64,
2100    /// Executive
2101    pub executive: f64,
2102}
2103
2104impl Default for EmployeeDepartmentDistribution {
2105    fn default() -> Self {
2106        Self {
2107            finance: 0.12,
2108            procurement: 0.10,
2109            sales: 0.25,
2110            warehouse: 0.15,
2111            it: 0.10,
2112            hr: 0.05,
2113            operations: 0.20,
2114            executive: 0.03,
2115        }
2116    }
2117}
2118
2119/// Cost center master data configuration.
2120#[derive(Debug, Clone, Serialize, Deserialize)]
2121pub struct CostCenterMasterConfig {
2122    /// Number of cost centers to generate
2123    #[serde(default = "default_cost_center_count")]
2124    pub count: usize,
2125    /// Generate cost center hierarchy
2126    #[serde(default = "default_true")]
2127    pub generate_hierarchy: bool,
2128    /// Maximum hierarchy depth
2129    #[serde(default = "default_cc_hierarchy_depth")]
2130    pub max_hierarchy_depth: u8,
2131}
2132
2133fn default_cost_center_count() -> usize {
2134    50
2135}
2136
2137fn default_cc_hierarchy_depth() -> u8 {
2138    3
2139}
2140
2141impl Default for CostCenterMasterConfig {
2142    fn default() -> Self {
2143        Self {
2144            count: default_cost_center_count(),
2145            generate_hierarchy: true,
2146            max_hierarchy_depth: default_cc_hierarchy_depth(),
2147        }
2148    }
2149}
2150
2151// ============================================================================
2152// Document Flow Configuration
2153// ============================================================================
2154
2155/// Document flow generation configuration.
2156#[derive(Debug, Clone, Serialize, Deserialize)]
2157pub struct DocumentFlowConfig {
2158    /// P2P (Procure-to-Pay) flow configuration
2159    #[serde(default)]
2160    pub p2p: P2PFlowConfig,
2161    /// O2C (Order-to-Cash) flow configuration
2162    #[serde(default)]
2163    pub o2c: O2CFlowConfig,
2164    /// Generate document reference chains
2165    #[serde(default = "default_true")]
2166    pub generate_document_references: bool,
2167    /// Export document flow graph
2168    #[serde(default)]
2169    pub export_flow_graph: bool,
2170}
2171
2172impl Default for DocumentFlowConfig {
2173    fn default() -> Self {
2174        Self {
2175            p2p: P2PFlowConfig::default(),
2176            o2c: O2CFlowConfig::default(),
2177            generate_document_references: true,
2178            export_flow_graph: false,
2179        }
2180    }
2181}
2182
2183/// P2P (Procure-to-Pay) flow configuration.
2184#[derive(Debug, Clone, Serialize, Deserialize)]
2185pub struct P2PFlowConfig {
2186    /// Enable P2P document flow generation
2187    #[serde(default = "default_true")]
2188    pub enabled: bool,
2189    /// Three-way match success rate (PO-GR-Invoice)
2190    #[serde(default = "default_three_way_match_rate")]
2191    pub three_way_match_rate: f64,
2192    /// Rate of partial deliveries
2193    #[serde(default = "default_partial_delivery_rate")]
2194    pub partial_delivery_rate: f64,
2195    /// Rate of price variances between PO and Invoice
2196    #[serde(default = "default_price_variance_rate")]
2197    pub price_variance_rate: f64,
2198    /// Maximum price variance percentage
2199    #[serde(default = "default_max_price_variance")]
2200    pub max_price_variance_percent: f64,
2201    /// Rate of quantity variances between PO/GR and Invoice
2202    #[serde(default = "default_quantity_variance_rate")]
2203    pub quantity_variance_rate: f64,
2204    /// Average days from PO to goods receipt
2205    #[serde(default = "default_po_to_gr_days")]
2206    pub average_po_to_gr_days: u32,
2207    /// Average days from GR to invoice
2208    #[serde(default = "default_gr_to_invoice_days")]
2209    pub average_gr_to_invoice_days: u32,
2210    /// Average days from invoice to payment
2211    #[serde(default = "default_invoice_to_payment_days")]
2212    pub average_invoice_to_payment_days: u32,
2213    /// PO line count distribution
2214    #[serde(default)]
2215    pub line_count_distribution: DocumentLineCountDistribution,
2216    /// Payment behavior configuration
2217    #[serde(default)]
2218    pub payment_behavior: P2PPaymentBehaviorConfig,
2219}
2220
2221fn default_three_way_match_rate() -> f64 {
2222    0.95
2223}
2224
2225fn default_partial_delivery_rate() -> f64 {
2226    0.15
2227}
2228
2229fn default_price_variance_rate() -> f64 {
2230    0.08
2231}
2232
2233fn default_max_price_variance() -> f64 {
2234    0.05
2235}
2236
2237fn default_quantity_variance_rate() -> f64 {
2238    0.05
2239}
2240
2241fn default_po_to_gr_days() -> u32 {
2242    14
2243}
2244
2245fn default_gr_to_invoice_days() -> u32 {
2246    5
2247}
2248
2249fn default_invoice_to_payment_days() -> u32 {
2250    30
2251}
2252
2253impl Default for P2PFlowConfig {
2254    fn default() -> Self {
2255        Self {
2256            enabled: true,
2257            three_way_match_rate: default_three_way_match_rate(),
2258            partial_delivery_rate: default_partial_delivery_rate(),
2259            price_variance_rate: default_price_variance_rate(),
2260            max_price_variance_percent: default_max_price_variance(),
2261            quantity_variance_rate: default_quantity_variance_rate(),
2262            average_po_to_gr_days: default_po_to_gr_days(),
2263            average_gr_to_invoice_days: default_gr_to_invoice_days(),
2264            average_invoice_to_payment_days: default_invoice_to_payment_days(),
2265            line_count_distribution: DocumentLineCountDistribution::default(),
2266            payment_behavior: P2PPaymentBehaviorConfig::default(),
2267        }
2268    }
2269}
2270
2271// ============================================================================
2272// P2P Payment Behavior Configuration
2273// ============================================================================
2274
2275/// P2P payment behavior configuration.
2276#[derive(Debug, Clone, Serialize, Deserialize)]
2277pub struct P2PPaymentBehaviorConfig {
2278    /// Rate of late payments (beyond due date)
2279    #[serde(default = "default_p2p_late_payment_rate")]
2280    pub late_payment_rate: f64,
2281    /// Distribution of late payment days
2282    #[serde(default)]
2283    pub late_payment_days_distribution: LatePaymentDaysDistribution,
2284    /// Rate of partial payments
2285    #[serde(default = "default_p2p_partial_payment_rate")]
2286    pub partial_payment_rate: f64,
2287    /// Rate of payment corrections (NSF, chargebacks, reversals)
2288    #[serde(default = "default_p2p_payment_correction_rate")]
2289    pub payment_correction_rate: f64,
2290}
2291
2292fn default_p2p_late_payment_rate() -> f64 {
2293    0.15
2294}
2295
2296fn default_p2p_partial_payment_rate() -> f64 {
2297    0.05
2298}
2299
2300fn default_p2p_payment_correction_rate() -> f64 {
2301    0.02
2302}
2303
2304impl Default for P2PPaymentBehaviorConfig {
2305    fn default() -> Self {
2306        Self {
2307            late_payment_rate: default_p2p_late_payment_rate(),
2308            late_payment_days_distribution: LatePaymentDaysDistribution::default(),
2309            partial_payment_rate: default_p2p_partial_payment_rate(),
2310            payment_correction_rate: default_p2p_payment_correction_rate(),
2311        }
2312    }
2313}
2314
2315/// Distribution of late payment days for P2P.
2316#[derive(Debug, Clone, Serialize, Deserialize)]
2317pub struct LatePaymentDaysDistribution {
2318    /// 1-7 days late (slightly late)
2319    #[serde(default = "default_slightly_late")]
2320    pub slightly_late_1_to_7: f64,
2321    /// 8-14 days late
2322    #[serde(default = "default_late_8_14")]
2323    pub late_8_to_14: f64,
2324    /// 15-30 days late (very late)
2325    #[serde(default = "default_very_late")]
2326    pub very_late_15_to_30: f64,
2327    /// 31-60 days late (severely late)
2328    #[serde(default = "default_severely_late")]
2329    pub severely_late_31_to_60: f64,
2330    /// Over 60 days late (extremely late)
2331    #[serde(default = "default_extremely_late")]
2332    pub extremely_late_over_60: f64,
2333}
2334
2335fn default_slightly_late() -> f64 {
2336    0.50
2337}
2338
2339fn default_late_8_14() -> f64 {
2340    0.25
2341}
2342
2343fn default_very_late() -> f64 {
2344    0.15
2345}
2346
2347fn default_severely_late() -> f64 {
2348    0.07
2349}
2350
2351fn default_extremely_late() -> f64 {
2352    0.03
2353}
2354
2355impl Default for LatePaymentDaysDistribution {
2356    fn default() -> Self {
2357        Self {
2358            slightly_late_1_to_7: default_slightly_late(),
2359            late_8_to_14: default_late_8_14(),
2360            very_late_15_to_30: default_very_late(),
2361            severely_late_31_to_60: default_severely_late(),
2362            extremely_late_over_60: default_extremely_late(),
2363        }
2364    }
2365}
2366
2367/// O2C (Order-to-Cash) flow configuration.
2368#[derive(Debug, Clone, Serialize, Deserialize)]
2369pub struct O2CFlowConfig {
2370    /// Enable O2C document flow generation
2371    #[serde(default = "default_true")]
2372    pub enabled: bool,
2373    /// Credit check failure rate
2374    #[serde(default = "default_credit_check_failure_rate")]
2375    pub credit_check_failure_rate: f64,
2376    /// Rate of partial shipments
2377    #[serde(default = "default_partial_shipment_rate")]
2378    pub partial_shipment_rate: f64,
2379    /// Rate of returns
2380    #[serde(default = "default_return_rate")]
2381    pub return_rate: f64,
2382    /// Bad debt write-off rate
2383    #[serde(default = "default_bad_debt_rate")]
2384    pub bad_debt_rate: f64,
2385    /// Average days from SO to delivery
2386    #[serde(default = "default_so_to_delivery_days")]
2387    pub average_so_to_delivery_days: u32,
2388    /// Average days from delivery to invoice
2389    #[serde(default = "default_delivery_to_invoice_days")]
2390    pub average_delivery_to_invoice_days: u32,
2391    /// Average days from invoice to receipt
2392    #[serde(default = "default_invoice_to_receipt_days")]
2393    pub average_invoice_to_receipt_days: u32,
2394    /// SO line count distribution
2395    #[serde(default)]
2396    pub line_count_distribution: DocumentLineCountDistribution,
2397    /// Cash discount configuration
2398    #[serde(default)]
2399    pub cash_discount: CashDiscountConfig,
2400    /// Payment behavior configuration
2401    #[serde(default)]
2402    pub payment_behavior: O2CPaymentBehaviorConfig,
2403}
2404
2405fn default_credit_check_failure_rate() -> f64 {
2406    0.02
2407}
2408
2409fn default_partial_shipment_rate() -> f64 {
2410    0.10
2411}
2412
2413fn default_return_rate() -> f64 {
2414    0.03
2415}
2416
2417fn default_bad_debt_rate() -> f64 {
2418    0.01
2419}
2420
2421fn default_so_to_delivery_days() -> u32 {
2422    7
2423}
2424
2425fn default_delivery_to_invoice_days() -> u32 {
2426    1
2427}
2428
2429fn default_invoice_to_receipt_days() -> u32 {
2430    45
2431}
2432
2433impl Default for O2CFlowConfig {
2434    fn default() -> Self {
2435        Self {
2436            enabled: true,
2437            credit_check_failure_rate: default_credit_check_failure_rate(),
2438            partial_shipment_rate: default_partial_shipment_rate(),
2439            return_rate: default_return_rate(),
2440            bad_debt_rate: default_bad_debt_rate(),
2441            average_so_to_delivery_days: default_so_to_delivery_days(),
2442            average_delivery_to_invoice_days: default_delivery_to_invoice_days(),
2443            average_invoice_to_receipt_days: default_invoice_to_receipt_days(),
2444            line_count_distribution: DocumentLineCountDistribution::default(),
2445            cash_discount: CashDiscountConfig::default(),
2446            payment_behavior: O2CPaymentBehaviorConfig::default(),
2447        }
2448    }
2449}
2450
2451// ============================================================================
2452// O2C Payment Behavior Configuration
2453// ============================================================================
2454
2455/// O2C payment behavior configuration.
2456#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2457pub struct O2CPaymentBehaviorConfig {
2458    /// Dunning (Mahnung) configuration
2459    #[serde(default)]
2460    pub dunning: DunningConfig,
2461    /// Partial payment configuration
2462    #[serde(default)]
2463    pub partial_payments: PartialPaymentConfig,
2464    /// Short payment configuration (unauthorized deductions)
2465    #[serde(default)]
2466    pub short_payments: ShortPaymentConfig,
2467    /// On-account payment configuration (unapplied payments)
2468    #[serde(default)]
2469    pub on_account_payments: OnAccountPaymentConfig,
2470    /// Payment correction configuration (NSF, chargebacks)
2471    #[serde(default)]
2472    pub payment_corrections: PaymentCorrectionConfig,
2473}
2474
2475/// Dunning (Mahnungen) configuration for AR collections.
2476#[derive(Debug, Clone, Serialize, Deserialize)]
2477pub struct DunningConfig {
2478    /// Enable dunning process
2479    #[serde(default)]
2480    pub enabled: bool,
2481    /// Days overdue for level 1 dunning (1st reminder)
2482    #[serde(default = "default_dunning_level_1_days")]
2483    pub level_1_days_overdue: u32,
2484    /// Days overdue for level 2 dunning (2nd reminder)
2485    #[serde(default = "default_dunning_level_2_days")]
2486    pub level_2_days_overdue: u32,
2487    /// Days overdue for level 3 dunning (final notice)
2488    #[serde(default = "default_dunning_level_3_days")]
2489    pub level_3_days_overdue: u32,
2490    /// Days overdue for collection handover
2491    #[serde(default = "default_collection_days")]
2492    pub collection_days_overdue: u32,
2493    /// Payment rates after each dunning level
2494    #[serde(default)]
2495    pub payment_after_dunning_rates: DunningPaymentRates,
2496    /// Rate of invoices blocked from dunning (disputes)
2497    #[serde(default = "default_dunning_block_rate")]
2498    pub dunning_block_rate: f64,
2499    /// Interest rate per year for overdue amounts
2500    #[serde(default = "default_dunning_interest_rate")]
2501    pub interest_rate_per_year: f64,
2502    /// Fixed dunning charge per letter
2503    #[serde(default = "default_dunning_charge")]
2504    pub dunning_charge: f64,
2505}
2506
2507fn default_dunning_level_1_days() -> u32 {
2508    14
2509}
2510
2511fn default_dunning_level_2_days() -> u32 {
2512    28
2513}
2514
2515fn default_dunning_level_3_days() -> u32 {
2516    42
2517}
2518
2519fn default_collection_days() -> u32 {
2520    60
2521}
2522
2523fn default_dunning_block_rate() -> f64 {
2524    0.05
2525}
2526
2527fn default_dunning_interest_rate() -> f64 {
2528    0.09
2529}
2530
2531fn default_dunning_charge() -> f64 {
2532    25.0
2533}
2534
2535impl Default for DunningConfig {
2536    fn default() -> Self {
2537        Self {
2538            enabled: false,
2539            level_1_days_overdue: default_dunning_level_1_days(),
2540            level_2_days_overdue: default_dunning_level_2_days(),
2541            level_3_days_overdue: default_dunning_level_3_days(),
2542            collection_days_overdue: default_collection_days(),
2543            payment_after_dunning_rates: DunningPaymentRates::default(),
2544            dunning_block_rate: default_dunning_block_rate(),
2545            interest_rate_per_year: default_dunning_interest_rate(),
2546            dunning_charge: default_dunning_charge(),
2547        }
2548    }
2549}
2550
2551/// Payment rates after each dunning level.
2552#[derive(Debug, Clone, Serialize, Deserialize)]
2553pub struct DunningPaymentRates {
2554    /// Rate that pays after level 1 reminder
2555    #[serde(default = "default_after_level_1")]
2556    pub after_level_1: f64,
2557    /// Rate that pays after level 2 reminder
2558    #[serde(default = "default_after_level_2")]
2559    pub after_level_2: f64,
2560    /// Rate that pays after level 3 final notice
2561    #[serde(default = "default_after_level_3")]
2562    pub after_level_3: f64,
2563    /// Rate that pays during collection
2564    #[serde(default = "default_during_collection")]
2565    pub during_collection: f64,
2566    /// Rate that never pays (becomes bad debt)
2567    #[serde(default = "default_never_pay")]
2568    pub never_pay: f64,
2569}
2570
2571fn default_after_level_1() -> f64 {
2572    0.40
2573}
2574
2575fn default_after_level_2() -> f64 {
2576    0.30
2577}
2578
2579fn default_after_level_3() -> f64 {
2580    0.15
2581}
2582
2583fn default_during_collection() -> f64 {
2584    0.05
2585}
2586
2587fn default_never_pay() -> f64 {
2588    0.10
2589}
2590
2591impl Default for DunningPaymentRates {
2592    fn default() -> Self {
2593        Self {
2594            after_level_1: default_after_level_1(),
2595            after_level_2: default_after_level_2(),
2596            after_level_3: default_after_level_3(),
2597            during_collection: default_during_collection(),
2598            never_pay: default_never_pay(),
2599        }
2600    }
2601}
2602
2603/// Partial payment configuration.
2604#[derive(Debug, Clone, Serialize, Deserialize)]
2605pub struct PartialPaymentConfig {
2606    /// Rate of invoices paid partially
2607    #[serde(default = "default_partial_payment_rate")]
2608    pub rate: f64,
2609    /// Distribution of partial payment percentages
2610    #[serde(default)]
2611    pub percentage_distribution: PartialPaymentPercentageDistribution,
2612    /// Average days until remainder is paid
2613    #[serde(default = "default_avg_days_until_remainder")]
2614    pub avg_days_until_remainder: u32,
2615}
2616
2617fn default_partial_payment_rate() -> f64 {
2618    0.08
2619}
2620
2621fn default_avg_days_until_remainder() -> u32 {
2622    30
2623}
2624
2625impl Default for PartialPaymentConfig {
2626    fn default() -> Self {
2627        Self {
2628            rate: default_partial_payment_rate(),
2629            percentage_distribution: PartialPaymentPercentageDistribution::default(),
2630            avg_days_until_remainder: default_avg_days_until_remainder(),
2631        }
2632    }
2633}
2634
2635/// Distribution of partial payment percentages.
2636#[derive(Debug, Clone, Serialize, Deserialize)]
2637pub struct PartialPaymentPercentageDistribution {
2638    /// Pay 25% of invoice
2639    #[serde(default = "default_partial_25")]
2640    pub pay_25_percent: f64,
2641    /// Pay 50% of invoice
2642    #[serde(default = "default_partial_50")]
2643    pub pay_50_percent: f64,
2644    /// Pay 75% of invoice
2645    #[serde(default = "default_partial_75")]
2646    pub pay_75_percent: f64,
2647    /// Pay random percentage
2648    #[serde(default = "default_partial_random")]
2649    pub pay_random_percent: f64,
2650}
2651
2652fn default_partial_25() -> f64 {
2653    0.15
2654}
2655
2656fn default_partial_50() -> f64 {
2657    0.50
2658}
2659
2660fn default_partial_75() -> f64 {
2661    0.25
2662}
2663
2664fn default_partial_random() -> f64 {
2665    0.10
2666}
2667
2668impl Default for PartialPaymentPercentageDistribution {
2669    fn default() -> Self {
2670        Self {
2671            pay_25_percent: default_partial_25(),
2672            pay_50_percent: default_partial_50(),
2673            pay_75_percent: default_partial_75(),
2674            pay_random_percent: default_partial_random(),
2675        }
2676    }
2677}
2678
2679/// Short payment configuration (unauthorized deductions).
2680#[derive(Debug, Clone, Serialize, Deserialize)]
2681pub struct ShortPaymentConfig {
2682    /// Rate of payments that are short
2683    #[serde(default = "default_short_payment_rate")]
2684    pub rate: f64,
2685    /// Distribution of short payment reasons
2686    #[serde(default)]
2687    pub reason_distribution: ShortPaymentReasonDistribution,
2688    /// Maximum percentage that can be short
2689    #[serde(default = "default_max_short_percent")]
2690    pub max_short_percent: f64,
2691}
2692
2693fn default_short_payment_rate() -> f64 {
2694    0.03
2695}
2696
2697fn default_max_short_percent() -> f64 {
2698    0.10
2699}
2700
2701impl Default for ShortPaymentConfig {
2702    fn default() -> Self {
2703        Self {
2704            rate: default_short_payment_rate(),
2705            reason_distribution: ShortPaymentReasonDistribution::default(),
2706            max_short_percent: default_max_short_percent(),
2707        }
2708    }
2709}
2710
2711/// Distribution of short payment reasons.
2712#[derive(Debug, Clone, Serialize, Deserialize)]
2713pub struct ShortPaymentReasonDistribution {
2714    /// Pricing dispute
2715    #[serde(default = "default_pricing_dispute")]
2716    pub pricing_dispute: f64,
2717    /// Quality issue
2718    #[serde(default = "default_quality_issue")]
2719    pub quality_issue: f64,
2720    /// Quantity discrepancy
2721    #[serde(default = "default_quantity_discrepancy")]
2722    pub quantity_discrepancy: f64,
2723    /// Unauthorized deduction
2724    #[serde(default = "default_unauthorized_deduction")]
2725    pub unauthorized_deduction: f64,
2726    /// Early payment discount taken incorrectly
2727    #[serde(default = "default_incorrect_discount")]
2728    pub incorrect_discount: f64,
2729}
2730
2731fn default_pricing_dispute() -> f64 {
2732    0.30
2733}
2734
2735fn default_quality_issue() -> f64 {
2736    0.20
2737}
2738
2739fn default_quantity_discrepancy() -> f64 {
2740    0.20
2741}
2742
2743fn default_unauthorized_deduction() -> f64 {
2744    0.15
2745}
2746
2747fn default_incorrect_discount() -> f64 {
2748    0.15
2749}
2750
2751impl Default for ShortPaymentReasonDistribution {
2752    fn default() -> Self {
2753        Self {
2754            pricing_dispute: default_pricing_dispute(),
2755            quality_issue: default_quality_issue(),
2756            quantity_discrepancy: default_quantity_discrepancy(),
2757            unauthorized_deduction: default_unauthorized_deduction(),
2758            incorrect_discount: default_incorrect_discount(),
2759        }
2760    }
2761}
2762
2763/// On-account payment configuration (unapplied payments).
2764#[derive(Debug, Clone, Serialize, Deserialize)]
2765pub struct OnAccountPaymentConfig {
2766    /// Rate of payments that are on-account (unapplied)
2767    #[serde(default = "default_on_account_rate")]
2768    pub rate: f64,
2769    /// Average days until on-account payments are applied
2770    #[serde(default = "default_avg_days_until_applied")]
2771    pub avg_days_until_applied: u32,
2772}
2773
2774fn default_on_account_rate() -> f64 {
2775    0.02
2776}
2777
2778fn default_avg_days_until_applied() -> u32 {
2779    14
2780}
2781
2782impl Default for OnAccountPaymentConfig {
2783    fn default() -> Self {
2784        Self {
2785            rate: default_on_account_rate(),
2786            avg_days_until_applied: default_avg_days_until_applied(),
2787        }
2788    }
2789}
2790
2791/// Payment correction configuration.
2792#[derive(Debug, Clone, Serialize, Deserialize)]
2793pub struct PaymentCorrectionConfig {
2794    /// Rate of payments requiring correction
2795    #[serde(default = "default_payment_correction_rate")]
2796    pub rate: f64,
2797    /// Distribution of correction types
2798    #[serde(default)]
2799    pub type_distribution: PaymentCorrectionTypeDistribution,
2800}
2801
2802fn default_payment_correction_rate() -> f64 {
2803    0.02
2804}
2805
2806impl Default for PaymentCorrectionConfig {
2807    fn default() -> Self {
2808        Self {
2809            rate: default_payment_correction_rate(),
2810            type_distribution: PaymentCorrectionTypeDistribution::default(),
2811        }
2812    }
2813}
2814
2815/// Distribution of payment correction types.
2816#[derive(Debug, Clone, Serialize, Deserialize)]
2817pub struct PaymentCorrectionTypeDistribution {
2818    /// NSF (Non-sufficient funds) / bounced check
2819    #[serde(default = "default_nsf_rate")]
2820    pub nsf: f64,
2821    /// Chargeback
2822    #[serde(default = "default_chargeback_rate")]
2823    pub chargeback: f64,
2824    /// Wrong amount applied
2825    #[serde(default = "default_wrong_amount_rate")]
2826    pub wrong_amount: f64,
2827    /// Wrong customer applied
2828    #[serde(default = "default_wrong_customer_rate")]
2829    pub wrong_customer: f64,
2830    /// Duplicate payment
2831    #[serde(default = "default_duplicate_payment_rate")]
2832    pub duplicate_payment: f64,
2833}
2834
2835fn default_nsf_rate() -> f64 {
2836    0.30
2837}
2838
2839fn default_chargeback_rate() -> f64 {
2840    0.20
2841}
2842
2843fn default_wrong_amount_rate() -> f64 {
2844    0.20
2845}
2846
2847fn default_wrong_customer_rate() -> f64 {
2848    0.15
2849}
2850
2851fn default_duplicate_payment_rate() -> f64 {
2852    0.15
2853}
2854
2855impl Default for PaymentCorrectionTypeDistribution {
2856    fn default() -> Self {
2857        Self {
2858            nsf: default_nsf_rate(),
2859            chargeback: default_chargeback_rate(),
2860            wrong_amount: default_wrong_amount_rate(),
2861            wrong_customer: default_wrong_customer_rate(),
2862            duplicate_payment: default_duplicate_payment_rate(),
2863        }
2864    }
2865}
2866
2867/// Document line count distribution.
2868#[derive(Debug, Clone, Serialize, Deserialize)]
2869pub struct DocumentLineCountDistribution {
2870    /// Minimum number of lines
2871    #[serde(default = "default_min_lines")]
2872    pub min_lines: u32,
2873    /// Maximum number of lines
2874    #[serde(default = "default_max_lines")]
2875    pub max_lines: u32,
2876    /// Most common line count (mode)
2877    #[serde(default = "default_mode_lines")]
2878    pub mode_lines: u32,
2879}
2880
2881fn default_min_lines() -> u32 {
2882    1
2883}
2884
2885fn default_max_lines() -> u32 {
2886    20
2887}
2888
2889fn default_mode_lines() -> u32 {
2890    3
2891}
2892
2893impl Default for DocumentLineCountDistribution {
2894    fn default() -> Self {
2895        Self {
2896            min_lines: default_min_lines(),
2897            max_lines: default_max_lines(),
2898            mode_lines: default_mode_lines(),
2899        }
2900    }
2901}
2902
2903/// Cash discount configuration.
2904#[derive(Debug, Clone, Serialize, Deserialize)]
2905pub struct CashDiscountConfig {
2906    /// Percentage of invoices eligible for cash discount
2907    #[serde(default = "default_discount_eligible_rate")]
2908    pub eligible_rate: f64,
2909    /// Rate at which customers take the discount
2910    #[serde(default = "default_discount_taken_rate")]
2911    pub taken_rate: f64,
2912    /// Standard discount percentage
2913    #[serde(default = "default_discount_percent")]
2914    pub discount_percent: f64,
2915    /// Days within which discount must be taken
2916    #[serde(default = "default_discount_days")]
2917    pub discount_days: u32,
2918}
2919
2920fn default_discount_eligible_rate() -> f64 {
2921    0.30
2922}
2923
2924fn default_discount_taken_rate() -> f64 {
2925    0.60
2926}
2927
2928fn default_discount_percent() -> f64 {
2929    0.02
2930}
2931
2932fn default_discount_days() -> u32 {
2933    10
2934}
2935
2936impl Default for CashDiscountConfig {
2937    fn default() -> Self {
2938        Self {
2939            eligible_rate: default_discount_eligible_rate(),
2940            taken_rate: default_discount_taken_rate(),
2941            discount_percent: default_discount_percent(),
2942            discount_days: default_discount_days(),
2943        }
2944    }
2945}
2946
2947// ============================================================================
2948// Intercompany Configuration
2949// ============================================================================
2950
2951/// Intercompany transaction configuration.
2952#[derive(Debug, Clone, Serialize, Deserialize)]
2953pub struct IntercompanyConfig {
2954    /// Enable intercompany transaction generation
2955    #[serde(default)]
2956    pub enabled: bool,
2957    /// Rate of transactions that are intercompany
2958    #[serde(default = "default_ic_transaction_rate")]
2959    pub ic_transaction_rate: f64,
2960    /// Transfer pricing method
2961    #[serde(default)]
2962    pub transfer_pricing_method: TransferPricingMethod,
2963    /// Transfer pricing markup percentage (for cost-plus)
2964    #[serde(default = "default_markup_percent")]
2965    pub markup_percent: f64,
2966    /// Generate matched IC pairs (offsetting entries)
2967    #[serde(default = "default_true")]
2968    pub generate_matched_pairs: bool,
2969    /// IC transaction type distribution
2970    #[serde(default)]
2971    pub transaction_type_distribution: ICTransactionTypeDistribution,
2972    /// Generate elimination entries for consolidation
2973    #[serde(default)]
2974    pub generate_eliminations: bool,
2975}
2976
2977fn default_ic_transaction_rate() -> f64 {
2978    0.15
2979}
2980
2981fn default_markup_percent() -> f64 {
2982    0.05
2983}
2984
2985impl Default for IntercompanyConfig {
2986    fn default() -> Self {
2987        Self {
2988            enabled: false,
2989            ic_transaction_rate: default_ic_transaction_rate(),
2990            transfer_pricing_method: TransferPricingMethod::default(),
2991            markup_percent: default_markup_percent(),
2992            generate_matched_pairs: true,
2993            transaction_type_distribution: ICTransactionTypeDistribution::default(),
2994            generate_eliminations: false,
2995        }
2996    }
2997}
2998
2999/// Transfer pricing method.
3000#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
3001#[serde(rename_all = "snake_case")]
3002pub enum TransferPricingMethod {
3003    /// Cost plus a markup
3004    #[default]
3005    CostPlus,
3006    /// Comparable uncontrolled price
3007    ComparableUncontrolled,
3008    /// Resale price method
3009    ResalePrice,
3010    /// Transactional net margin method
3011    TransactionalNetMargin,
3012    /// Profit split method
3013    ProfitSplit,
3014}
3015
3016/// IC transaction type distribution.
3017#[derive(Debug, Clone, Serialize, Deserialize)]
3018pub struct ICTransactionTypeDistribution {
3019    /// Goods sales between entities
3020    pub goods_sale: f64,
3021    /// Services provided
3022    pub service_provided: f64,
3023    /// Intercompany loans
3024    pub loan: f64,
3025    /// Dividends
3026    pub dividend: f64,
3027    /// Management fees
3028    pub management_fee: f64,
3029    /// Royalties
3030    pub royalty: f64,
3031    /// Cost sharing
3032    pub cost_sharing: f64,
3033}
3034
3035impl Default for ICTransactionTypeDistribution {
3036    fn default() -> Self {
3037        Self {
3038            goods_sale: 0.35,
3039            service_provided: 0.20,
3040            loan: 0.10,
3041            dividend: 0.05,
3042            management_fee: 0.15,
3043            royalty: 0.10,
3044            cost_sharing: 0.05,
3045        }
3046    }
3047}
3048
3049// ============================================================================
3050// Balance Configuration
3051// ============================================================================
3052
3053/// Balance and trial balance configuration.
3054#[derive(Debug, Clone, Serialize, Deserialize)]
3055pub struct BalanceConfig {
3056    /// Generate opening balances
3057    #[serde(default)]
3058    pub generate_opening_balances: bool,
3059    /// Generate trial balances
3060    #[serde(default = "default_true")]
3061    pub generate_trial_balances: bool,
3062    /// Target gross margin (for revenue/COGS coherence)
3063    #[serde(default = "default_gross_margin")]
3064    pub target_gross_margin: f64,
3065    /// Target DSO (Days Sales Outstanding)
3066    #[serde(default = "default_dso")]
3067    pub target_dso_days: u32,
3068    /// Target DPO (Days Payable Outstanding)
3069    #[serde(default = "default_dpo")]
3070    pub target_dpo_days: u32,
3071    /// Target current ratio
3072    #[serde(default = "default_current_ratio")]
3073    pub target_current_ratio: f64,
3074    /// Target debt-to-equity ratio
3075    #[serde(default = "default_debt_equity")]
3076    pub target_debt_to_equity: f64,
3077    /// Validate balance sheet equation (A = L + E)
3078    #[serde(default = "default_true")]
3079    pub validate_balance_equation: bool,
3080    /// Reconcile subledgers to GL control accounts
3081    #[serde(default = "default_true")]
3082    pub reconcile_subledgers: bool,
3083}
3084
3085fn default_gross_margin() -> f64 {
3086    0.35
3087}
3088
3089fn default_dso() -> u32 {
3090    45
3091}
3092
3093fn default_dpo() -> u32 {
3094    30
3095}
3096
3097fn default_current_ratio() -> f64 {
3098    1.5
3099}
3100
3101fn default_debt_equity() -> f64 {
3102    0.5
3103}
3104
3105impl Default for BalanceConfig {
3106    fn default() -> Self {
3107        Self {
3108            generate_opening_balances: false,
3109            generate_trial_balances: true,
3110            target_gross_margin: default_gross_margin(),
3111            target_dso_days: default_dso(),
3112            target_dpo_days: default_dpo(),
3113            target_current_ratio: default_current_ratio(),
3114            target_debt_to_equity: default_debt_equity(),
3115            validate_balance_equation: true,
3116            reconcile_subledgers: true,
3117        }
3118    }
3119}
3120
3121// ==========================================================================
3122// OCPM (Object-Centric Process Mining) Configuration
3123// ==========================================================================
3124
3125/// OCPM (Object-Centric Process Mining) configuration.
3126///
3127/// Controls generation of OCEL 2.0 compatible event logs with
3128/// many-to-many event-to-object relationships.
3129#[derive(Debug, Clone, Serialize, Deserialize)]
3130pub struct OcpmConfig {
3131    /// Enable OCPM event log generation
3132    #[serde(default)]
3133    pub enabled: bool,
3134
3135    /// Generate lifecycle events (Start/Complete pairs vs atomic events)
3136    #[serde(default = "default_true")]
3137    pub generate_lifecycle_events: bool,
3138
3139    /// Include object-to-object relationships in output
3140    #[serde(default = "default_true")]
3141    pub include_object_relationships: bool,
3142
3143    /// Compute and export process variants
3144    #[serde(default = "default_true")]
3145    pub compute_variants: bool,
3146
3147    /// Maximum variants to track (0 = unlimited)
3148    #[serde(default)]
3149    pub max_variants: usize,
3150
3151    /// P2P process configuration
3152    #[serde(default)]
3153    pub p2p_process: OcpmProcessConfig,
3154
3155    /// O2C process configuration
3156    #[serde(default)]
3157    pub o2c_process: OcpmProcessConfig,
3158
3159    /// Output format configuration
3160    #[serde(default)]
3161    pub output: OcpmOutputConfig,
3162}
3163
3164impl Default for OcpmConfig {
3165    fn default() -> Self {
3166        Self {
3167            enabled: false,
3168            generate_lifecycle_events: true,
3169            include_object_relationships: true,
3170            compute_variants: true,
3171            max_variants: 0,
3172            p2p_process: OcpmProcessConfig::default(),
3173            o2c_process: OcpmProcessConfig::default(),
3174            output: OcpmOutputConfig::default(),
3175        }
3176    }
3177}
3178
3179/// Process-specific OCPM configuration.
3180#[derive(Debug, Clone, Serialize, Deserialize)]
3181pub struct OcpmProcessConfig {
3182    /// Rework probability (0.0-1.0)
3183    #[serde(default = "default_rework_probability")]
3184    pub rework_probability: f64,
3185
3186    /// Skip step probability (0.0-1.0)
3187    #[serde(default = "default_skip_probability")]
3188    pub skip_step_probability: f64,
3189
3190    /// Out-of-order step probability (0.0-1.0)
3191    #[serde(default = "default_out_of_order_probability")]
3192    pub out_of_order_probability: f64,
3193}
3194
3195fn default_rework_probability() -> f64 {
3196    0.05
3197}
3198
3199fn default_skip_probability() -> f64 {
3200    0.02
3201}
3202
3203fn default_out_of_order_probability() -> f64 {
3204    0.03
3205}
3206
3207impl Default for OcpmProcessConfig {
3208    fn default() -> Self {
3209        Self {
3210            rework_probability: default_rework_probability(),
3211            skip_step_probability: default_skip_probability(),
3212            out_of_order_probability: default_out_of_order_probability(),
3213        }
3214    }
3215}
3216
3217/// OCPM output format configuration.
3218#[derive(Debug, Clone, Serialize, Deserialize)]
3219pub struct OcpmOutputConfig {
3220    /// Export OCEL 2.0 JSON format
3221    #[serde(default = "default_true")]
3222    pub ocel_json: bool,
3223
3224    /// Export OCEL 2.0 XML format
3225    #[serde(default)]
3226    pub ocel_xml: bool,
3227
3228    /// Export flattened CSV for each object type
3229    #[serde(default = "default_true")]
3230    pub flattened_csv: bool,
3231
3232    /// Export event-object relationship table
3233    #[serde(default = "default_true")]
3234    pub event_object_csv: bool,
3235
3236    /// Export object-object relationship table
3237    #[serde(default = "default_true")]
3238    pub object_relationship_csv: bool,
3239
3240    /// Export process variants summary
3241    #[serde(default = "default_true")]
3242    pub variants_csv: bool,
3243}
3244
3245impl Default for OcpmOutputConfig {
3246    fn default() -> Self {
3247        Self {
3248            ocel_json: true,
3249            ocel_xml: false,
3250            flattened_csv: true,
3251            event_object_csv: true,
3252            object_relationship_csv: true,
3253            variants_csv: true,
3254        }
3255    }
3256}
3257
3258/// Audit engagement and workpaper generation configuration.
3259#[derive(Debug, Clone, Serialize, Deserialize)]
3260pub struct AuditGenerationConfig {
3261    /// Enable audit engagement generation
3262    #[serde(default)]
3263    pub enabled: bool,
3264
3265    /// Generate engagement documents and workpapers
3266    #[serde(default = "default_true")]
3267    pub generate_workpapers: bool,
3268
3269    /// Default engagement type distribution
3270    #[serde(default)]
3271    pub engagement_types: AuditEngagementTypesConfig,
3272
3273    /// Workpaper configuration
3274    #[serde(default)]
3275    pub workpapers: WorkpaperConfig,
3276
3277    /// Team configuration
3278    #[serde(default)]
3279    pub team: AuditTeamConfig,
3280
3281    /// Review workflow configuration
3282    #[serde(default)]
3283    pub review: ReviewWorkflowConfig,
3284}
3285
3286impl Default for AuditGenerationConfig {
3287    fn default() -> Self {
3288        Self {
3289            enabled: false,
3290            generate_workpapers: true,
3291            engagement_types: AuditEngagementTypesConfig::default(),
3292            workpapers: WorkpaperConfig::default(),
3293            team: AuditTeamConfig::default(),
3294            review: ReviewWorkflowConfig::default(),
3295        }
3296    }
3297}
3298
3299/// Engagement type distribution configuration.
3300#[derive(Debug, Clone, Serialize, Deserialize)]
3301pub struct AuditEngagementTypesConfig {
3302    /// Financial statement audit probability
3303    #[serde(default = "default_financial_audit_prob")]
3304    pub financial_statement: f64,
3305    /// SOX/ICFR audit probability
3306    #[serde(default = "default_sox_audit_prob")]
3307    pub sox_icfr: f64,
3308    /// Integrated audit probability
3309    #[serde(default = "default_integrated_audit_prob")]
3310    pub integrated: f64,
3311    /// Review engagement probability
3312    #[serde(default = "default_review_prob")]
3313    pub review: f64,
3314    /// Agreed-upon procedures probability
3315    #[serde(default = "default_aup_prob")]
3316    pub agreed_upon_procedures: f64,
3317}
3318
3319fn default_financial_audit_prob() -> f64 {
3320    0.40
3321}
3322fn default_sox_audit_prob() -> f64 {
3323    0.20
3324}
3325fn default_integrated_audit_prob() -> f64 {
3326    0.25
3327}
3328fn default_review_prob() -> f64 {
3329    0.10
3330}
3331fn default_aup_prob() -> f64 {
3332    0.05
3333}
3334
3335impl Default for AuditEngagementTypesConfig {
3336    fn default() -> Self {
3337        Self {
3338            financial_statement: default_financial_audit_prob(),
3339            sox_icfr: default_sox_audit_prob(),
3340            integrated: default_integrated_audit_prob(),
3341            review: default_review_prob(),
3342            agreed_upon_procedures: default_aup_prob(),
3343        }
3344    }
3345}
3346
3347/// Workpaper generation configuration.
3348#[derive(Debug, Clone, Serialize, Deserialize)]
3349pub struct WorkpaperConfig {
3350    /// Average workpapers per engagement phase
3351    #[serde(default = "default_workpapers_per_phase")]
3352    pub average_per_phase: usize,
3353
3354    /// Include ISA compliance references
3355    #[serde(default = "default_true")]
3356    pub include_isa_references: bool,
3357
3358    /// Generate sample details
3359    #[serde(default = "default_true")]
3360    pub include_sample_details: bool,
3361
3362    /// Include cross-references between workpapers
3363    #[serde(default = "default_true")]
3364    pub include_cross_references: bool,
3365
3366    /// Sampling configuration
3367    #[serde(default)]
3368    pub sampling: SamplingConfig,
3369}
3370
3371fn default_workpapers_per_phase() -> usize {
3372    5
3373}
3374
3375impl Default for WorkpaperConfig {
3376    fn default() -> Self {
3377        Self {
3378            average_per_phase: default_workpapers_per_phase(),
3379            include_isa_references: true,
3380            include_sample_details: true,
3381            include_cross_references: true,
3382            sampling: SamplingConfig::default(),
3383        }
3384    }
3385}
3386
3387/// Sampling method configuration.
3388#[derive(Debug, Clone, Serialize, Deserialize)]
3389pub struct SamplingConfig {
3390    /// Statistical sampling rate (0.0-1.0)
3391    #[serde(default = "default_statistical_rate")]
3392    pub statistical_rate: f64,
3393    /// Judgmental sampling rate (0.0-1.0)
3394    #[serde(default = "default_judgmental_rate")]
3395    pub judgmental_rate: f64,
3396    /// Haphazard sampling rate (0.0-1.0)
3397    #[serde(default = "default_haphazard_rate")]
3398    pub haphazard_rate: f64,
3399    /// 100% examination rate (0.0-1.0)
3400    #[serde(default = "default_complete_examination_rate")]
3401    pub complete_examination_rate: f64,
3402}
3403
3404fn default_statistical_rate() -> f64 {
3405    0.40
3406}
3407fn default_judgmental_rate() -> f64 {
3408    0.30
3409}
3410fn default_haphazard_rate() -> f64 {
3411    0.20
3412}
3413fn default_complete_examination_rate() -> f64 {
3414    0.10
3415}
3416
3417impl Default for SamplingConfig {
3418    fn default() -> Self {
3419        Self {
3420            statistical_rate: default_statistical_rate(),
3421            judgmental_rate: default_judgmental_rate(),
3422            haphazard_rate: default_haphazard_rate(),
3423            complete_examination_rate: default_complete_examination_rate(),
3424        }
3425    }
3426}
3427
3428/// Audit team configuration.
3429#[derive(Debug, Clone, Serialize, Deserialize)]
3430pub struct AuditTeamConfig {
3431    /// Minimum team size
3432    #[serde(default = "default_min_team_size")]
3433    pub min_team_size: usize,
3434    /// Maximum team size
3435    #[serde(default = "default_max_team_size")]
3436    pub max_team_size: usize,
3437    /// Probability of having a specialist on the team
3438    #[serde(default = "default_specialist_probability")]
3439    pub specialist_probability: f64,
3440}
3441
3442fn default_min_team_size() -> usize {
3443    3
3444}
3445fn default_max_team_size() -> usize {
3446    8
3447}
3448fn default_specialist_probability() -> f64 {
3449    0.30
3450}
3451
3452impl Default for AuditTeamConfig {
3453    fn default() -> Self {
3454        Self {
3455            min_team_size: default_min_team_size(),
3456            max_team_size: default_max_team_size(),
3457            specialist_probability: default_specialist_probability(),
3458        }
3459    }
3460}
3461
3462/// Review workflow configuration.
3463#[derive(Debug, Clone, Serialize, Deserialize)]
3464pub struct ReviewWorkflowConfig {
3465    /// Average days between preparer completion and first review
3466    #[serde(default = "default_review_delay_days")]
3467    pub average_review_delay_days: u32,
3468    /// Probability of review notes requiring rework
3469    #[serde(default = "default_rework_probability_review")]
3470    pub rework_probability: f64,
3471    /// Require partner sign-off for all workpapers
3472    #[serde(default = "default_true")]
3473    pub require_partner_signoff: bool,
3474}
3475
3476fn default_review_delay_days() -> u32 {
3477    2
3478}
3479fn default_rework_probability_review() -> f64 {
3480    0.15
3481}
3482
3483impl Default for ReviewWorkflowConfig {
3484    fn default() -> Self {
3485        Self {
3486            average_review_delay_days: default_review_delay_days(),
3487            rework_probability: default_rework_probability_review(),
3488            require_partner_signoff: true,
3489        }
3490    }
3491}
3492
3493// =============================================================================
3494// Data Quality Configuration
3495// =============================================================================
3496
3497/// Data quality variation settings for realistic flakiness injection.
3498#[derive(Debug, Clone, Serialize, Deserialize)]
3499pub struct DataQualitySchemaConfig {
3500    /// Enable data quality variations
3501    #[serde(default)]
3502    pub enabled: bool,
3503    /// Preset to use (overrides individual settings if set)
3504    #[serde(default)]
3505    pub preset: DataQualityPreset,
3506    /// Missing value injection settings
3507    #[serde(default)]
3508    pub missing_values: MissingValuesSchemaConfig,
3509    /// Typo injection settings
3510    #[serde(default)]
3511    pub typos: TypoSchemaConfig,
3512    /// Format variation settings
3513    #[serde(default)]
3514    pub format_variations: FormatVariationSchemaConfig,
3515    /// Duplicate injection settings
3516    #[serde(default)]
3517    pub duplicates: DuplicateSchemaConfig,
3518    /// Encoding issue settings
3519    #[serde(default)]
3520    pub encoding_issues: EncodingIssueSchemaConfig,
3521    /// Generate quality issue labels for ML training
3522    #[serde(default)]
3523    pub generate_labels: bool,
3524    /// Per-sink quality profiles (different settings for CSV vs JSON etc.)
3525    #[serde(default)]
3526    pub sink_profiles: SinkQualityProfiles,
3527}
3528
3529impl Default for DataQualitySchemaConfig {
3530    fn default() -> Self {
3531        Self {
3532            enabled: false,
3533            preset: DataQualityPreset::None,
3534            missing_values: MissingValuesSchemaConfig::default(),
3535            typos: TypoSchemaConfig::default(),
3536            format_variations: FormatVariationSchemaConfig::default(),
3537            duplicates: DuplicateSchemaConfig::default(),
3538            encoding_issues: EncodingIssueSchemaConfig::default(),
3539            generate_labels: true,
3540            sink_profiles: SinkQualityProfiles::default(),
3541        }
3542    }
3543}
3544
3545impl DataQualitySchemaConfig {
3546    /// Creates a config for a specific preset profile.
3547    pub fn with_preset(preset: DataQualityPreset) -> Self {
3548        let mut config = Self {
3549            preset,
3550            ..Default::default()
3551        };
3552        config.apply_preset();
3553        config
3554    }
3555
3556    /// Applies the preset settings to the individual configuration fields.
3557    /// Call this after deserializing if preset is not Custom or None.
3558    pub fn apply_preset(&mut self) {
3559        if !self.preset.overrides_settings() {
3560            return;
3561        }
3562
3563        self.enabled = true;
3564
3565        // Missing values
3566        self.missing_values.enabled = self.preset.missing_rate() > 0.0;
3567        self.missing_values.rate = self.preset.missing_rate();
3568
3569        // Typos
3570        self.typos.enabled = self.preset.typo_rate() > 0.0;
3571        self.typos.char_error_rate = self.preset.typo_rate();
3572
3573        // Duplicates
3574        self.duplicates.enabled = self.preset.duplicate_rate() > 0.0;
3575        self.duplicates.exact_duplicate_ratio = self.preset.duplicate_rate() * 0.4;
3576        self.duplicates.near_duplicate_ratio = self.preset.duplicate_rate() * 0.4;
3577        self.duplicates.fuzzy_duplicate_ratio = self.preset.duplicate_rate() * 0.2;
3578
3579        // Format variations
3580        self.format_variations.enabled = self.preset.format_variations_enabled();
3581
3582        // Encoding issues
3583        self.encoding_issues.enabled = self.preset.encoding_issues_enabled();
3584        self.encoding_issues.rate = self.preset.encoding_issue_rate();
3585
3586        // OCR errors for typos in legacy preset
3587        if self.preset.ocr_errors_enabled() {
3588            self.typos.type_weights.ocr_errors = 0.3;
3589        }
3590    }
3591
3592    /// Returns the effective missing value rate (considering preset).
3593    pub fn effective_missing_rate(&self) -> f64 {
3594        if self.preset.overrides_settings() {
3595            self.preset.missing_rate()
3596        } else {
3597            self.missing_values.rate
3598        }
3599    }
3600
3601    /// Returns the effective typo rate (considering preset).
3602    pub fn effective_typo_rate(&self) -> f64 {
3603        if self.preset.overrides_settings() {
3604            self.preset.typo_rate()
3605        } else {
3606            self.typos.char_error_rate
3607        }
3608    }
3609
3610    /// Returns the effective duplicate rate (considering preset).
3611    pub fn effective_duplicate_rate(&self) -> f64 {
3612        if self.preset.overrides_settings() {
3613            self.preset.duplicate_rate()
3614        } else {
3615            self.duplicates.exact_duplicate_ratio
3616                + self.duplicates.near_duplicate_ratio
3617                + self.duplicates.fuzzy_duplicate_ratio
3618        }
3619    }
3620
3621    /// Creates a clean profile config.
3622    pub fn clean() -> Self {
3623        Self::with_preset(DataQualityPreset::Clean)
3624    }
3625
3626    /// Creates a noisy profile config.
3627    pub fn noisy() -> Self {
3628        Self::with_preset(DataQualityPreset::Noisy)
3629    }
3630
3631    /// Creates a legacy profile config.
3632    pub fn legacy() -> Self {
3633        Self::with_preset(DataQualityPreset::Legacy)
3634    }
3635}
3636
3637/// Preset configurations for common data quality scenarios.
3638#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
3639#[serde(rename_all = "snake_case")]
3640pub enum DataQualityPreset {
3641    /// No data quality variations (clean data)
3642    #[default]
3643    None,
3644    /// Minimal variations (very clean data with rare issues)
3645    Minimal,
3646    /// Normal variations (realistic enterprise data quality)
3647    Normal,
3648    /// High variations (messy data for stress testing)
3649    High,
3650    /// Custom (use individual settings)
3651    Custom,
3652
3653    // ========================================
3654    // ML-Oriented Profiles (Phase 2.1)
3655    // ========================================
3656    /// Clean profile for ML training - minimal data quality issues
3657    /// Missing: 0.1%, Typos: 0.05%, Duplicates: 0%, Format: None
3658    Clean,
3659    /// Noisy profile simulating typical production data issues
3660    /// Missing: 5%, Typos: 2%, Duplicates: 1%, Format: Medium
3661    Noisy,
3662    /// Legacy profile simulating migrated/OCR'd historical data
3663    /// Missing: 10%, Typos: 5%, Duplicates: 3%, Format: Heavy + OCR
3664    Legacy,
3665}
3666
3667impl DataQualityPreset {
3668    /// Returns the missing value rate for this preset.
3669    pub fn missing_rate(&self) -> f64 {
3670        match self {
3671            DataQualityPreset::None => 0.0,
3672            DataQualityPreset::Minimal => 0.005,
3673            DataQualityPreset::Normal => 0.02,
3674            DataQualityPreset::High => 0.08,
3675            DataQualityPreset::Custom => 0.01, // Use config value
3676            DataQualityPreset::Clean => 0.001,
3677            DataQualityPreset::Noisy => 0.05,
3678            DataQualityPreset::Legacy => 0.10,
3679        }
3680    }
3681
3682    /// Returns the typo rate for this preset.
3683    pub fn typo_rate(&self) -> f64 {
3684        match self {
3685            DataQualityPreset::None => 0.0,
3686            DataQualityPreset::Minimal => 0.0005,
3687            DataQualityPreset::Normal => 0.002,
3688            DataQualityPreset::High => 0.01,
3689            DataQualityPreset::Custom => 0.001, // Use config value
3690            DataQualityPreset::Clean => 0.0005,
3691            DataQualityPreset::Noisy => 0.02,
3692            DataQualityPreset::Legacy => 0.05,
3693        }
3694    }
3695
3696    /// Returns the duplicate rate for this preset.
3697    pub fn duplicate_rate(&self) -> f64 {
3698        match self {
3699            DataQualityPreset::None => 0.0,
3700            DataQualityPreset::Minimal => 0.001,
3701            DataQualityPreset::Normal => 0.005,
3702            DataQualityPreset::High => 0.02,
3703            DataQualityPreset::Custom => 0.0, // Use config value
3704            DataQualityPreset::Clean => 0.0,
3705            DataQualityPreset::Noisy => 0.01,
3706            DataQualityPreset::Legacy => 0.03,
3707        }
3708    }
3709
3710    /// Returns whether format variations are enabled for this preset.
3711    pub fn format_variations_enabled(&self) -> bool {
3712        match self {
3713            DataQualityPreset::None | DataQualityPreset::Clean => false,
3714            DataQualityPreset::Minimal => true,
3715            DataQualityPreset::Normal => true,
3716            DataQualityPreset::High => true,
3717            DataQualityPreset::Custom => true,
3718            DataQualityPreset::Noisy => true,
3719            DataQualityPreset::Legacy => true,
3720        }
3721    }
3722
3723    /// Returns whether OCR-style errors are enabled for this preset.
3724    pub fn ocr_errors_enabled(&self) -> bool {
3725        matches!(self, DataQualityPreset::Legacy | DataQualityPreset::High)
3726    }
3727
3728    /// Returns whether encoding issues are enabled for this preset.
3729    pub fn encoding_issues_enabled(&self) -> bool {
3730        matches!(
3731            self,
3732            DataQualityPreset::Legacy | DataQualityPreset::High | DataQualityPreset::Noisy
3733        )
3734    }
3735
3736    /// Returns the encoding issue rate for this preset.
3737    pub fn encoding_issue_rate(&self) -> f64 {
3738        match self {
3739            DataQualityPreset::None | DataQualityPreset::Clean | DataQualityPreset::Minimal => 0.0,
3740            DataQualityPreset::Normal => 0.002,
3741            DataQualityPreset::High => 0.01,
3742            DataQualityPreset::Custom => 0.0,
3743            DataQualityPreset::Noisy => 0.005,
3744            DataQualityPreset::Legacy => 0.02,
3745        }
3746    }
3747
3748    /// Returns true if this preset overrides individual settings.
3749    pub fn overrides_settings(&self) -> bool {
3750        !matches!(self, DataQualityPreset::Custom | DataQualityPreset::None)
3751    }
3752
3753    /// Returns a human-readable description of this preset.
3754    pub fn description(&self) -> &'static str {
3755        match self {
3756            DataQualityPreset::None => "No data quality issues (pristine data)",
3757            DataQualityPreset::Minimal => "Very rare data quality issues",
3758            DataQualityPreset::Normal => "Realistic enterprise data quality",
3759            DataQualityPreset::High => "Messy data for stress testing",
3760            DataQualityPreset::Custom => "Custom settings from configuration",
3761            DataQualityPreset::Clean => "ML-ready clean data with minimal issues",
3762            DataQualityPreset::Noisy => "Typical production data with moderate issues",
3763            DataQualityPreset::Legacy => "Legacy/migrated data with heavy issues and OCR errors",
3764        }
3765    }
3766}
3767
3768/// Missing value injection configuration.
3769#[derive(Debug, Clone, Serialize, Deserialize)]
3770pub struct MissingValuesSchemaConfig {
3771    /// Enable missing value injection
3772    #[serde(default)]
3773    pub enabled: bool,
3774    /// Global missing rate (0.0 to 1.0)
3775    #[serde(default = "default_missing_rate")]
3776    pub rate: f64,
3777    /// Missing value strategy
3778    #[serde(default)]
3779    pub strategy: MissingValueStrategy,
3780    /// Field-specific rates (field name -> rate)
3781    #[serde(default)]
3782    pub field_rates: std::collections::HashMap<String, f64>,
3783    /// Fields that should never have missing values
3784    #[serde(default)]
3785    pub protected_fields: Vec<String>,
3786}
3787
3788fn default_missing_rate() -> f64 {
3789    0.01
3790}
3791
3792impl Default for MissingValuesSchemaConfig {
3793    fn default() -> Self {
3794        Self {
3795            enabled: false,
3796            rate: default_missing_rate(),
3797            strategy: MissingValueStrategy::Mcar,
3798            field_rates: std::collections::HashMap::new(),
3799            protected_fields: vec![
3800                "document_id".to_string(),
3801                "company_code".to_string(),
3802                "posting_date".to_string(),
3803            ],
3804        }
3805    }
3806}
3807
3808/// Missing value strategy types.
3809#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
3810#[serde(rename_all = "snake_case")]
3811pub enum MissingValueStrategy {
3812    /// Missing Completely At Random - equal probability for all values
3813    #[default]
3814    Mcar,
3815    /// Missing At Random - depends on other observed values
3816    Mar,
3817    /// Missing Not At Random - depends on the value itself
3818    Mnar,
3819    /// Systematic - entire field groups missing together
3820    Systematic,
3821}
3822
3823/// Typo injection configuration.
3824#[derive(Debug, Clone, Serialize, Deserialize)]
3825pub struct TypoSchemaConfig {
3826    /// Enable typo injection
3827    #[serde(default)]
3828    pub enabled: bool,
3829    /// Character error rate (per character, not per field)
3830    #[serde(default = "default_typo_rate")]
3831    pub char_error_rate: f64,
3832    /// Typo type weights
3833    #[serde(default)]
3834    pub type_weights: TypoTypeWeights,
3835    /// Fields that should never have typos
3836    #[serde(default)]
3837    pub protected_fields: Vec<String>,
3838}
3839
3840fn default_typo_rate() -> f64 {
3841    0.001
3842}
3843
3844impl Default for TypoSchemaConfig {
3845    fn default() -> Self {
3846        Self {
3847            enabled: false,
3848            char_error_rate: default_typo_rate(),
3849            type_weights: TypoTypeWeights::default(),
3850            protected_fields: vec![
3851                "document_id".to_string(),
3852                "gl_account".to_string(),
3853                "company_code".to_string(),
3854            ],
3855        }
3856    }
3857}
3858
3859/// Weights for different typo types.
3860#[derive(Debug, Clone, Serialize, Deserialize)]
3861pub struct TypoTypeWeights {
3862    /// Keyboard-adjacent substitution (e.g., 'a' -> 's')
3863    #[serde(default = "default_substitution_weight")]
3864    pub substitution: f64,
3865    /// Adjacent character transposition (e.g., 'ab' -> 'ba')
3866    #[serde(default = "default_transposition_weight")]
3867    pub transposition: f64,
3868    /// Character insertion
3869    #[serde(default = "default_insertion_weight")]
3870    pub insertion: f64,
3871    /// Character deletion
3872    #[serde(default = "default_deletion_weight")]
3873    pub deletion: f64,
3874    /// OCR-style errors (e.g., '0' -> 'O')
3875    #[serde(default = "default_ocr_weight")]
3876    pub ocr_errors: f64,
3877    /// Homophone substitution (e.g., 'their' -> 'there')
3878    #[serde(default = "default_homophone_weight")]
3879    pub homophones: f64,
3880}
3881
3882fn default_substitution_weight() -> f64 {
3883    0.35
3884}
3885fn default_transposition_weight() -> f64 {
3886    0.25
3887}
3888fn default_insertion_weight() -> f64 {
3889    0.10
3890}
3891fn default_deletion_weight() -> f64 {
3892    0.15
3893}
3894fn default_ocr_weight() -> f64 {
3895    0.10
3896}
3897fn default_homophone_weight() -> f64 {
3898    0.05
3899}
3900
3901impl Default for TypoTypeWeights {
3902    fn default() -> Self {
3903        Self {
3904            substitution: default_substitution_weight(),
3905            transposition: default_transposition_weight(),
3906            insertion: default_insertion_weight(),
3907            deletion: default_deletion_weight(),
3908            ocr_errors: default_ocr_weight(),
3909            homophones: default_homophone_weight(),
3910        }
3911    }
3912}
3913
3914/// Format variation configuration.
3915#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3916pub struct FormatVariationSchemaConfig {
3917    /// Enable format variations
3918    #[serde(default)]
3919    pub enabled: bool,
3920    /// Date format variation settings
3921    #[serde(default)]
3922    pub dates: DateFormatVariationConfig,
3923    /// Amount format variation settings
3924    #[serde(default)]
3925    pub amounts: AmountFormatVariationConfig,
3926    /// Identifier format variation settings
3927    #[serde(default)]
3928    pub identifiers: IdentifierFormatVariationConfig,
3929}
3930
3931/// Date format variation configuration.
3932#[derive(Debug, Clone, Serialize, Deserialize)]
3933pub struct DateFormatVariationConfig {
3934    /// Enable date format variations
3935    #[serde(default)]
3936    pub enabled: bool,
3937    /// Overall variation rate
3938    #[serde(default = "default_date_variation_rate")]
3939    pub rate: f64,
3940    /// Include ISO format (2024-01-15)
3941    #[serde(default = "default_true")]
3942    pub iso_format: bool,
3943    /// Include US format (01/15/2024)
3944    #[serde(default)]
3945    pub us_format: bool,
3946    /// Include EU format (15.01.2024)
3947    #[serde(default)]
3948    pub eu_format: bool,
3949    /// Include long format (January 15, 2024)
3950    #[serde(default)]
3951    pub long_format: bool,
3952}
3953
3954fn default_date_variation_rate() -> f64 {
3955    0.05
3956}
3957
3958impl Default for DateFormatVariationConfig {
3959    fn default() -> Self {
3960        Self {
3961            enabled: false,
3962            rate: default_date_variation_rate(),
3963            iso_format: true,
3964            us_format: false,
3965            eu_format: false,
3966            long_format: false,
3967        }
3968    }
3969}
3970
3971/// Amount format variation configuration.
3972#[derive(Debug, Clone, Serialize, Deserialize)]
3973pub struct AmountFormatVariationConfig {
3974    /// Enable amount format variations
3975    #[serde(default)]
3976    pub enabled: bool,
3977    /// Overall variation rate
3978    #[serde(default = "default_amount_variation_rate")]
3979    pub rate: f64,
3980    /// Include US comma format (1,234.56)
3981    #[serde(default)]
3982    pub us_comma_format: bool,
3983    /// Include EU format (1.234,56)
3984    #[serde(default)]
3985    pub eu_format: bool,
3986    /// Include currency prefix ($1,234.56)
3987    #[serde(default)]
3988    pub currency_prefix: bool,
3989    /// Include accounting format with parentheses for negatives
3990    #[serde(default)]
3991    pub accounting_format: bool,
3992}
3993
3994fn default_amount_variation_rate() -> f64 {
3995    0.02
3996}
3997
3998impl Default for AmountFormatVariationConfig {
3999    fn default() -> Self {
4000        Self {
4001            enabled: false,
4002            rate: default_amount_variation_rate(),
4003            us_comma_format: false,
4004            eu_format: false,
4005            currency_prefix: false,
4006            accounting_format: false,
4007        }
4008    }
4009}
4010
4011/// Identifier format variation configuration.
4012#[derive(Debug, Clone, Serialize, Deserialize)]
4013pub struct IdentifierFormatVariationConfig {
4014    /// Enable identifier format variations
4015    #[serde(default)]
4016    pub enabled: bool,
4017    /// Overall variation rate
4018    #[serde(default = "default_identifier_variation_rate")]
4019    pub rate: f64,
4020    /// Case variations (uppercase, lowercase, mixed)
4021    #[serde(default)]
4022    pub case_variations: bool,
4023    /// Padding variations (leading zeros)
4024    #[serde(default)]
4025    pub padding_variations: bool,
4026    /// Separator variations (dash vs underscore)
4027    #[serde(default)]
4028    pub separator_variations: bool,
4029}
4030
4031fn default_identifier_variation_rate() -> f64 {
4032    0.02
4033}
4034
4035impl Default for IdentifierFormatVariationConfig {
4036    fn default() -> Self {
4037        Self {
4038            enabled: false,
4039            rate: default_identifier_variation_rate(),
4040            case_variations: false,
4041            padding_variations: false,
4042            separator_variations: false,
4043        }
4044    }
4045}
4046
4047/// Duplicate injection configuration.
4048#[derive(Debug, Clone, Serialize, Deserialize)]
4049pub struct DuplicateSchemaConfig {
4050    /// Enable duplicate injection
4051    #[serde(default)]
4052    pub enabled: bool,
4053    /// Overall duplicate rate
4054    #[serde(default = "default_duplicate_rate")]
4055    pub rate: f64,
4056    /// Exact duplicate proportion (out of duplicates)
4057    #[serde(default = "default_exact_duplicate_ratio")]
4058    pub exact_duplicate_ratio: f64,
4059    /// Near duplicate proportion (slight variations)
4060    #[serde(default = "default_near_duplicate_ratio")]
4061    pub near_duplicate_ratio: f64,
4062    /// Fuzzy duplicate proportion (typos in key fields)
4063    #[serde(default = "default_fuzzy_duplicate_ratio")]
4064    pub fuzzy_duplicate_ratio: f64,
4065    /// Maximum date offset for near/fuzzy duplicates (days)
4066    #[serde(default = "default_max_date_offset")]
4067    pub max_date_offset_days: u32,
4068    /// Maximum amount variance for near duplicates (fraction)
4069    #[serde(default = "default_max_amount_variance")]
4070    pub max_amount_variance: f64,
4071}
4072
4073fn default_duplicate_rate() -> f64 {
4074    0.005
4075}
4076fn default_exact_duplicate_ratio() -> f64 {
4077    0.4
4078}
4079fn default_near_duplicate_ratio() -> f64 {
4080    0.35
4081}
4082fn default_fuzzy_duplicate_ratio() -> f64 {
4083    0.25
4084}
4085fn default_max_date_offset() -> u32 {
4086    3
4087}
4088fn default_max_amount_variance() -> f64 {
4089    0.01
4090}
4091
4092impl Default for DuplicateSchemaConfig {
4093    fn default() -> Self {
4094        Self {
4095            enabled: false,
4096            rate: default_duplicate_rate(),
4097            exact_duplicate_ratio: default_exact_duplicate_ratio(),
4098            near_duplicate_ratio: default_near_duplicate_ratio(),
4099            fuzzy_duplicate_ratio: default_fuzzy_duplicate_ratio(),
4100            max_date_offset_days: default_max_date_offset(),
4101            max_amount_variance: default_max_amount_variance(),
4102        }
4103    }
4104}
4105
4106/// Encoding issue configuration.
4107#[derive(Debug, Clone, Serialize, Deserialize)]
4108pub struct EncodingIssueSchemaConfig {
4109    /// Enable encoding issue injection
4110    #[serde(default)]
4111    pub enabled: bool,
4112    /// Overall encoding issue rate
4113    #[serde(default = "default_encoding_rate")]
4114    pub rate: f64,
4115    /// Include mojibake (UTF-8/Latin-1 confusion)
4116    #[serde(default)]
4117    pub mojibake: bool,
4118    /// Include HTML entity corruption
4119    #[serde(default)]
4120    pub html_entities: bool,
4121    /// Include BOM issues
4122    #[serde(default)]
4123    pub bom_issues: bool,
4124}
4125
4126fn default_encoding_rate() -> f64 {
4127    0.001
4128}
4129
4130impl Default for EncodingIssueSchemaConfig {
4131    fn default() -> Self {
4132        Self {
4133            enabled: false,
4134            rate: default_encoding_rate(),
4135            mojibake: false,
4136            html_entities: false,
4137            bom_issues: false,
4138        }
4139    }
4140}
4141
4142/// Per-sink quality profiles for different output formats.
4143#[derive(Debug, Clone, Serialize, Deserialize, Default)]
4144pub struct SinkQualityProfiles {
4145    /// CSV-specific quality settings
4146    #[serde(default)]
4147    pub csv: Option<SinkQualityOverride>,
4148    /// JSON-specific quality settings
4149    #[serde(default)]
4150    pub json: Option<SinkQualityOverride>,
4151    /// Parquet-specific quality settings
4152    #[serde(default)]
4153    pub parquet: Option<SinkQualityOverride>,
4154}
4155
4156/// Quality setting overrides for a specific sink type.
4157#[derive(Debug, Clone, Serialize, Deserialize)]
4158pub struct SinkQualityOverride {
4159    /// Override enabled state
4160    pub enabled: Option<bool>,
4161    /// Override missing value rate
4162    pub missing_rate: Option<f64>,
4163    /// Override typo rate
4164    pub typo_rate: Option<f64>,
4165    /// Override format variation rate
4166    pub format_variation_rate: Option<f64>,
4167    /// Override duplicate rate
4168    pub duplicate_rate: Option<f64>,
4169}
4170
4171// =============================================================================
4172// Accounting Standards Configuration
4173// =============================================================================
4174
4175/// Accounting standards framework configuration for generating standards-compliant data.
4176///
4177/// Supports US GAAP and IFRS frameworks with specific standards:
4178/// - ASC 606/IFRS 15: Revenue Recognition
4179/// - ASC 842/IFRS 16: Leases
4180/// - ASC 820/IFRS 13: Fair Value Measurement
4181/// - ASC 360/IAS 36: Impairment
4182#[derive(Debug, Clone, Serialize, Deserialize, Default)]
4183pub struct AccountingStandardsConfig {
4184    /// Enable accounting standards generation
4185    #[serde(default)]
4186    pub enabled: bool,
4187
4188    /// Accounting framework to use
4189    #[serde(default)]
4190    pub framework: AccountingFrameworkConfig,
4191
4192    /// Revenue recognition configuration (ASC 606/IFRS 15)
4193    #[serde(default)]
4194    pub revenue_recognition: RevenueRecognitionConfig,
4195
4196    /// Lease accounting configuration (ASC 842/IFRS 16)
4197    #[serde(default)]
4198    pub leases: LeaseAccountingConfig,
4199
4200    /// Fair value measurement configuration (ASC 820/IFRS 13)
4201    #[serde(default)]
4202    pub fair_value: FairValueConfig,
4203
4204    /// Impairment testing configuration (ASC 360/IAS 36)
4205    #[serde(default)]
4206    pub impairment: ImpairmentConfig,
4207
4208    /// Generate framework differences for dual reporting
4209    #[serde(default)]
4210    pub generate_differences: bool,
4211}
4212
4213/// Accounting framework selection.
4214#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
4215#[serde(rename_all = "snake_case")]
4216pub enum AccountingFrameworkConfig {
4217    /// US Generally Accepted Accounting Principles
4218    #[default]
4219    UsGaap,
4220    /// International Financial Reporting Standards
4221    Ifrs,
4222    /// Generate data for both frameworks with reconciliation
4223    DualReporting,
4224}
4225
4226/// Revenue recognition configuration (ASC 606/IFRS 15).
4227#[derive(Debug, Clone, Serialize, Deserialize)]
4228pub struct RevenueRecognitionConfig {
4229    /// Enable revenue recognition generation
4230    #[serde(default)]
4231    pub enabled: bool,
4232
4233    /// Generate customer contracts
4234    #[serde(default = "default_true")]
4235    pub generate_contracts: bool,
4236
4237    /// Average number of performance obligations per contract
4238    #[serde(default = "default_avg_obligations")]
4239    pub avg_obligations_per_contract: f64,
4240
4241    /// Rate of contracts with variable consideration
4242    #[serde(default = "default_variable_consideration_rate")]
4243    pub variable_consideration_rate: f64,
4244
4245    /// Rate of over-time revenue recognition (vs point-in-time)
4246    #[serde(default = "default_over_time_rate")]
4247    pub over_time_recognition_rate: f64,
4248
4249    /// Number of contracts to generate
4250    #[serde(default = "default_contract_count")]
4251    pub contract_count: usize,
4252}
4253
4254fn default_avg_obligations() -> f64 {
4255    2.0
4256}
4257
4258fn default_variable_consideration_rate() -> f64 {
4259    0.15
4260}
4261
4262fn default_over_time_rate() -> f64 {
4263    0.30
4264}
4265
4266fn default_contract_count() -> usize {
4267    100
4268}
4269
4270impl Default for RevenueRecognitionConfig {
4271    fn default() -> Self {
4272        Self {
4273            enabled: false,
4274            generate_contracts: true,
4275            avg_obligations_per_contract: default_avg_obligations(),
4276            variable_consideration_rate: default_variable_consideration_rate(),
4277            over_time_recognition_rate: default_over_time_rate(),
4278            contract_count: default_contract_count(),
4279        }
4280    }
4281}
4282
4283/// Lease accounting configuration (ASC 842/IFRS 16).
4284#[derive(Debug, Clone, Serialize, Deserialize)]
4285pub struct LeaseAccountingConfig {
4286    /// Enable lease accounting generation
4287    #[serde(default)]
4288    pub enabled: bool,
4289
4290    /// Number of leases to generate
4291    #[serde(default = "default_lease_count")]
4292    pub lease_count: usize,
4293
4294    /// Percentage of finance leases (vs operating)
4295    #[serde(default = "default_finance_lease_pct")]
4296    pub finance_lease_percent: f64,
4297
4298    /// Average lease term in months
4299    #[serde(default = "default_avg_lease_term")]
4300    pub avg_lease_term_months: u32,
4301
4302    /// Generate amortization schedules
4303    #[serde(default = "default_true")]
4304    pub generate_amortization: bool,
4305
4306    /// Real estate lease percentage
4307    #[serde(default = "default_real_estate_pct")]
4308    pub real_estate_percent: f64,
4309}
4310
4311fn default_lease_count() -> usize {
4312    50
4313}
4314
4315fn default_finance_lease_pct() -> f64 {
4316    0.30
4317}
4318
4319fn default_avg_lease_term() -> u32 {
4320    60
4321}
4322
4323fn default_real_estate_pct() -> f64 {
4324    0.40
4325}
4326
4327impl Default for LeaseAccountingConfig {
4328    fn default() -> Self {
4329        Self {
4330            enabled: false,
4331            lease_count: default_lease_count(),
4332            finance_lease_percent: default_finance_lease_pct(),
4333            avg_lease_term_months: default_avg_lease_term(),
4334            generate_amortization: true,
4335            real_estate_percent: default_real_estate_pct(),
4336        }
4337    }
4338}
4339
4340/// Fair value measurement configuration (ASC 820/IFRS 13).
4341#[derive(Debug, Clone, Serialize, Deserialize)]
4342pub struct FairValueConfig {
4343    /// Enable fair value measurement generation
4344    #[serde(default)]
4345    pub enabled: bool,
4346
4347    /// Number of fair value measurements to generate
4348    #[serde(default = "default_fv_count")]
4349    pub measurement_count: usize,
4350
4351    /// Level 1 (quoted prices) percentage
4352    #[serde(default = "default_level1_pct")]
4353    pub level1_percent: f64,
4354
4355    /// Level 2 (observable inputs) percentage
4356    #[serde(default = "default_level2_pct")]
4357    pub level2_percent: f64,
4358
4359    /// Level 3 (unobservable inputs) percentage
4360    #[serde(default = "default_level3_pct")]
4361    pub level3_percent: f64,
4362
4363    /// Include sensitivity analysis for Level 3
4364    #[serde(default)]
4365    pub include_sensitivity_analysis: bool,
4366}
4367
4368fn default_fv_count() -> usize {
4369    25
4370}
4371
4372fn default_level1_pct() -> f64 {
4373    0.40
4374}
4375
4376fn default_level2_pct() -> f64 {
4377    0.35
4378}
4379
4380fn default_level3_pct() -> f64 {
4381    0.25
4382}
4383
4384impl Default for FairValueConfig {
4385    fn default() -> Self {
4386        Self {
4387            enabled: false,
4388            measurement_count: default_fv_count(),
4389            level1_percent: default_level1_pct(),
4390            level2_percent: default_level2_pct(),
4391            level3_percent: default_level3_pct(),
4392            include_sensitivity_analysis: false,
4393        }
4394    }
4395}
4396
4397/// Impairment testing configuration (ASC 360/IAS 36).
4398#[derive(Debug, Clone, Serialize, Deserialize)]
4399pub struct ImpairmentConfig {
4400    /// Enable impairment testing generation
4401    #[serde(default)]
4402    pub enabled: bool,
4403
4404    /// Number of impairment tests to generate
4405    #[serde(default = "default_impairment_count")]
4406    pub test_count: usize,
4407
4408    /// Rate of tests resulting in impairment
4409    #[serde(default = "default_impairment_rate")]
4410    pub impairment_rate: f64,
4411
4412    /// Generate cash flow projections
4413    #[serde(default = "default_true")]
4414    pub generate_projections: bool,
4415
4416    /// Include goodwill impairment tests
4417    #[serde(default)]
4418    pub include_goodwill: bool,
4419}
4420
4421fn default_impairment_count() -> usize {
4422    15
4423}
4424
4425fn default_impairment_rate() -> f64 {
4426    0.10
4427}
4428
4429impl Default for ImpairmentConfig {
4430    fn default() -> Self {
4431        Self {
4432            enabled: false,
4433            test_count: default_impairment_count(),
4434            impairment_rate: default_impairment_rate(),
4435            generate_projections: true,
4436            include_goodwill: false,
4437        }
4438    }
4439}
4440
4441// =============================================================================
4442// Audit Standards Configuration
4443// =============================================================================
4444
4445/// Audit standards framework configuration for generating standards-compliant audit data.
4446///
4447/// Supports ISA (International Standards on Auditing) and PCAOB standards:
4448/// - ISA 200-720: Complete coverage of audit standards
4449/// - ISA 520: Analytical Procedures
4450/// - ISA 505: External Confirmations
4451/// - ISA 700/705/706/701: Audit Reports
4452/// - PCAOB AS 2201: ICFR Auditing
4453#[derive(Debug, Clone, Serialize, Deserialize, Default)]
4454pub struct AuditStandardsConfig {
4455    /// Enable audit standards generation
4456    #[serde(default)]
4457    pub enabled: bool,
4458
4459    /// ISA compliance configuration
4460    #[serde(default)]
4461    pub isa_compliance: IsaComplianceConfig,
4462
4463    /// Analytical procedures configuration (ISA 520)
4464    #[serde(default)]
4465    pub analytical_procedures: AnalyticalProceduresConfig,
4466
4467    /// External confirmations configuration (ISA 505)
4468    #[serde(default)]
4469    pub confirmations: ConfirmationsConfig,
4470
4471    /// Audit opinion configuration (ISA 700/705/706/701)
4472    #[serde(default)]
4473    pub opinion: AuditOpinionConfig,
4474
4475    /// Generate complete audit trail with traceability
4476    #[serde(default)]
4477    pub generate_audit_trail: bool,
4478
4479    /// SOX 302/404 compliance configuration
4480    #[serde(default)]
4481    pub sox: SoxComplianceConfig,
4482
4483    /// PCAOB-specific configuration
4484    #[serde(default)]
4485    pub pcaob: PcaobConfig,
4486}
4487
4488/// ISA compliance level configuration.
4489#[derive(Debug, Clone, Serialize, Deserialize)]
4490pub struct IsaComplianceConfig {
4491    /// Enable ISA compliance tracking
4492    #[serde(default)]
4493    pub enabled: bool,
4494
4495    /// Compliance level: "basic", "standard", "comprehensive"
4496    #[serde(default = "default_compliance_level")]
4497    pub compliance_level: String,
4498
4499    /// Generate ISA requirement mappings
4500    #[serde(default = "default_true")]
4501    pub generate_isa_mappings: bool,
4502
4503    /// Generate ISA coverage summary
4504    #[serde(default = "default_true")]
4505    pub generate_coverage_summary: bool,
4506
4507    /// Include PCAOB standard mappings (for dual framework)
4508    #[serde(default)]
4509    pub include_pcaob: bool,
4510
4511    /// Framework to use: "isa", "pcaob", "dual"
4512    #[serde(default = "default_audit_framework")]
4513    pub framework: String,
4514}
4515
4516fn default_compliance_level() -> String {
4517    "standard".to_string()
4518}
4519
4520fn default_audit_framework() -> String {
4521    "isa".to_string()
4522}
4523
4524impl Default for IsaComplianceConfig {
4525    fn default() -> Self {
4526        Self {
4527            enabled: false,
4528            compliance_level: default_compliance_level(),
4529            generate_isa_mappings: true,
4530            generate_coverage_summary: true,
4531            include_pcaob: false,
4532            framework: default_audit_framework(),
4533        }
4534    }
4535}
4536
4537/// Analytical procedures configuration (ISA 520).
4538#[derive(Debug, Clone, Serialize, Deserialize)]
4539pub struct AnalyticalProceduresConfig {
4540    /// Enable analytical procedures generation
4541    #[serde(default)]
4542    pub enabled: bool,
4543
4544    /// Number of procedures per account/area
4545    #[serde(default = "default_procedures_per_account")]
4546    pub procedures_per_account: usize,
4547
4548    /// Probability of variance exceeding threshold
4549    #[serde(default = "default_variance_probability")]
4550    pub variance_probability: f64,
4551
4552    /// Include variance investigations
4553    #[serde(default = "default_true")]
4554    pub generate_investigations: bool,
4555
4556    /// Include financial ratio analysis
4557    #[serde(default = "default_true")]
4558    pub include_ratio_analysis: bool,
4559}
4560
4561fn default_procedures_per_account() -> usize {
4562    3
4563}
4564
4565fn default_variance_probability() -> f64 {
4566    0.20
4567}
4568
4569impl Default for AnalyticalProceduresConfig {
4570    fn default() -> Self {
4571        Self {
4572            enabled: false,
4573            procedures_per_account: default_procedures_per_account(),
4574            variance_probability: default_variance_probability(),
4575            generate_investigations: true,
4576            include_ratio_analysis: true,
4577        }
4578    }
4579}
4580
4581/// External confirmations configuration (ISA 505).
4582#[derive(Debug, Clone, Serialize, Deserialize)]
4583pub struct ConfirmationsConfig {
4584    /// Enable confirmation generation
4585    #[serde(default)]
4586    pub enabled: bool,
4587
4588    /// Number of confirmations to generate
4589    #[serde(default = "default_confirmation_count")]
4590    pub confirmation_count: usize,
4591
4592    /// Positive response rate
4593    #[serde(default = "default_positive_response_rate")]
4594    pub positive_response_rate: f64,
4595
4596    /// Exception rate (responses with differences)
4597    #[serde(default = "default_exception_rate_confirm")]
4598    pub exception_rate: f64,
4599
4600    /// Non-response rate
4601    #[serde(default = "default_non_response_rate")]
4602    pub non_response_rate: f64,
4603
4604    /// Generate alternative procedures for non-responses
4605    #[serde(default = "default_true")]
4606    pub generate_alternative_procedures: bool,
4607}
4608
4609fn default_confirmation_count() -> usize {
4610    50
4611}
4612
4613fn default_positive_response_rate() -> f64 {
4614    0.85
4615}
4616
4617fn default_exception_rate_confirm() -> f64 {
4618    0.10
4619}
4620
4621fn default_non_response_rate() -> f64 {
4622    0.05
4623}
4624
4625impl Default for ConfirmationsConfig {
4626    fn default() -> Self {
4627        Self {
4628            enabled: false,
4629            confirmation_count: default_confirmation_count(),
4630            positive_response_rate: default_positive_response_rate(),
4631            exception_rate: default_exception_rate_confirm(),
4632            non_response_rate: default_non_response_rate(),
4633            generate_alternative_procedures: true,
4634        }
4635    }
4636}
4637
4638/// Audit opinion configuration (ISA 700/705/706/701).
4639#[derive(Debug, Clone, Serialize, Deserialize)]
4640pub struct AuditOpinionConfig {
4641    /// Enable audit opinion generation
4642    #[serde(default)]
4643    pub enabled: bool,
4644
4645    /// Generate Key Audit Matters (KAM) / Critical Audit Matters (CAM)
4646    #[serde(default = "default_true")]
4647    pub generate_kam: bool,
4648
4649    /// Average number of KAMs/CAMs per opinion
4650    #[serde(default = "default_kam_count")]
4651    pub average_kam_count: usize,
4652
4653    /// Rate of modified opinions
4654    #[serde(default = "default_modified_opinion_rate")]
4655    pub modified_opinion_rate: f64,
4656
4657    /// Include emphasis of matter paragraphs
4658    #[serde(default)]
4659    pub include_emphasis_of_matter: bool,
4660
4661    /// Include going concern conclusions
4662    #[serde(default = "default_true")]
4663    pub include_going_concern: bool,
4664}
4665
4666fn default_kam_count() -> usize {
4667    3
4668}
4669
4670fn default_modified_opinion_rate() -> f64 {
4671    0.05
4672}
4673
4674impl Default for AuditOpinionConfig {
4675    fn default() -> Self {
4676        Self {
4677            enabled: false,
4678            generate_kam: true,
4679            average_kam_count: default_kam_count(),
4680            modified_opinion_rate: default_modified_opinion_rate(),
4681            include_emphasis_of_matter: false,
4682            include_going_concern: true,
4683        }
4684    }
4685}
4686
4687/// SOX compliance configuration (Sections 302/404).
4688#[derive(Debug, Clone, Serialize, Deserialize)]
4689pub struct SoxComplianceConfig {
4690    /// Enable SOX compliance generation
4691    #[serde(default)]
4692    pub enabled: bool,
4693
4694    /// Generate Section 302 CEO/CFO certifications
4695    #[serde(default = "default_true")]
4696    pub generate_302_certifications: bool,
4697
4698    /// Generate Section 404 ICFR assessments
4699    #[serde(default = "default_true")]
4700    pub generate_404_assessments: bool,
4701
4702    /// Materiality threshold for SOX testing
4703    #[serde(default = "default_sox_materiality_threshold")]
4704    pub materiality_threshold: f64,
4705
4706    /// Rate of material weaknesses
4707    #[serde(default = "default_material_weakness_rate")]
4708    pub material_weakness_rate: f64,
4709
4710    /// Rate of significant deficiencies
4711    #[serde(default = "default_significant_deficiency_rate")]
4712    pub significant_deficiency_rate: f64,
4713}
4714
4715fn default_material_weakness_rate() -> f64 {
4716    0.02
4717}
4718
4719fn default_significant_deficiency_rate() -> f64 {
4720    0.08
4721}
4722
4723impl Default for SoxComplianceConfig {
4724    fn default() -> Self {
4725        Self {
4726            enabled: false,
4727            generate_302_certifications: true,
4728            generate_404_assessments: true,
4729            materiality_threshold: default_sox_materiality_threshold(),
4730            material_weakness_rate: default_material_weakness_rate(),
4731            significant_deficiency_rate: default_significant_deficiency_rate(),
4732        }
4733    }
4734}
4735
4736/// PCAOB-specific configuration.
4737#[derive(Debug, Clone, Serialize, Deserialize)]
4738pub struct PcaobConfig {
4739    /// Enable PCAOB-specific elements
4740    #[serde(default)]
4741    pub enabled: bool,
4742
4743    /// Treat as PCAOB audit (vs ISA-only)
4744    #[serde(default)]
4745    pub is_pcaob_audit: bool,
4746
4747    /// Generate Critical Audit Matters (CAM)
4748    #[serde(default = "default_true")]
4749    pub generate_cam: bool,
4750
4751    /// Include ICFR opinion (for integrated audits)
4752    #[serde(default)]
4753    pub include_icfr_opinion: bool,
4754
4755    /// Generate PCAOB-ISA standard mappings
4756    #[serde(default)]
4757    pub generate_standard_mappings: bool,
4758}
4759
4760impl Default for PcaobConfig {
4761    fn default() -> Self {
4762        Self {
4763            enabled: false,
4764            is_pcaob_audit: false,
4765            generate_cam: true,
4766            include_icfr_opinion: false,
4767            generate_standard_mappings: false,
4768        }
4769    }
4770}
4771
4772#[cfg(test)]
4773mod tests {
4774    use super::*;
4775    use crate::presets::demo_preset;
4776
4777    // ==========================================================================
4778    // Serialization/Deserialization Tests
4779    // ==========================================================================
4780
4781    #[test]
4782    fn test_config_yaml_roundtrip() {
4783        let config = demo_preset();
4784        let yaml = serde_yaml::to_string(&config).expect("Failed to serialize to YAML");
4785        let deserialized: GeneratorConfig =
4786            serde_yaml::from_str(&yaml).expect("Failed to deserialize from YAML");
4787
4788        assert_eq!(
4789            config.global.period_months,
4790            deserialized.global.period_months
4791        );
4792        assert_eq!(config.global.industry, deserialized.global.industry);
4793        assert_eq!(config.companies.len(), deserialized.companies.len());
4794        assert_eq!(config.companies[0].code, deserialized.companies[0].code);
4795    }
4796
4797    #[test]
4798    fn test_config_json_roundtrip() {
4799        // Create a config without infinity values (JSON can't serialize f64::INFINITY)
4800        let mut config = demo_preset();
4801        // Replace infinity with a large but finite value for JSON compatibility
4802        config.master_data.employees.approval_limits.executive = 1e12;
4803
4804        let json = serde_json::to_string(&config).expect("Failed to serialize to JSON");
4805        let deserialized: GeneratorConfig =
4806            serde_json::from_str(&json).expect("Failed to deserialize from JSON");
4807
4808        assert_eq!(
4809            config.global.period_months,
4810            deserialized.global.period_months
4811        );
4812        assert_eq!(config.global.industry, deserialized.global.industry);
4813        assert_eq!(config.companies.len(), deserialized.companies.len());
4814    }
4815
4816    #[test]
4817    fn test_transaction_volume_serialization() {
4818        // Test various transaction volumes serialize correctly
4819        let volumes = vec![
4820            (TransactionVolume::TenK, "ten_k"),
4821            (TransactionVolume::HundredK, "hundred_k"),
4822            (TransactionVolume::OneM, "one_m"),
4823            (TransactionVolume::TenM, "ten_m"),
4824            (TransactionVolume::HundredM, "hundred_m"),
4825        ];
4826
4827        for (volume, expected_key) in volumes {
4828            let json = serde_json::to_string(&volume).expect("Failed to serialize");
4829            assert!(
4830                json.contains(expected_key),
4831                "Expected {} in JSON: {}",
4832                expected_key,
4833                json
4834            );
4835        }
4836    }
4837
4838    #[test]
4839    fn test_transaction_volume_custom_serialization() {
4840        let volume = TransactionVolume::Custom(12345);
4841        let json = serde_json::to_string(&volume).expect("Failed to serialize");
4842        let deserialized: TransactionVolume =
4843            serde_json::from_str(&json).expect("Failed to deserialize");
4844        assert_eq!(deserialized.count(), 12345);
4845    }
4846
4847    #[test]
4848    fn test_output_mode_serialization() {
4849        let modes = vec![
4850            OutputMode::Streaming,
4851            OutputMode::FlatFile,
4852            OutputMode::Both,
4853        ];
4854
4855        for mode in modes {
4856            let json = serde_json::to_string(&mode).expect("Failed to serialize");
4857            let deserialized: OutputMode =
4858                serde_json::from_str(&json).expect("Failed to deserialize");
4859            assert!(format!("{:?}", mode) == format!("{:?}", deserialized));
4860        }
4861    }
4862
4863    #[test]
4864    fn test_file_format_serialization() {
4865        let formats = vec![
4866            FileFormat::Csv,
4867            FileFormat::Parquet,
4868            FileFormat::Json,
4869            FileFormat::JsonLines,
4870        ];
4871
4872        for format in formats {
4873            let json = serde_json::to_string(&format).expect("Failed to serialize");
4874            let deserialized: FileFormat =
4875                serde_json::from_str(&json).expect("Failed to deserialize");
4876            assert!(format!("{:?}", format) == format!("{:?}", deserialized));
4877        }
4878    }
4879
4880    #[test]
4881    fn test_compression_algorithm_serialization() {
4882        let algos = vec![
4883            CompressionAlgorithm::Gzip,
4884            CompressionAlgorithm::Zstd,
4885            CompressionAlgorithm::Lz4,
4886            CompressionAlgorithm::Snappy,
4887        ];
4888
4889        for algo in algos {
4890            let json = serde_json::to_string(&algo).expect("Failed to serialize");
4891            let deserialized: CompressionAlgorithm =
4892                serde_json::from_str(&json).expect("Failed to deserialize");
4893            assert!(format!("{:?}", algo) == format!("{:?}", deserialized));
4894        }
4895    }
4896
4897    #[test]
4898    fn test_transfer_pricing_method_serialization() {
4899        let methods = vec![
4900            TransferPricingMethod::CostPlus,
4901            TransferPricingMethod::ComparableUncontrolled,
4902            TransferPricingMethod::ResalePrice,
4903            TransferPricingMethod::TransactionalNetMargin,
4904            TransferPricingMethod::ProfitSplit,
4905        ];
4906
4907        for method in methods {
4908            let json = serde_json::to_string(&method).expect("Failed to serialize");
4909            let deserialized: TransferPricingMethod =
4910                serde_json::from_str(&json).expect("Failed to deserialize");
4911            assert!(format!("{:?}", method) == format!("{:?}", deserialized));
4912        }
4913    }
4914
4915    #[test]
4916    fn test_benford_exemption_serialization() {
4917        let exemptions = vec![
4918            BenfordExemption::Recurring,
4919            BenfordExemption::Payroll,
4920            BenfordExemption::FixedFees,
4921            BenfordExemption::RoundAmounts,
4922        ];
4923
4924        for exemption in exemptions {
4925            let json = serde_json::to_string(&exemption).expect("Failed to serialize");
4926            let deserialized: BenfordExemption =
4927                serde_json::from_str(&json).expect("Failed to deserialize");
4928            assert!(format!("{:?}", exemption) == format!("{:?}", deserialized));
4929        }
4930    }
4931
4932    // ==========================================================================
4933    // Default Value Tests
4934    // ==========================================================================
4935
4936    #[test]
4937    fn test_global_config_defaults() {
4938        let yaml = r#"
4939            industry: manufacturing
4940            start_date: "2024-01-01"
4941            period_months: 6
4942        "#;
4943        let config: GlobalConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
4944        assert_eq!(config.group_currency, "USD");
4945        assert!(config.parallel);
4946        assert_eq!(config.worker_threads, 0);
4947        assert_eq!(config.memory_limit_mb, 0);
4948    }
4949
4950    #[test]
4951    fn test_fraud_config_defaults() {
4952        let config = FraudConfig::default();
4953        assert!(!config.enabled);
4954        assert_eq!(config.fraud_rate, 0.005);
4955        assert!(!config.clustering_enabled);
4956    }
4957
4958    #[test]
4959    fn test_internal_controls_config_defaults() {
4960        let config = InternalControlsConfig::default();
4961        assert!(!config.enabled);
4962        assert_eq!(config.exception_rate, 0.02);
4963        assert_eq!(config.sod_violation_rate, 0.01);
4964        assert!(config.export_control_master_data);
4965        assert_eq!(config.sox_materiality_threshold, 10000.0);
4966        // COSO fields
4967        assert!(config.coso_enabled);
4968        assert!(!config.include_entity_level_controls);
4969        assert_eq!(config.target_maturity_level, "mixed");
4970    }
4971
4972    #[test]
4973    fn test_output_config_defaults() {
4974        let config = OutputConfig::default();
4975        assert!(matches!(config.mode, OutputMode::FlatFile));
4976        assert_eq!(config.formats, vec![FileFormat::Parquet]);
4977        assert!(config.compression.enabled);
4978        assert!(matches!(
4979            config.compression.algorithm,
4980            CompressionAlgorithm::Zstd
4981        ));
4982        assert!(config.include_acdoca);
4983        assert!(!config.include_bseg);
4984        assert!(config.partition_by_period);
4985        assert!(!config.partition_by_company);
4986    }
4987
4988    #[test]
4989    fn test_approval_config_defaults() {
4990        let config = ApprovalConfig::default();
4991        assert!(!config.enabled);
4992        assert_eq!(config.auto_approve_threshold, 1000.0);
4993        assert_eq!(config.rejection_rate, 0.02);
4994        assert_eq!(config.revision_rate, 0.05);
4995        assert_eq!(config.average_approval_delay_hours, 4.0);
4996        assert_eq!(config.thresholds.len(), 4);
4997    }
4998
4999    #[test]
5000    fn test_p2p_flow_config_defaults() {
5001        let config = P2PFlowConfig::default();
5002        assert!(config.enabled);
5003        assert_eq!(config.three_way_match_rate, 0.95);
5004        assert_eq!(config.partial_delivery_rate, 0.15);
5005        assert_eq!(config.average_po_to_gr_days, 14);
5006    }
5007
5008    #[test]
5009    fn test_o2c_flow_config_defaults() {
5010        let config = O2CFlowConfig::default();
5011        assert!(config.enabled);
5012        assert_eq!(config.credit_check_failure_rate, 0.02);
5013        assert_eq!(config.return_rate, 0.03);
5014        assert_eq!(config.bad_debt_rate, 0.01);
5015    }
5016
5017    #[test]
5018    fn test_balance_config_defaults() {
5019        let config = BalanceConfig::default();
5020        assert!(!config.generate_opening_balances);
5021        assert!(config.generate_trial_balances);
5022        assert_eq!(config.target_gross_margin, 0.35);
5023        assert!(config.validate_balance_equation);
5024        assert!(config.reconcile_subledgers);
5025    }
5026
5027    // ==========================================================================
5028    // Partial Config Deserialization Tests
5029    // ==========================================================================
5030
5031    #[test]
5032    fn test_partial_config_with_defaults() {
5033        // Minimal config that should use all defaults
5034        let yaml = r#"
5035            global:
5036              industry: manufacturing
5037              start_date: "2024-01-01"
5038              period_months: 3
5039            companies:
5040              - code: "TEST"
5041                name: "Test Company"
5042                currency: "USD"
5043                country: "US"
5044                annual_transaction_volume: ten_k
5045            chart_of_accounts:
5046              complexity: small
5047            output:
5048              output_directory: "./output"
5049        "#;
5050
5051        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
5052        assert_eq!(config.global.period_months, 3);
5053        assert_eq!(config.companies.len(), 1);
5054        assert!(!config.fraud.enabled); // Default
5055        assert!(!config.internal_controls.enabled); // Default
5056    }
5057
5058    #[test]
5059    fn test_config_with_fraud_enabled() {
5060        let yaml = r#"
5061            global:
5062              industry: retail
5063              start_date: "2024-01-01"
5064              period_months: 12
5065            companies:
5066              - code: "RETAIL"
5067                name: "Retail Co"
5068                currency: "USD"
5069                country: "US"
5070                annual_transaction_volume: hundred_k
5071            chart_of_accounts:
5072              complexity: medium
5073            output:
5074              output_directory: "./output"
5075            fraud:
5076              enabled: true
5077              fraud_rate: 0.05
5078              clustering_enabled: true
5079        "#;
5080
5081        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
5082        assert!(config.fraud.enabled);
5083        assert_eq!(config.fraud.fraud_rate, 0.05);
5084        assert!(config.fraud.clustering_enabled);
5085    }
5086
5087    #[test]
5088    fn test_config_with_multiple_companies() {
5089        let yaml = r#"
5090            global:
5091              industry: manufacturing
5092              start_date: "2024-01-01"
5093              period_months: 6
5094            companies:
5095              - code: "HQ"
5096                name: "Headquarters"
5097                currency: "USD"
5098                country: "US"
5099                annual_transaction_volume: hundred_k
5100                volume_weight: 1.0
5101              - code: "EU"
5102                name: "European Subsidiary"
5103                currency: "EUR"
5104                country: "DE"
5105                annual_transaction_volume: hundred_k
5106                volume_weight: 0.5
5107              - code: "APAC"
5108                name: "Asia Pacific"
5109                currency: "JPY"
5110                country: "JP"
5111                annual_transaction_volume: ten_k
5112                volume_weight: 0.3
5113            chart_of_accounts:
5114              complexity: large
5115            output:
5116              output_directory: "./output"
5117        "#;
5118
5119        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
5120        assert_eq!(config.companies.len(), 3);
5121        assert_eq!(config.companies[0].code, "HQ");
5122        assert_eq!(config.companies[1].currency, "EUR");
5123        assert_eq!(config.companies[2].volume_weight, 0.3);
5124    }
5125
5126    #[test]
5127    fn test_intercompany_config() {
5128        let yaml = r#"
5129            enabled: true
5130            ic_transaction_rate: 0.20
5131            transfer_pricing_method: cost_plus
5132            markup_percent: 0.08
5133            generate_matched_pairs: true
5134            generate_eliminations: true
5135        "#;
5136
5137        let config: IntercompanyConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
5138        assert!(config.enabled);
5139        assert_eq!(config.ic_transaction_rate, 0.20);
5140        assert!(matches!(
5141            config.transfer_pricing_method,
5142            TransferPricingMethod::CostPlus
5143        ));
5144        assert_eq!(config.markup_percent, 0.08);
5145        assert!(config.generate_eliminations);
5146    }
5147
5148    // ==========================================================================
5149    // Company Config Tests
5150    // ==========================================================================
5151
5152    #[test]
5153    fn test_company_config_defaults() {
5154        let yaml = r#"
5155            code: "TEST"
5156            name: "Test Company"
5157            currency: "USD"
5158            country: "US"
5159            annual_transaction_volume: ten_k
5160        "#;
5161
5162        let config: CompanyConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
5163        assert_eq!(config.fiscal_year_variant, "K4"); // Default
5164        assert_eq!(config.volume_weight, 1.0); // Default
5165    }
5166
5167    // ==========================================================================
5168    // Chart of Accounts Config Tests
5169    // ==========================================================================
5170
5171    #[test]
5172    fn test_coa_config_defaults() {
5173        let yaml = r#"
5174            complexity: medium
5175        "#;
5176
5177        let config: ChartOfAccountsConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
5178        assert!(config.industry_specific); // Default true
5179        assert!(config.custom_accounts.is_none());
5180        assert_eq!(config.min_hierarchy_depth, 2); // Default
5181        assert_eq!(config.max_hierarchy_depth, 5); // Default
5182    }
5183
5184    // ==========================================================================
5185    // Accounting Standards Config Tests
5186    // ==========================================================================
5187
5188    #[test]
5189    fn test_accounting_standards_config_defaults() {
5190        let config = AccountingStandardsConfig::default();
5191        assert!(!config.enabled);
5192        assert!(matches!(
5193            config.framework,
5194            AccountingFrameworkConfig::UsGaap
5195        ));
5196        assert!(!config.revenue_recognition.enabled);
5197        assert!(!config.leases.enabled);
5198        assert!(!config.fair_value.enabled);
5199        assert!(!config.impairment.enabled);
5200        assert!(!config.generate_differences);
5201    }
5202
5203    #[test]
5204    fn test_accounting_standards_config_yaml() {
5205        let yaml = r#"
5206            enabled: true
5207            framework: ifrs
5208            revenue_recognition:
5209              enabled: true
5210              generate_contracts: true
5211              avg_obligations_per_contract: 2.5
5212              variable_consideration_rate: 0.20
5213              over_time_recognition_rate: 0.35
5214              contract_count: 150
5215            leases:
5216              enabled: true
5217              lease_count: 75
5218              finance_lease_percent: 0.25
5219              avg_lease_term_months: 48
5220            generate_differences: true
5221        "#;
5222
5223        let config: AccountingStandardsConfig =
5224            serde_yaml::from_str(yaml).expect("Failed to parse");
5225        assert!(config.enabled);
5226        assert!(matches!(config.framework, AccountingFrameworkConfig::Ifrs));
5227        assert!(config.revenue_recognition.enabled);
5228        assert_eq!(config.revenue_recognition.contract_count, 150);
5229        assert_eq!(config.revenue_recognition.avg_obligations_per_contract, 2.5);
5230        assert!(config.leases.enabled);
5231        assert_eq!(config.leases.lease_count, 75);
5232        assert_eq!(config.leases.finance_lease_percent, 0.25);
5233        assert!(config.generate_differences);
5234    }
5235
5236    #[test]
5237    fn test_accounting_framework_serialization() {
5238        let frameworks = [
5239            AccountingFrameworkConfig::UsGaap,
5240            AccountingFrameworkConfig::Ifrs,
5241            AccountingFrameworkConfig::DualReporting,
5242        ];
5243
5244        for framework in frameworks {
5245            let json = serde_json::to_string(&framework).expect("Failed to serialize");
5246            let deserialized: AccountingFrameworkConfig =
5247                serde_json::from_str(&json).expect("Failed to deserialize");
5248            assert!(format!("{:?}", framework) == format!("{:?}", deserialized));
5249        }
5250    }
5251
5252    #[test]
5253    fn test_revenue_recognition_config_defaults() {
5254        let config = RevenueRecognitionConfig::default();
5255        assert!(!config.enabled);
5256        assert!(config.generate_contracts);
5257        assert_eq!(config.avg_obligations_per_contract, 2.0);
5258        assert_eq!(config.variable_consideration_rate, 0.15);
5259        assert_eq!(config.over_time_recognition_rate, 0.30);
5260        assert_eq!(config.contract_count, 100);
5261    }
5262
5263    #[test]
5264    fn test_lease_accounting_config_defaults() {
5265        let config = LeaseAccountingConfig::default();
5266        assert!(!config.enabled);
5267        assert_eq!(config.lease_count, 50);
5268        assert_eq!(config.finance_lease_percent, 0.30);
5269        assert_eq!(config.avg_lease_term_months, 60);
5270        assert!(config.generate_amortization);
5271        assert_eq!(config.real_estate_percent, 0.40);
5272    }
5273
5274    #[test]
5275    fn test_fair_value_config_defaults() {
5276        let config = FairValueConfig::default();
5277        assert!(!config.enabled);
5278        assert_eq!(config.measurement_count, 25);
5279        assert_eq!(config.level1_percent, 0.40);
5280        assert_eq!(config.level2_percent, 0.35);
5281        assert_eq!(config.level3_percent, 0.25);
5282        assert!(!config.include_sensitivity_analysis);
5283    }
5284
5285    #[test]
5286    fn test_impairment_config_defaults() {
5287        let config = ImpairmentConfig::default();
5288        assert!(!config.enabled);
5289        assert_eq!(config.test_count, 15);
5290        assert_eq!(config.impairment_rate, 0.10);
5291        assert!(config.generate_projections);
5292        assert!(!config.include_goodwill);
5293    }
5294
5295    // ==========================================================================
5296    // Audit Standards Config Tests
5297    // ==========================================================================
5298
5299    #[test]
5300    fn test_audit_standards_config_defaults() {
5301        let config = AuditStandardsConfig::default();
5302        assert!(!config.enabled);
5303        assert!(!config.isa_compliance.enabled);
5304        assert!(!config.analytical_procedures.enabled);
5305        assert!(!config.confirmations.enabled);
5306        assert!(!config.opinion.enabled);
5307        assert!(!config.generate_audit_trail);
5308        assert!(!config.sox.enabled);
5309        assert!(!config.pcaob.enabled);
5310    }
5311
5312    #[test]
5313    fn test_audit_standards_config_yaml() {
5314        let yaml = r#"
5315            enabled: true
5316            isa_compliance:
5317              enabled: true
5318              compliance_level: comprehensive
5319              generate_isa_mappings: true
5320              include_pcaob: true
5321              framework: dual
5322            analytical_procedures:
5323              enabled: true
5324              procedures_per_account: 5
5325              variance_probability: 0.25
5326            confirmations:
5327              enabled: true
5328              confirmation_count: 75
5329              positive_response_rate: 0.90
5330              exception_rate: 0.08
5331            opinion:
5332              enabled: true
5333              generate_kam: true
5334              average_kam_count: 4
5335            sox:
5336              enabled: true
5337              generate_302_certifications: true
5338              generate_404_assessments: true
5339              material_weakness_rate: 0.03
5340            pcaob:
5341              enabled: true
5342              is_pcaob_audit: true
5343              include_icfr_opinion: true
5344            generate_audit_trail: true
5345        "#;
5346
5347        let config: AuditStandardsConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
5348        assert!(config.enabled);
5349        assert!(config.isa_compliance.enabled);
5350        assert_eq!(config.isa_compliance.compliance_level, "comprehensive");
5351        assert!(config.isa_compliance.include_pcaob);
5352        assert_eq!(config.isa_compliance.framework, "dual");
5353        assert!(config.analytical_procedures.enabled);
5354        assert_eq!(config.analytical_procedures.procedures_per_account, 5);
5355        assert!(config.confirmations.enabled);
5356        assert_eq!(config.confirmations.confirmation_count, 75);
5357        assert!(config.opinion.enabled);
5358        assert_eq!(config.opinion.average_kam_count, 4);
5359        assert!(config.sox.enabled);
5360        assert!(config.sox.generate_302_certifications);
5361        assert_eq!(config.sox.material_weakness_rate, 0.03);
5362        assert!(config.pcaob.enabled);
5363        assert!(config.pcaob.is_pcaob_audit);
5364        assert!(config.pcaob.include_icfr_opinion);
5365        assert!(config.generate_audit_trail);
5366    }
5367
5368    #[test]
5369    fn test_isa_compliance_config_defaults() {
5370        let config = IsaComplianceConfig::default();
5371        assert!(!config.enabled);
5372        assert_eq!(config.compliance_level, "standard");
5373        assert!(config.generate_isa_mappings);
5374        assert!(config.generate_coverage_summary);
5375        assert!(!config.include_pcaob);
5376        assert_eq!(config.framework, "isa");
5377    }
5378
5379    #[test]
5380    fn test_sox_compliance_config_defaults() {
5381        let config = SoxComplianceConfig::default();
5382        assert!(!config.enabled);
5383        assert!(config.generate_302_certifications);
5384        assert!(config.generate_404_assessments);
5385        assert_eq!(config.materiality_threshold, 10000.0);
5386        assert_eq!(config.material_weakness_rate, 0.02);
5387        assert_eq!(config.significant_deficiency_rate, 0.08);
5388    }
5389
5390    #[test]
5391    fn test_pcaob_config_defaults() {
5392        let config = PcaobConfig::default();
5393        assert!(!config.enabled);
5394        assert!(!config.is_pcaob_audit);
5395        assert!(config.generate_cam);
5396        assert!(!config.include_icfr_opinion);
5397        assert!(!config.generate_standard_mappings);
5398    }
5399
5400    #[test]
5401    fn test_config_with_standards_enabled() {
5402        let yaml = r#"
5403            global:
5404              industry: financial_services
5405              start_date: "2024-01-01"
5406              period_months: 12
5407            companies:
5408              - code: "BANK"
5409                name: "Test Bank"
5410                currency: "USD"
5411                country: "US"
5412                annual_transaction_volume: hundred_k
5413            chart_of_accounts:
5414              complexity: large
5415            output:
5416              output_directory: "./output"
5417            accounting_standards:
5418              enabled: true
5419              framework: us_gaap
5420              revenue_recognition:
5421                enabled: true
5422              leases:
5423                enabled: true
5424            audit_standards:
5425              enabled: true
5426              isa_compliance:
5427                enabled: true
5428              sox:
5429                enabled: true
5430        "#;
5431
5432        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
5433        assert!(config.accounting_standards.enabled);
5434        assert!(matches!(
5435            config.accounting_standards.framework,
5436            AccountingFrameworkConfig::UsGaap
5437        ));
5438        assert!(config.accounting_standards.revenue_recognition.enabled);
5439        assert!(config.accounting_standards.leases.enabled);
5440        assert!(config.audit_standards.enabled);
5441        assert!(config.audit_standards.isa_compliance.enabled);
5442        assert!(config.audit_standards.sox.enabled);
5443    }
5444}