use datasynth_core::distributions::{
AmountDistributionConfig, DebitCreditDistributionConfig, EvenOddDistributionConfig,
LineItemDistributionConfig, SeasonalityConfig,
};
use datasynth_core::models::{CoAComplexity, IndustrySector};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GeneratorConfig {
pub global: GlobalConfig,
pub companies: Vec<CompanyConfig>,
pub chart_of_accounts: ChartOfAccountsConfig,
#[serde(default)]
pub transactions: TransactionConfig,
pub output: OutputConfig,
#[serde(default)]
pub fraud: FraudConfig,
#[serde(default)]
pub data_quality: DataQualitySchemaConfig,
#[serde(default)]
pub internal_controls: InternalControlsConfig,
#[serde(default)]
pub business_processes: BusinessProcessConfig,
#[serde(default)]
pub user_personas: UserPersonaConfig,
#[serde(default)]
pub templates: TemplateConfig,
#[serde(default)]
pub approval: ApprovalConfig,
#[serde(default)]
pub departments: DepartmentConfig,
#[serde(default)]
pub master_data: MasterDataConfig,
#[serde(default)]
pub document_flows: DocumentFlowConfig,
#[serde(default)]
pub intercompany: IntercompanyConfig,
#[serde(default)]
pub balance: BalanceConfig,
#[serde(default)]
pub ocpm: OcpmConfig,
#[serde(default)]
pub audit: AuditGenerationConfig,
#[serde(default)]
pub banking: datasynth_banking::BankingConfig,
#[serde(default)]
pub scenario: ScenarioConfig,
#[serde(default)]
pub temporal: TemporalDriftConfig,
#[serde(default)]
pub graph_export: GraphExportConfig,
#[serde(default)]
pub streaming: StreamingSchemaConfig,
#[serde(default)]
pub rate_limit: RateLimitSchemaConfig,
#[serde(default)]
pub temporal_attributes: TemporalAttributeSchemaConfig,
#[serde(default)]
pub relationships: RelationshipSchemaConfig,
#[serde(default)]
pub accounting_standards: AccountingStandardsConfig,
#[serde(default)]
pub audit_standards: AuditStandardsConfig,
#[serde(default)]
pub distributions: AdvancedDistributionConfig,
#[serde(default)]
pub temporal_patterns: TemporalPatternsConfig,
#[serde(default)]
pub vendor_network: VendorNetworkSchemaConfig,
#[serde(default)]
pub customer_segmentation: CustomerSegmentationSchemaConfig,
#[serde(default)]
pub relationship_strength: RelationshipStrengthSchemaConfig,
#[serde(default)]
pub cross_process_links: CrossProcessLinksSchemaConfig,
#[serde(default)]
pub organizational_events: OrganizationalEventsSchemaConfig,
#[serde(default)]
pub behavioral_drift: BehavioralDriftSchemaConfig,
#[serde(default)]
pub market_drift: MarketDriftSchemaConfig,
#[serde(default)]
pub drift_labeling: DriftLabelingSchemaConfig,
#[serde(default)]
pub anomaly_injection: EnhancedAnomalyConfig,
#[serde(default)]
pub industry_specific: IndustrySpecificConfig,
#[serde(default)]
pub fingerprint_privacy: FingerprintPrivacyConfig,
#[serde(default)]
pub quality_gates: QualityGatesSchemaConfig,
#[serde(default)]
pub compliance: ComplianceSchemaConfig,
#[serde(default)]
pub webhooks: WebhookSchemaConfig,
#[serde(default)]
pub llm: LlmSchemaConfig,
#[serde(default)]
pub diffusion: DiffusionSchemaConfig,
#[serde(default)]
pub causal: CausalSchemaConfig,
#[serde(default)]
pub source_to_pay: SourceToPayConfig,
#[serde(default)]
pub financial_reporting: FinancialReportingConfig,
#[serde(default)]
pub hr: HrConfig,
#[serde(default)]
pub manufacturing: ManufacturingProcessConfig,
#[serde(default)]
pub sales_quotes: SalesQuoteConfig,
#[serde(default)]
pub tax: TaxConfig,
#[serde(default)]
pub treasury: TreasuryConfig,
#[serde(default)]
pub project_accounting: ProjectAccountingConfig,
#[serde(default)]
pub esg: EsgConfig,
#[serde(default)]
pub country_packs: Option<CountryPacksSchemaConfig>,
#[serde(default)]
pub scenarios: ScenariosConfig,
#[serde(default)]
pub session: SessionSchemaConfig,
#[serde(default)]
pub compliance_regulations: ComplianceRegulationsConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LlmSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_llm_provider")]
pub provider: String,
#[serde(default = "default_llm_model_name")]
pub model: String,
#[serde(default = "default_llm_batch_size")]
pub max_vendor_enrichments: usize,
}
fn default_llm_provider() -> String {
"mock".to_string()
}
fn default_llm_model_name() -> String {
"gpt-4o-mini".to_string()
}
fn default_llm_batch_size() -> usize {
50
}
impl Default for LlmSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
provider: default_llm_provider(),
model: default_llm_model_name(),
max_vendor_enrichments: default_llm_batch_size(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiffusionSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_diffusion_steps")]
pub n_steps: usize,
#[serde(default = "default_diffusion_schedule")]
pub schedule: String,
#[serde(default = "default_diffusion_sample_size")]
pub sample_size: usize,
#[serde(default = "default_diffusion_backend")]
pub backend: String,
#[serde(default)]
pub neural: NeuralDiffusionSchemaConfig,
}
fn default_diffusion_steps() -> usize {
100
}
fn default_diffusion_schedule() -> String {
"linear".to_string()
}
fn default_diffusion_sample_size() -> usize {
100
}
fn default_diffusion_backend() -> String {
"statistical".to_string()
}
impl Default for DiffusionSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
n_steps: default_diffusion_steps(),
schedule: default_diffusion_schedule(),
sample_size: default_diffusion_sample_size(),
backend: default_diffusion_backend(),
neural: NeuralDiffusionSchemaConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NeuralDiffusionSchemaConfig {
#[serde(default = "default_neural_hidden_dims")]
pub hidden_dims: Vec<usize>,
#[serde(default = "default_neural_timestep_embed_dim")]
pub timestep_embed_dim: usize,
#[serde(default = "default_neural_learning_rate")]
pub learning_rate: f64,
#[serde(default = "default_neural_training_epochs")]
pub training_epochs: usize,
#[serde(default = "default_neural_batch_size")]
pub batch_size: usize,
#[serde(default = "default_neural_hybrid_weight")]
pub hybrid_weight: f64,
#[serde(default = "default_neural_hybrid_strategy")]
pub hybrid_strategy: String,
#[serde(default)]
pub neural_columns: Vec<String>,
}
fn default_neural_hidden_dims() -> Vec<usize> {
vec![256, 256, 128]
}
fn default_neural_timestep_embed_dim() -> usize {
64
}
fn default_neural_learning_rate() -> f64 {
0.001
}
fn default_neural_training_epochs() -> usize {
100
}
fn default_neural_batch_size() -> usize {
64
}
fn default_neural_hybrid_weight() -> f64 {
0.5
}
fn default_neural_hybrid_strategy() -> String {
"weighted_average".to_string()
}
impl Default for NeuralDiffusionSchemaConfig {
fn default() -> Self {
Self {
hidden_dims: default_neural_hidden_dims(),
timestep_embed_dim: default_neural_timestep_embed_dim(),
learning_rate: default_neural_learning_rate(),
training_epochs: default_neural_training_epochs(),
batch_size: default_neural_batch_size(),
hybrid_weight: default_neural_hybrid_weight(),
hybrid_strategy: default_neural_hybrid_strategy(),
neural_columns: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CausalSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_causal_template")]
pub template: String,
#[serde(default = "default_causal_sample_size")]
pub sample_size: usize,
#[serde(default = "default_true")]
pub validate: bool,
}
fn default_causal_template() -> String {
"fraud_detection".to_string()
}
fn default_causal_sample_size() -> usize {
500
}
impl Default for CausalSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
template: default_causal_template(),
sample_size: default_causal_sample_size(),
validate: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphExportConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_graph_types")]
pub graph_types: Vec<GraphTypeConfig>,
#[serde(default = "default_graph_formats")]
pub formats: Vec<GraphExportFormat>,
#[serde(default = "default_train_ratio")]
pub train_ratio: f64,
#[serde(default = "default_val_ratio")]
pub validation_ratio: f64,
#[serde(default)]
pub split_seed: Option<u64>,
#[serde(default = "default_graph_subdir")]
pub output_subdirectory: String,
#[serde(default)]
pub hypergraph: HypergraphExportSettings,
#[serde(default)]
pub dgl: DglExportConfig,
}
fn default_graph_types() -> Vec<GraphTypeConfig> {
vec![GraphTypeConfig::default()]
}
fn default_graph_formats() -> Vec<GraphExportFormat> {
vec![GraphExportFormat::PytorchGeometric]
}
fn default_train_ratio() -> f64 {
0.7
}
fn default_val_ratio() -> f64 {
0.15
}
fn default_graph_subdir() -> String {
"graphs".to_string()
}
impl Default for GraphExportConfig {
fn default() -> Self {
Self {
enabled: false,
graph_types: default_graph_types(),
formats: default_graph_formats(),
train_ratio: 0.7,
validation_ratio: 0.15,
split_seed: None,
output_subdirectory: "graphs".to_string(),
hypergraph: HypergraphExportSettings::default(),
dgl: DglExportConfig::default(),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct DglExportConfig {
#[serde(default)]
pub heterogeneous: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HypergraphExportSettings {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_hypergraph_max_nodes")]
pub max_nodes: usize,
#[serde(default = "default_aggregation_strategy")]
pub aggregation_strategy: String,
#[serde(default)]
pub governance_layer: GovernanceLayerSettings,
#[serde(default)]
pub process_layer: ProcessLayerSettings,
#[serde(default)]
pub accounting_layer: AccountingLayerSettings,
#[serde(default)]
pub cross_layer: CrossLayerSettings,
#[serde(default = "default_hypergraph_subdir")]
pub output_subdirectory: String,
#[serde(default = "default_hypergraph_format")]
pub output_format: String,
#[serde(default)]
pub stream_target: Option<String>,
#[serde(default = "default_stream_batch_size")]
pub stream_batch_size: usize,
}
fn default_hypergraph_max_nodes() -> usize {
50_000
}
fn default_aggregation_strategy() -> String {
"pool_by_counterparty".to_string()
}
fn default_hypergraph_subdir() -> String {
"hypergraph".to_string()
}
fn default_hypergraph_format() -> String {
"native".to_string()
}
fn default_stream_batch_size() -> usize {
1000
}
impl Default for HypergraphExportSettings {
fn default() -> Self {
Self {
enabled: false,
max_nodes: 50_000,
aggregation_strategy: "pool_by_counterparty".to_string(),
governance_layer: GovernanceLayerSettings::default(),
process_layer: ProcessLayerSettings::default(),
accounting_layer: AccountingLayerSettings::default(),
cross_layer: CrossLayerSettings::default(),
output_subdirectory: "hypergraph".to_string(),
output_format: "native".to_string(),
stream_target: None,
stream_batch_size: 1000,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GovernanceLayerSettings {
#[serde(default = "default_true")]
pub include_coso: bool,
#[serde(default = "default_true")]
pub include_controls: bool,
#[serde(default = "default_true")]
pub include_sox: bool,
#[serde(default = "default_true")]
pub include_vendors: bool,
#[serde(default = "default_true")]
pub include_customers: bool,
#[serde(default = "default_true")]
pub include_employees: bool,
}
impl Default for GovernanceLayerSettings {
fn default() -> Self {
Self {
include_coso: true,
include_controls: true,
include_sox: true,
include_vendors: true,
include_customers: true,
include_employees: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessLayerSettings {
#[serde(default = "default_true")]
pub include_p2p: bool,
#[serde(default = "default_true")]
pub include_o2c: bool,
#[serde(default = "default_true")]
pub include_s2c: bool,
#[serde(default = "default_true")]
pub include_h2r: bool,
#[serde(default = "default_true")]
pub include_mfg: bool,
#[serde(default = "default_true")]
pub include_bank: bool,
#[serde(default = "default_true")]
pub include_audit: bool,
#[serde(default = "default_true")]
pub include_r2r: bool,
#[serde(default = "default_true")]
pub events_as_hyperedges: bool,
#[serde(default = "default_docs_per_counterparty_threshold")]
pub docs_per_counterparty_threshold: usize,
}
fn default_docs_per_counterparty_threshold() -> usize {
20
}
impl Default for ProcessLayerSettings {
fn default() -> Self {
Self {
include_p2p: true,
include_o2c: true,
include_s2c: true,
include_h2r: true,
include_mfg: true,
include_bank: true,
include_audit: true,
include_r2r: true,
events_as_hyperedges: true,
docs_per_counterparty_threshold: 20,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccountingLayerSettings {
#[serde(default = "default_true")]
pub include_accounts: bool,
#[serde(default = "default_true")]
pub je_as_hyperedges: bool,
}
impl Default for AccountingLayerSettings {
fn default() -> Self {
Self {
include_accounts: true,
je_as_hyperedges: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrossLayerSettings {
#[serde(default = "default_true")]
pub enabled: bool,
}
impl Default for CrossLayerSettings {
fn default() -> Self {
Self { enabled: true }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphTypeConfig {
#[serde(default = "default_graph_name")]
pub name: String,
#[serde(default)]
pub aggregate_edges: bool,
#[serde(default)]
pub min_edge_weight: f64,
#[serde(default)]
pub include_document_nodes: bool,
}
fn default_graph_name() -> String {
"accounting_network".to_string()
}
impl Default for GraphTypeConfig {
fn default() -> Self {
Self {
name: "accounting_network".to_string(),
aggregate_edges: false,
min_edge_weight: 0.0,
include_document_nodes: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum GraphExportFormat {
PytorchGeometric,
Neo4j,
Dgl,
RustGraph,
RustGraphHypergraph,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ScenarioConfig {
#[serde(default)]
pub tags: Vec<String>,
#[serde(default)]
pub profile: Option<String>,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub ml_training: bool,
#[serde(default)]
pub target_anomaly_ratio: Option<f64>,
#[serde(default)]
pub metadata: std::collections::HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemporalDriftConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_amount_drift")]
pub amount_mean_drift: f64,
#[serde(default)]
pub amount_variance_drift: f64,
#[serde(default)]
pub anomaly_rate_drift: f64,
#[serde(default = "default_concept_drift")]
pub concept_drift_rate: f64,
#[serde(default)]
pub sudden_drift_probability: f64,
#[serde(default = "default_sudden_drift_magnitude")]
pub sudden_drift_magnitude: f64,
#[serde(default)]
pub seasonal_drift: bool,
#[serde(default)]
pub drift_start_period: u32,
#[serde(default = "default_drift_type")]
pub drift_type: DriftType,
}
fn default_amount_drift() -> f64 {
0.02
}
fn default_concept_drift() -> f64 {
0.01
}
fn default_sudden_drift_magnitude() -> f64 {
2.0
}
fn default_drift_type() -> DriftType {
DriftType::Gradual
}
impl Default for TemporalDriftConfig {
fn default() -> Self {
Self {
enabled: false,
amount_mean_drift: 0.02,
amount_variance_drift: 0.0,
anomaly_rate_drift: 0.0,
concept_drift_rate: 0.01,
sudden_drift_probability: 0.0,
sudden_drift_magnitude: 2.0,
seasonal_drift: false,
drift_start_period: 0,
drift_type: DriftType::Gradual,
}
}
}
impl TemporalDriftConfig {
pub fn to_core_config(&self) -> datasynth_core::distributions::DriftConfig {
datasynth_core::distributions::DriftConfig {
enabled: self.enabled,
amount_mean_drift: self.amount_mean_drift,
amount_variance_drift: self.amount_variance_drift,
anomaly_rate_drift: self.anomaly_rate_drift,
concept_drift_rate: self.concept_drift_rate,
sudden_drift_probability: self.sudden_drift_probability,
sudden_drift_magnitude: self.sudden_drift_magnitude,
seasonal_drift: self.seasonal_drift,
drift_start_period: self.drift_start_period,
drift_type: match self.drift_type {
DriftType::Gradual => datasynth_core::distributions::DriftType::Gradual,
DriftType::Sudden => datasynth_core::distributions::DriftType::Sudden,
DriftType::Recurring => datasynth_core::distributions::DriftType::Recurring,
DriftType::Mixed => datasynth_core::distributions::DriftType::Mixed,
},
regime_changes: Vec::new(),
economic_cycle: Default::default(),
parameter_drifts: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum DriftType {
#[default]
Gradual,
Sudden,
Recurring,
Mixed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StreamingSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub events_per_second: f64,
#[serde(default = "default_burst_size")]
pub burst_size: u32,
#[serde(default = "default_buffer_size")]
pub buffer_size: usize,
#[serde(default = "default_true")]
pub enable_progress: bool,
#[serde(default = "default_progress_interval")]
pub progress_interval: u64,
#[serde(default)]
pub backpressure: BackpressureSchemaStrategy,
}
fn default_buffer_size() -> usize {
1000
}
fn default_progress_interval() -> u64 {
100
}
impl Default for StreamingSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
events_per_second: 0.0,
burst_size: 100,
buffer_size: 1000,
enable_progress: true,
progress_interval: 100,
backpressure: BackpressureSchemaStrategy::Block,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum BackpressureSchemaStrategy {
#[default]
Block,
DropOldest,
DropNewest,
Buffer,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RateLimitSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_entities_per_second")]
pub entities_per_second: f64,
#[serde(default = "default_burst_size")]
pub burst_size: u32,
#[serde(default)]
pub backpressure: RateLimitBackpressureSchema,
}
fn default_entities_per_second() -> f64 {
1000.0
}
fn default_burst_size() -> u32 {
100
}
impl Default for RateLimitSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
entities_per_second: 1000.0,
burst_size: 100,
backpressure: RateLimitBackpressureSchema::Block,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum RateLimitBackpressureSchema {
#[default]
Block,
Drop,
Buffer,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemporalAttributeSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub valid_time: ValidTimeSchemaConfig,
#[serde(default)]
pub transaction_time: TransactionTimeSchemaConfig,
#[serde(default)]
pub generate_version_chains: bool,
#[serde(default = "default_avg_versions")]
pub avg_versions_per_entity: f64,
}
fn default_avg_versions() -> f64 {
1.5
}
impl Default for TemporalAttributeSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
valid_time: ValidTimeSchemaConfig::default(),
transaction_time: TransactionTimeSchemaConfig::default(),
generate_version_chains: false,
avg_versions_per_entity: 1.5,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidTimeSchemaConfig {
#[serde(default = "default_closed_probability")]
pub closed_probability: f64,
#[serde(default = "default_avg_validity_days")]
pub avg_validity_days: u32,
#[serde(default = "default_validity_stddev")]
pub validity_stddev_days: u32,
}
fn default_closed_probability() -> f64 {
0.1
}
fn default_avg_validity_days() -> u32 {
365
}
fn default_validity_stddev() -> u32 {
90
}
impl Default for ValidTimeSchemaConfig {
fn default() -> Self {
Self {
closed_probability: 0.1,
avg_validity_days: 365,
validity_stddev_days: 90,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransactionTimeSchemaConfig {
#[serde(default)]
pub avg_recording_delay_seconds: u32,
#[serde(default)]
pub allow_backdating: bool,
#[serde(default = "default_backdating_probability")]
pub backdating_probability: f64,
#[serde(default = "default_max_backdate_days")]
pub max_backdate_days: u32,
}
fn default_backdating_probability() -> f64 {
0.01
}
fn default_max_backdate_days() -> u32 {
30
}
impl Default for TransactionTimeSchemaConfig {
fn default() -> Self {
Self {
avg_recording_delay_seconds: 0,
allow_backdating: false,
backdating_probability: 0.01,
max_backdate_days: 30,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RelationshipSchemaConfig {
#[serde(default)]
pub relationship_types: Vec<RelationshipTypeSchemaConfig>,
#[serde(default = "default_true")]
pub allow_orphans: bool,
#[serde(default = "default_orphan_probability")]
pub orphan_probability: f64,
#[serde(default)]
pub allow_circular: bool,
#[serde(default = "default_max_circular_depth")]
pub max_circular_depth: u32,
}
fn default_orphan_probability() -> f64 {
0.01
}
fn default_max_circular_depth() -> u32 {
3
}
impl Default for RelationshipSchemaConfig {
fn default() -> Self {
Self {
relationship_types: Vec::new(),
allow_orphans: true,
orphan_probability: 0.01,
allow_circular: false,
max_circular_depth: 3,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RelationshipTypeSchemaConfig {
pub name: String,
pub source_type: String,
pub target_type: String,
#[serde(default)]
pub cardinality: CardinalitySchemaRule,
#[serde(default = "default_relationship_weight")]
pub weight: f64,
#[serde(default)]
pub required: bool,
#[serde(default = "default_true")]
pub directed: bool,
}
fn default_relationship_weight() -> f64 {
1.0
}
impl Default for RelationshipTypeSchemaConfig {
fn default() -> Self {
Self {
name: String::new(),
source_type: String::new(),
target_type: String::new(),
cardinality: CardinalitySchemaRule::default(),
weight: 1.0,
required: false,
directed: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CardinalitySchemaRule {
OneToOne,
OneToMany {
min: u32,
max: u32,
},
ManyToOne {
min: u32,
max: u32,
},
ManyToMany {
min_per_source: u32,
max_per_source: u32,
},
}
impl Default for CardinalitySchemaRule {
fn default() -> Self {
Self::OneToMany { min: 1, max: 5 }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalConfig {
pub seed: Option<u64>,
pub industry: IndustrySector,
pub start_date: String,
pub period_months: u32,
#[serde(default = "default_currency")]
pub group_currency: String,
#[serde(default)]
pub presentation_currency: Option<String>,
#[serde(default = "default_true")]
pub parallel: bool,
#[serde(default)]
pub worker_threads: usize,
#[serde(default)]
pub memory_limit_mb: usize,
#[serde(default)]
pub fiscal_year_months: Option<u32>,
}
fn default_currency() -> String {
"USD".to_string()
}
fn default_true() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub checkpoint_path: Option<String>,
#[serde(default = "default_true")]
pub per_period_output: bool,
#[serde(default = "default_true")]
pub consolidated_output: bool,
}
impl Default for SessionSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
checkpoint_path: None,
per_period_output: true,
consolidated_output: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompanyConfig {
pub code: String,
pub name: String,
pub currency: String,
#[serde(default)]
pub functional_currency: Option<String>,
pub country: String,
#[serde(default = "default_fiscal_variant")]
pub fiscal_year_variant: String,
pub annual_transaction_volume: TransactionVolume,
#[serde(default = "default_weight")]
pub volume_weight: f64,
}
fn default_fiscal_variant() -> String {
"K4".to_string()
}
fn default_weight() -> f64 {
1.0
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TransactionVolume {
TenK,
HundredK,
OneM,
TenM,
HundredM,
Custom(u64),
}
impl TransactionVolume {
pub fn count(&self) -> u64 {
match self {
Self::TenK => 10_000,
Self::HundredK => 100_000,
Self::OneM => 1_000_000,
Self::TenM => 10_000_000,
Self::HundredM => 100_000_000,
Self::Custom(n) => *n,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChartOfAccountsConfig {
pub complexity: CoAComplexity,
#[serde(default = "default_true")]
pub industry_specific: bool,
pub custom_accounts: Option<PathBuf>,
#[serde(default = "default_min_depth")]
pub min_hierarchy_depth: u8,
#[serde(default = "default_max_depth")]
pub max_hierarchy_depth: u8,
}
fn default_min_depth() -> u8 {
2
}
fn default_max_depth() -> u8 {
5
}
impl Default for ChartOfAccountsConfig {
fn default() -> Self {
Self {
complexity: CoAComplexity::Small,
industry_specific: true,
custom_accounts: None,
min_hierarchy_depth: default_min_depth(),
max_hierarchy_depth: default_max_depth(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TransactionConfig {
#[serde(default)]
pub line_item_distribution: LineItemDistributionConfig,
#[serde(default)]
pub debit_credit_distribution: DebitCreditDistributionConfig,
#[serde(default)]
pub even_odd_distribution: EvenOddDistributionConfig,
#[serde(default)]
pub source_distribution: SourceDistribution,
#[serde(default)]
pub seasonality: SeasonalityConfig,
#[serde(default)]
pub amounts: AmountDistributionConfig,
#[serde(default)]
pub benford: BenfordConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenfordConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_benford_tolerance")]
pub tolerance: f64,
#[serde(default)]
pub exempt_sources: Vec<BenfordExemption>,
}
fn default_benford_tolerance() -> f64 {
0.05
}
impl Default for BenfordConfig {
fn default() -> Self {
Self {
enabled: true,
tolerance: default_benford_tolerance(),
exempt_sources: vec![BenfordExemption::Recurring, BenfordExemption::Payroll],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BenfordExemption {
Recurring,
Payroll,
FixedFees,
RoundAmounts,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceDistribution {
pub manual: f64,
pub automated: f64,
pub recurring: f64,
pub adjustment: f64,
}
impl Default for SourceDistribution {
fn default() -> Self {
Self {
manual: 0.20,
automated: 0.70,
recurring: 0.07,
adjustment: 0.03,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputConfig {
#[serde(default)]
pub mode: OutputMode,
pub output_directory: PathBuf,
#[serde(default = "default_formats")]
pub formats: Vec<FileFormat>,
#[serde(default)]
pub compression: CompressionConfig,
#[serde(default = "default_batch_size")]
pub batch_size: usize,
#[serde(default = "default_true")]
pub include_acdoca: bool,
#[serde(default)]
pub include_bseg: bool,
#[serde(default = "default_true")]
pub partition_by_period: bool,
#[serde(default)]
pub partition_by_company: bool,
#[serde(default)]
pub numeric_mode: NumericMode,
#[serde(default)]
pub export_layout: ExportLayout,
}
fn default_formats() -> Vec<FileFormat> {
vec![FileFormat::Parquet]
}
fn default_batch_size() -> usize {
100_000
}
impl Default for OutputConfig {
fn default() -> Self {
Self {
mode: OutputMode::FlatFile,
output_directory: PathBuf::from("./output"),
formats: default_formats(),
compression: CompressionConfig::default(),
batch_size: default_batch_size(),
include_acdoca: true,
include_bseg: false,
partition_by_period: true,
partition_by_company: false,
numeric_mode: NumericMode::default(),
export_layout: ExportLayout::default(),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum NumericMode {
#[default]
String,
Native,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ExportLayout {
#[default]
Nested,
Flat,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OutputMode {
Streaming,
#[default]
FlatFile,
Both,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FileFormat {
Csv,
Parquet,
Json,
JsonLines,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompressionConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default)]
pub algorithm: CompressionAlgorithm,
#[serde(default = "default_compression_level")]
pub level: u8,
}
fn default_compression_level() -> u8 {
3
}
impl Default for CompressionConfig {
fn default() -> Self {
Self {
enabled: true,
algorithm: CompressionAlgorithm::default(),
level: default_compression_level(),
}
}
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CompressionAlgorithm {
Gzip,
#[default]
Zstd,
Lz4,
Snappy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FraudConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_fraud_rate")]
pub fraud_rate: f64,
#[serde(default)]
pub fraud_type_distribution: FraudTypeDistribution,
#[serde(default)]
pub clustering_enabled: bool,
#[serde(default = "default_clustering_factor")]
pub clustering_factor: f64,
#[serde(default = "default_approval_thresholds")]
pub approval_thresholds: Vec<f64>,
}
fn default_approval_thresholds() -> Vec<f64> {
vec![1000.0, 5000.0, 10000.0, 25000.0, 50000.0, 100000.0]
}
fn default_fraud_rate() -> f64 {
0.005
}
fn default_clustering_factor() -> f64 {
3.0
}
impl Default for FraudConfig {
fn default() -> Self {
Self {
enabled: false,
fraud_rate: default_fraud_rate(),
fraud_type_distribution: FraudTypeDistribution::default(),
clustering_enabled: false,
clustering_factor: default_clustering_factor(),
approval_thresholds: default_approval_thresholds(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FraudTypeDistribution {
pub suspense_account_abuse: f64,
pub fictitious_transaction: f64,
pub revenue_manipulation: f64,
pub expense_capitalization: f64,
pub split_transaction: f64,
pub timing_anomaly: f64,
pub unauthorized_access: f64,
pub duplicate_payment: f64,
}
impl Default for FraudTypeDistribution {
fn default() -> Self {
Self {
suspense_account_abuse: 0.25,
fictitious_transaction: 0.15,
revenue_manipulation: 0.10,
expense_capitalization: 0.10,
split_transaction: 0.15,
timing_anomaly: 0.10,
unauthorized_access: 0.10,
duplicate_payment: 0.05,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InternalControlsConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_exception_rate")]
pub exception_rate: f64,
#[serde(default = "default_sod_violation_rate")]
pub sod_violation_rate: f64,
#[serde(default = "default_true")]
pub export_control_master_data: bool,
#[serde(default = "default_sox_materiality_threshold")]
pub sox_materiality_threshold: f64,
#[serde(default = "default_true")]
pub coso_enabled: bool,
#[serde(default)]
pub include_entity_level_controls: bool,
#[serde(default = "default_target_maturity_level")]
pub target_maturity_level: String,
}
fn default_exception_rate() -> f64 {
0.02
}
fn default_sod_violation_rate() -> f64 {
0.01
}
fn default_sox_materiality_threshold() -> f64 {
10000.0
}
fn default_target_maturity_level() -> String {
"mixed".to_string()
}
impl Default for InternalControlsConfig {
fn default() -> Self {
Self {
enabled: false,
exception_rate: default_exception_rate(),
sod_violation_rate: default_sod_violation_rate(),
export_control_master_data: true,
sox_materiality_threshold: default_sox_materiality_threshold(),
coso_enabled: true,
include_entity_level_controls: false,
target_maturity_level: default_target_maturity_level(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BusinessProcessConfig {
#[serde(default = "default_o2c")]
pub o2c_weight: f64,
#[serde(default = "default_p2p")]
pub p2p_weight: f64,
#[serde(default = "default_r2r")]
pub r2r_weight: f64,
#[serde(default = "default_h2r")]
pub h2r_weight: f64,
#[serde(default = "default_a2r")]
pub a2r_weight: f64,
}
fn default_o2c() -> f64 {
0.35
}
fn default_p2p() -> f64 {
0.30
}
fn default_r2r() -> f64 {
0.20
}
fn default_h2r() -> f64 {
0.10
}
fn default_a2r() -> f64 {
0.05
}
impl Default for BusinessProcessConfig {
fn default() -> Self {
Self {
o2c_weight: default_o2c(),
p2p_weight: default_p2p(),
r2r_weight: default_r2r(),
h2r_weight: default_h2r(),
a2r_weight: default_a2r(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UserPersonaConfig {
#[serde(default)]
pub persona_distribution: PersonaDistribution,
#[serde(default)]
pub users_per_persona: UsersPerPersona,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersonaDistribution {
pub junior_accountant: f64,
pub senior_accountant: f64,
pub controller: f64,
pub manager: f64,
pub automated_system: f64,
}
impl Default for PersonaDistribution {
fn default() -> Self {
Self {
junior_accountant: 0.15,
senior_accountant: 0.15,
controller: 0.05,
manager: 0.05,
automated_system: 0.60,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UsersPerPersona {
pub junior_accountant: usize,
pub senior_accountant: usize,
pub controller: usize,
pub manager: usize,
pub automated_system: usize,
}
impl Default for UsersPerPersona {
fn default() -> Self {
Self {
junior_accountant: 10,
senior_accountant: 5,
controller: 2,
manager: 3,
automated_system: 20,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TemplateConfig {
#[serde(default)]
pub names: NameTemplateConfig,
#[serde(default)]
pub descriptions: DescriptionTemplateConfig,
#[serde(default)]
pub references: ReferenceTemplateConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NameTemplateConfig {
#[serde(default)]
pub culture_distribution: CultureDistribution,
#[serde(default = "default_email_domain")]
pub email_domain: String,
#[serde(default = "default_true")]
pub generate_realistic_names: bool,
}
fn default_email_domain() -> String {
"company.com".to_string()
}
impl Default for NameTemplateConfig {
fn default() -> Self {
Self {
culture_distribution: CultureDistribution::default(),
email_domain: default_email_domain(),
generate_realistic_names: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CultureDistribution {
pub western_us: f64,
pub hispanic: f64,
pub german: f64,
pub french: f64,
pub chinese: f64,
pub japanese: f64,
pub indian: f64,
}
impl Default for CultureDistribution {
fn default() -> Self {
Self {
western_us: 0.40,
hispanic: 0.20,
german: 0.10,
french: 0.05,
chinese: 0.10,
japanese: 0.05,
indian: 0.10,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DescriptionTemplateConfig {
#[serde(default = "default_true")]
pub generate_header_text: bool,
#[serde(default = "default_true")]
pub generate_line_text: bool,
}
impl Default for DescriptionTemplateConfig {
fn default() -> Self {
Self {
generate_header_text: true,
generate_line_text: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReferenceTemplateConfig {
#[serde(default = "default_true")]
pub generate_references: bool,
#[serde(default = "default_invoice_prefix")]
pub invoice_prefix: String,
#[serde(default = "default_po_prefix")]
pub po_prefix: String,
#[serde(default = "default_so_prefix")]
pub so_prefix: String,
}
fn default_invoice_prefix() -> String {
"INV".to_string()
}
fn default_po_prefix() -> String {
"PO".to_string()
}
fn default_so_prefix() -> String {
"SO".to_string()
}
impl Default for ReferenceTemplateConfig {
fn default() -> Self {
Self {
generate_references: true,
invoice_prefix: default_invoice_prefix(),
po_prefix: default_po_prefix(),
so_prefix: default_so_prefix(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApprovalConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_auto_approve_threshold")]
pub auto_approve_threshold: f64,
#[serde(default = "default_rejection_rate")]
pub rejection_rate: f64,
#[serde(default = "default_revision_rate")]
pub revision_rate: f64,
#[serde(default = "default_approval_delay_hours")]
pub average_approval_delay_hours: f64,
#[serde(default)]
pub thresholds: Vec<ApprovalThresholdConfig>,
}
fn default_auto_approve_threshold() -> f64 {
1000.0
}
fn default_rejection_rate() -> f64 {
0.02
}
fn default_revision_rate() -> f64 {
0.05
}
fn default_approval_delay_hours() -> f64 {
4.0
}
impl Default for ApprovalConfig {
fn default() -> Self {
Self {
enabled: false,
auto_approve_threshold: default_auto_approve_threshold(),
rejection_rate: default_rejection_rate(),
revision_rate: default_revision_rate(),
average_approval_delay_hours: default_approval_delay_hours(),
thresholds: vec![
ApprovalThresholdConfig {
amount: 1000.0,
level: 1,
roles: vec!["senior_accountant".to_string()],
},
ApprovalThresholdConfig {
amount: 10000.0,
level: 2,
roles: vec!["senior_accountant".to_string(), "controller".to_string()],
},
ApprovalThresholdConfig {
amount: 100000.0,
level: 3,
roles: vec![
"senior_accountant".to_string(),
"controller".to_string(),
"manager".to_string(),
],
},
ApprovalThresholdConfig {
amount: 500000.0,
level: 4,
roles: vec![
"senior_accountant".to_string(),
"controller".to_string(),
"manager".to_string(),
"executive".to_string(),
],
},
],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApprovalThresholdConfig {
pub amount: f64,
pub level: u8,
pub roles: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DepartmentConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_headcount_multiplier")]
pub headcount_multiplier: f64,
#[serde(default)]
pub custom_departments: Vec<CustomDepartmentConfig>,
}
fn default_headcount_multiplier() -> f64 {
1.0
}
impl Default for DepartmentConfig {
fn default() -> Self {
Self {
enabled: false,
headcount_multiplier: default_headcount_multiplier(),
custom_departments: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomDepartmentConfig {
pub code: String,
pub name: String,
#[serde(default)]
pub cost_center: Option<String>,
#[serde(default)]
pub primary_processes: Vec<String>,
#[serde(default)]
pub parent_code: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct MasterDataConfig {
#[serde(default)]
pub vendors: VendorMasterConfig,
#[serde(default)]
pub customers: CustomerMasterConfig,
#[serde(default)]
pub materials: MaterialMasterConfig,
#[serde(default)]
pub fixed_assets: FixedAssetMasterConfig,
#[serde(default)]
pub employees: EmployeeMasterConfig,
#[serde(default)]
pub cost_centers: CostCenterMasterConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VendorMasterConfig {
#[serde(default = "default_vendor_count")]
pub count: usize,
#[serde(default = "default_intercompany_percent")]
pub intercompany_percent: f64,
#[serde(default)]
pub payment_terms_distribution: PaymentTermsDistribution,
#[serde(default)]
pub behavior_distribution: VendorBehaviorDistribution,
#[serde(default = "default_true")]
pub generate_bank_accounts: bool,
#[serde(default = "default_true")]
pub generate_tax_ids: bool,
}
fn default_vendor_count() -> usize {
500
}
fn default_intercompany_percent() -> f64 {
0.05
}
impl Default for VendorMasterConfig {
fn default() -> Self {
Self {
count: default_vendor_count(),
intercompany_percent: default_intercompany_percent(),
payment_terms_distribution: PaymentTermsDistribution::default(),
behavior_distribution: VendorBehaviorDistribution::default(),
generate_bank_accounts: true,
generate_tax_ids: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentTermsDistribution {
pub net_30: f64,
pub net_60: f64,
pub net_90: f64,
pub two_ten_net_30: f64,
pub due_on_receipt: f64,
pub end_of_month: f64,
}
impl Default for PaymentTermsDistribution {
fn default() -> Self {
Self {
net_30: 0.40,
net_60: 0.20,
net_90: 0.10,
two_ten_net_30: 0.15,
due_on_receipt: 0.05,
end_of_month: 0.10,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VendorBehaviorDistribution {
pub reliable: f64,
pub sometimes_late: f64,
pub inconsistent_quality: f64,
pub premium: f64,
pub budget: f64,
}
impl Default for VendorBehaviorDistribution {
fn default() -> Self {
Self {
reliable: 0.50,
sometimes_late: 0.20,
inconsistent_quality: 0.10,
premium: 0.10,
budget: 0.10,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomerMasterConfig {
#[serde(default = "default_customer_count")]
pub count: usize,
#[serde(default = "default_intercompany_percent")]
pub intercompany_percent: f64,
#[serde(default)]
pub credit_rating_distribution: CreditRatingDistribution,
#[serde(default)]
pub payment_behavior_distribution: PaymentBehaviorDistribution,
#[serde(default = "default_true")]
pub generate_credit_limits: bool,
}
fn default_customer_count() -> usize {
2000
}
impl Default for CustomerMasterConfig {
fn default() -> Self {
Self {
count: default_customer_count(),
intercompany_percent: default_intercompany_percent(),
credit_rating_distribution: CreditRatingDistribution::default(),
payment_behavior_distribution: PaymentBehaviorDistribution::default(),
generate_credit_limits: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreditRatingDistribution {
pub aaa: f64,
pub aa: f64,
pub a: f64,
pub bbb: f64,
pub bb: f64,
pub b: f64,
pub below_b: f64,
}
impl Default for CreditRatingDistribution {
fn default() -> Self {
Self {
aaa: 0.05,
aa: 0.10,
a: 0.20,
bbb: 0.30,
bb: 0.20,
b: 0.10,
below_b: 0.05,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentBehaviorDistribution {
pub early_payer: f64,
pub on_time: f64,
pub occasional_late: f64,
pub frequent_late: f64,
pub discount_taker: f64,
}
impl Default for PaymentBehaviorDistribution {
fn default() -> Self {
Self {
early_payer: 0.10,
on_time: 0.50,
occasional_late: 0.25,
frequent_late: 0.10,
discount_taker: 0.05,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaterialMasterConfig {
#[serde(default = "default_material_count")]
pub count: usize,
#[serde(default)]
pub type_distribution: MaterialTypeDistribution,
#[serde(default)]
pub valuation_distribution: ValuationMethodDistribution,
#[serde(default = "default_bom_percent")]
pub bom_percent: f64,
#[serde(default = "default_max_bom_depth")]
pub max_bom_depth: u8,
}
fn default_material_count() -> usize {
5000
}
fn default_bom_percent() -> f64 {
0.20
}
fn default_max_bom_depth() -> u8 {
3
}
impl Default for MaterialMasterConfig {
fn default() -> Self {
Self {
count: default_material_count(),
type_distribution: MaterialTypeDistribution::default(),
valuation_distribution: ValuationMethodDistribution::default(),
bom_percent: default_bom_percent(),
max_bom_depth: default_max_bom_depth(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaterialTypeDistribution {
pub raw_material: f64,
pub semi_finished: f64,
pub finished_good: f64,
pub trading_good: f64,
pub operating_supply: f64,
pub service: f64,
}
impl Default for MaterialTypeDistribution {
fn default() -> Self {
Self {
raw_material: 0.30,
semi_finished: 0.15,
finished_good: 0.25,
trading_good: 0.15,
operating_supply: 0.10,
service: 0.05,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValuationMethodDistribution {
pub standard_cost: f64,
pub moving_average: f64,
pub fifo: f64,
pub lifo: f64,
}
impl Default for ValuationMethodDistribution {
fn default() -> Self {
Self {
standard_cost: 0.50,
moving_average: 0.30,
fifo: 0.15,
lifo: 0.05,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FixedAssetMasterConfig {
#[serde(default = "default_asset_count")]
pub count: usize,
#[serde(default)]
pub class_distribution: AssetClassDistribution,
#[serde(default)]
pub depreciation_distribution: DepreciationMethodDistribution,
#[serde(default = "default_fully_depreciated_percent")]
pub fully_depreciated_percent: f64,
#[serde(default = "default_true")]
pub generate_acquisition_history: bool,
}
fn default_asset_count() -> usize {
800
}
fn default_fully_depreciated_percent() -> f64 {
0.15
}
impl Default for FixedAssetMasterConfig {
fn default() -> Self {
Self {
count: default_asset_count(),
class_distribution: AssetClassDistribution::default(),
depreciation_distribution: DepreciationMethodDistribution::default(),
fully_depreciated_percent: default_fully_depreciated_percent(),
generate_acquisition_history: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssetClassDistribution {
pub buildings: f64,
pub machinery: f64,
pub vehicles: f64,
pub it_equipment: f64,
pub furniture: f64,
pub land: f64,
pub leasehold: f64,
}
impl Default for AssetClassDistribution {
fn default() -> Self {
Self {
buildings: 0.15,
machinery: 0.30,
vehicles: 0.15,
it_equipment: 0.20,
furniture: 0.10,
land: 0.05,
leasehold: 0.05,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DepreciationMethodDistribution {
pub straight_line: f64,
pub declining_balance: f64,
pub double_declining: f64,
pub sum_of_years: f64,
pub units_of_production: f64,
}
impl Default for DepreciationMethodDistribution {
fn default() -> Self {
Self {
straight_line: 0.60,
declining_balance: 0.20,
double_declining: 0.10,
sum_of_years: 0.05,
units_of_production: 0.05,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmployeeMasterConfig {
#[serde(default = "default_employee_count")]
pub count: usize,
#[serde(default = "default_true")]
pub generate_hierarchy: bool,
#[serde(default = "default_hierarchy_depth")]
pub max_hierarchy_depth: u8,
#[serde(default = "default_span_of_control")]
pub average_span_of_control: f64,
#[serde(default)]
pub approval_limits: ApprovalLimitDistribution,
#[serde(default)]
pub department_distribution: EmployeeDepartmentDistribution,
}
fn default_employee_count() -> usize {
1500
}
fn default_hierarchy_depth() -> u8 {
6
}
fn default_span_of_control() -> f64 {
5.0
}
impl Default for EmployeeMasterConfig {
fn default() -> Self {
Self {
count: default_employee_count(),
generate_hierarchy: true,
max_hierarchy_depth: default_hierarchy_depth(),
average_span_of_control: default_span_of_control(),
approval_limits: ApprovalLimitDistribution::default(),
department_distribution: EmployeeDepartmentDistribution::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApprovalLimitDistribution {
#[serde(default = "default_staff_limit")]
pub staff: f64,
#[serde(default = "default_senior_limit")]
pub senior: f64,
#[serde(default = "default_manager_limit")]
pub manager: f64,
#[serde(default = "default_director_limit")]
pub director: f64,
#[serde(default = "default_vp_limit")]
pub vp: f64,
#[serde(default = "default_executive_limit")]
pub executive: f64,
}
fn default_staff_limit() -> f64 {
1000.0
}
fn default_senior_limit() -> f64 {
5000.0
}
fn default_manager_limit() -> f64 {
25000.0
}
fn default_director_limit() -> f64 {
100000.0
}
fn default_vp_limit() -> f64 {
500000.0
}
fn default_executive_limit() -> f64 {
f64::INFINITY
}
impl Default for ApprovalLimitDistribution {
fn default() -> Self {
Self {
staff: default_staff_limit(),
senior: default_senior_limit(),
manager: default_manager_limit(),
director: default_director_limit(),
vp: default_vp_limit(),
executive: default_executive_limit(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmployeeDepartmentDistribution {
pub finance: f64,
pub procurement: f64,
pub sales: f64,
pub warehouse: f64,
pub it: f64,
pub hr: f64,
pub operations: f64,
pub executive: f64,
}
impl Default for EmployeeDepartmentDistribution {
fn default() -> Self {
Self {
finance: 0.12,
procurement: 0.10,
sales: 0.25,
warehouse: 0.15,
it: 0.10,
hr: 0.05,
operations: 0.20,
executive: 0.03,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CostCenterMasterConfig {
#[serde(default = "default_cost_center_count")]
pub count: usize,
#[serde(default = "default_true")]
pub generate_hierarchy: bool,
#[serde(default = "default_cc_hierarchy_depth")]
pub max_hierarchy_depth: u8,
}
fn default_cost_center_count() -> usize {
50
}
fn default_cc_hierarchy_depth() -> u8 {
3
}
impl Default for CostCenterMasterConfig {
fn default() -> Self {
Self {
count: default_cost_center_count(),
generate_hierarchy: true,
max_hierarchy_depth: default_cc_hierarchy_depth(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocumentFlowConfig {
#[serde(default)]
pub p2p: P2PFlowConfig,
#[serde(default)]
pub o2c: O2CFlowConfig,
#[serde(default = "default_true")]
pub generate_document_references: bool,
#[serde(default)]
pub export_flow_graph: bool,
}
impl Default for DocumentFlowConfig {
fn default() -> Self {
Self {
p2p: P2PFlowConfig::default(),
o2c: O2CFlowConfig::default(),
generate_document_references: true,
export_flow_graph: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct P2PFlowConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_three_way_match_rate")]
pub three_way_match_rate: f64,
#[serde(default = "default_partial_delivery_rate")]
pub partial_delivery_rate: f64,
#[serde(default = "default_price_variance_rate")]
pub price_variance_rate: f64,
#[serde(default = "default_max_price_variance")]
pub max_price_variance_percent: f64,
#[serde(default = "default_quantity_variance_rate")]
pub quantity_variance_rate: f64,
#[serde(default = "default_po_to_gr_days")]
pub average_po_to_gr_days: u32,
#[serde(default = "default_gr_to_invoice_days")]
pub average_gr_to_invoice_days: u32,
#[serde(default = "default_invoice_to_payment_days")]
pub average_invoice_to_payment_days: u32,
#[serde(default)]
pub line_count_distribution: DocumentLineCountDistribution,
#[serde(default)]
pub payment_behavior: P2PPaymentBehaviorConfig,
#[serde(default)]
pub over_delivery_rate: Option<f64>,
#[serde(default)]
pub early_payment_discount_rate: Option<f64>,
}
fn default_three_way_match_rate() -> f64 {
0.95
}
fn default_partial_delivery_rate() -> f64 {
0.15
}
fn default_price_variance_rate() -> f64 {
0.08
}
fn default_max_price_variance() -> f64 {
0.05
}
fn default_quantity_variance_rate() -> f64 {
0.05
}
fn default_po_to_gr_days() -> u32 {
14
}
fn default_gr_to_invoice_days() -> u32 {
5
}
fn default_invoice_to_payment_days() -> u32 {
30
}
impl Default for P2PFlowConfig {
fn default() -> Self {
Self {
enabled: true,
three_way_match_rate: default_three_way_match_rate(),
partial_delivery_rate: default_partial_delivery_rate(),
price_variance_rate: default_price_variance_rate(),
max_price_variance_percent: default_max_price_variance(),
quantity_variance_rate: default_quantity_variance_rate(),
average_po_to_gr_days: default_po_to_gr_days(),
average_gr_to_invoice_days: default_gr_to_invoice_days(),
average_invoice_to_payment_days: default_invoice_to_payment_days(),
line_count_distribution: DocumentLineCountDistribution::default(),
payment_behavior: P2PPaymentBehaviorConfig::default(),
over_delivery_rate: None,
early_payment_discount_rate: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct P2PPaymentBehaviorConfig {
#[serde(default = "default_p2p_late_payment_rate")]
pub late_payment_rate: f64,
#[serde(default)]
pub late_payment_days_distribution: LatePaymentDaysDistribution,
#[serde(default = "default_p2p_partial_payment_rate")]
pub partial_payment_rate: f64,
#[serde(default = "default_p2p_payment_correction_rate")]
pub payment_correction_rate: f64,
#[serde(default = "default_p2p_avg_days_until_remainder")]
pub avg_days_until_remainder: u32,
}
fn default_p2p_late_payment_rate() -> f64 {
0.15
}
fn default_p2p_partial_payment_rate() -> f64 {
0.05
}
fn default_p2p_payment_correction_rate() -> f64 {
0.02
}
fn default_p2p_avg_days_until_remainder() -> u32 {
30
}
impl Default for P2PPaymentBehaviorConfig {
fn default() -> Self {
Self {
late_payment_rate: default_p2p_late_payment_rate(),
late_payment_days_distribution: LatePaymentDaysDistribution::default(),
partial_payment_rate: default_p2p_partial_payment_rate(),
payment_correction_rate: default_p2p_payment_correction_rate(),
avg_days_until_remainder: default_p2p_avg_days_until_remainder(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LatePaymentDaysDistribution {
#[serde(default = "default_slightly_late")]
pub slightly_late_1_to_7: f64,
#[serde(default = "default_late_8_14")]
pub late_8_to_14: f64,
#[serde(default = "default_very_late")]
pub very_late_15_to_30: f64,
#[serde(default = "default_severely_late")]
pub severely_late_31_to_60: f64,
#[serde(default = "default_extremely_late")]
pub extremely_late_over_60: f64,
}
fn default_slightly_late() -> f64 {
0.50
}
fn default_late_8_14() -> f64 {
0.25
}
fn default_very_late() -> f64 {
0.15
}
fn default_severely_late() -> f64 {
0.07
}
fn default_extremely_late() -> f64 {
0.03
}
impl Default for LatePaymentDaysDistribution {
fn default() -> Self {
Self {
slightly_late_1_to_7: default_slightly_late(),
late_8_to_14: default_late_8_14(),
very_late_15_to_30: default_very_late(),
severely_late_31_to_60: default_severely_late(),
extremely_late_over_60: default_extremely_late(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct O2CFlowConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_credit_check_failure_rate")]
pub credit_check_failure_rate: f64,
#[serde(default = "default_partial_shipment_rate")]
pub partial_shipment_rate: f64,
#[serde(default = "default_return_rate")]
pub return_rate: f64,
#[serde(default = "default_bad_debt_rate")]
pub bad_debt_rate: f64,
#[serde(default = "default_so_to_delivery_days")]
pub average_so_to_delivery_days: u32,
#[serde(default = "default_delivery_to_invoice_days")]
pub average_delivery_to_invoice_days: u32,
#[serde(default = "default_invoice_to_receipt_days")]
pub average_invoice_to_receipt_days: u32,
#[serde(default)]
pub line_count_distribution: DocumentLineCountDistribution,
#[serde(default)]
pub cash_discount: CashDiscountConfig,
#[serde(default)]
pub payment_behavior: O2CPaymentBehaviorConfig,
#[serde(default)]
pub late_payment_rate: Option<f64>,
}
fn default_credit_check_failure_rate() -> f64 {
0.02
}
fn default_partial_shipment_rate() -> f64 {
0.10
}
fn default_return_rate() -> f64 {
0.03
}
fn default_bad_debt_rate() -> f64 {
0.01
}
fn default_so_to_delivery_days() -> u32 {
7
}
fn default_delivery_to_invoice_days() -> u32 {
1
}
fn default_invoice_to_receipt_days() -> u32 {
45
}
impl Default for O2CFlowConfig {
fn default() -> Self {
Self {
enabled: true,
credit_check_failure_rate: default_credit_check_failure_rate(),
partial_shipment_rate: default_partial_shipment_rate(),
return_rate: default_return_rate(),
bad_debt_rate: default_bad_debt_rate(),
average_so_to_delivery_days: default_so_to_delivery_days(),
average_delivery_to_invoice_days: default_delivery_to_invoice_days(),
average_invoice_to_receipt_days: default_invoice_to_receipt_days(),
line_count_distribution: DocumentLineCountDistribution::default(),
cash_discount: CashDiscountConfig::default(),
payment_behavior: O2CPaymentBehaviorConfig::default(),
late_payment_rate: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct O2CPaymentBehaviorConfig {
#[serde(default)]
pub dunning: DunningConfig,
#[serde(default)]
pub partial_payments: PartialPaymentConfig,
#[serde(default)]
pub short_payments: ShortPaymentConfig,
#[serde(default)]
pub on_account_payments: OnAccountPaymentConfig,
#[serde(default)]
pub payment_corrections: PaymentCorrectionConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DunningConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_dunning_level_1_days")]
pub level_1_days_overdue: u32,
#[serde(default = "default_dunning_level_2_days")]
pub level_2_days_overdue: u32,
#[serde(default = "default_dunning_level_3_days")]
pub level_3_days_overdue: u32,
#[serde(default = "default_collection_days")]
pub collection_days_overdue: u32,
#[serde(default)]
pub payment_after_dunning_rates: DunningPaymentRates,
#[serde(default = "default_dunning_block_rate")]
pub dunning_block_rate: f64,
#[serde(default = "default_dunning_interest_rate")]
pub interest_rate_per_year: f64,
#[serde(default = "default_dunning_charge")]
pub dunning_charge: f64,
}
fn default_dunning_level_1_days() -> u32 {
14
}
fn default_dunning_level_2_days() -> u32 {
28
}
fn default_dunning_level_3_days() -> u32 {
42
}
fn default_collection_days() -> u32 {
60
}
fn default_dunning_block_rate() -> f64 {
0.05
}
fn default_dunning_interest_rate() -> f64 {
0.09
}
fn default_dunning_charge() -> f64 {
25.0
}
impl Default for DunningConfig {
fn default() -> Self {
Self {
enabled: false,
level_1_days_overdue: default_dunning_level_1_days(),
level_2_days_overdue: default_dunning_level_2_days(),
level_3_days_overdue: default_dunning_level_3_days(),
collection_days_overdue: default_collection_days(),
payment_after_dunning_rates: DunningPaymentRates::default(),
dunning_block_rate: default_dunning_block_rate(),
interest_rate_per_year: default_dunning_interest_rate(),
dunning_charge: default_dunning_charge(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DunningPaymentRates {
#[serde(default = "default_after_level_1")]
pub after_level_1: f64,
#[serde(default = "default_after_level_2")]
pub after_level_2: f64,
#[serde(default = "default_after_level_3")]
pub after_level_3: f64,
#[serde(default = "default_during_collection")]
pub during_collection: f64,
#[serde(default = "default_never_pay")]
pub never_pay: f64,
}
fn default_after_level_1() -> f64 {
0.40
}
fn default_after_level_2() -> f64 {
0.30
}
fn default_after_level_3() -> f64 {
0.15
}
fn default_during_collection() -> f64 {
0.05
}
fn default_never_pay() -> f64 {
0.10
}
impl Default for DunningPaymentRates {
fn default() -> Self {
Self {
after_level_1: default_after_level_1(),
after_level_2: default_after_level_2(),
after_level_3: default_after_level_3(),
during_collection: default_during_collection(),
never_pay: default_never_pay(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PartialPaymentConfig {
#[serde(default = "default_partial_payment_rate")]
pub rate: f64,
#[serde(default)]
pub percentage_distribution: PartialPaymentPercentageDistribution,
#[serde(default = "default_avg_days_until_remainder")]
pub avg_days_until_remainder: u32,
}
fn default_partial_payment_rate() -> f64 {
0.08
}
fn default_avg_days_until_remainder() -> u32 {
30
}
impl Default for PartialPaymentConfig {
fn default() -> Self {
Self {
rate: default_partial_payment_rate(),
percentage_distribution: PartialPaymentPercentageDistribution::default(),
avg_days_until_remainder: default_avg_days_until_remainder(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PartialPaymentPercentageDistribution {
#[serde(default = "default_partial_25")]
pub pay_25_percent: f64,
#[serde(default = "default_partial_50")]
pub pay_50_percent: f64,
#[serde(default = "default_partial_75")]
pub pay_75_percent: f64,
#[serde(default = "default_partial_random")]
pub pay_random_percent: f64,
}
fn default_partial_25() -> f64 {
0.15
}
fn default_partial_50() -> f64 {
0.50
}
fn default_partial_75() -> f64 {
0.25
}
fn default_partial_random() -> f64 {
0.10
}
impl Default for PartialPaymentPercentageDistribution {
fn default() -> Self {
Self {
pay_25_percent: default_partial_25(),
pay_50_percent: default_partial_50(),
pay_75_percent: default_partial_75(),
pay_random_percent: default_partial_random(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShortPaymentConfig {
#[serde(default = "default_short_payment_rate")]
pub rate: f64,
#[serde(default)]
pub reason_distribution: ShortPaymentReasonDistribution,
#[serde(default = "default_max_short_percent")]
pub max_short_percent: f64,
}
fn default_short_payment_rate() -> f64 {
0.03
}
fn default_max_short_percent() -> f64 {
0.10
}
impl Default for ShortPaymentConfig {
fn default() -> Self {
Self {
rate: default_short_payment_rate(),
reason_distribution: ShortPaymentReasonDistribution::default(),
max_short_percent: default_max_short_percent(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShortPaymentReasonDistribution {
#[serde(default = "default_pricing_dispute")]
pub pricing_dispute: f64,
#[serde(default = "default_quality_issue")]
pub quality_issue: f64,
#[serde(default = "default_quantity_discrepancy")]
pub quantity_discrepancy: f64,
#[serde(default = "default_unauthorized_deduction")]
pub unauthorized_deduction: f64,
#[serde(default = "default_incorrect_discount")]
pub incorrect_discount: f64,
}
fn default_pricing_dispute() -> f64 {
0.30
}
fn default_quality_issue() -> f64 {
0.20
}
fn default_quantity_discrepancy() -> f64 {
0.20
}
fn default_unauthorized_deduction() -> f64 {
0.15
}
fn default_incorrect_discount() -> f64 {
0.15
}
impl Default for ShortPaymentReasonDistribution {
fn default() -> Self {
Self {
pricing_dispute: default_pricing_dispute(),
quality_issue: default_quality_issue(),
quantity_discrepancy: default_quantity_discrepancy(),
unauthorized_deduction: default_unauthorized_deduction(),
incorrect_discount: default_incorrect_discount(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OnAccountPaymentConfig {
#[serde(default = "default_on_account_rate")]
pub rate: f64,
#[serde(default = "default_avg_days_until_applied")]
pub avg_days_until_applied: u32,
}
fn default_on_account_rate() -> f64 {
0.02
}
fn default_avg_days_until_applied() -> u32 {
14
}
impl Default for OnAccountPaymentConfig {
fn default() -> Self {
Self {
rate: default_on_account_rate(),
avg_days_until_applied: default_avg_days_until_applied(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentCorrectionConfig {
#[serde(default = "default_payment_correction_rate")]
pub rate: f64,
#[serde(default)]
pub type_distribution: PaymentCorrectionTypeDistribution,
}
fn default_payment_correction_rate() -> f64 {
0.02
}
impl Default for PaymentCorrectionConfig {
fn default() -> Self {
Self {
rate: default_payment_correction_rate(),
type_distribution: PaymentCorrectionTypeDistribution::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentCorrectionTypeDistribution {
#[serde(default = "default_nsf_rate")]
pub nsf: f64,
#[serde(default = "default_chargeback_rate")]
pub chargeback: f64,
#[serde(default = "default_wrong_amount_rate")]
pub wrong_amount: f64,
#[serde(default = "default_wrong_customer_rate")]
pub wrong_customer: f64,
#[serde(default = "default_duplicate_payment_rate")]
pub duplicate_payment: f64,
}
fn default_nsf_rate() -> f64 {
0.30
}
fn default_chargeback_rate() -> f64 {
0.20
}
fn default_wrong_amount_rate() -> f64 {
0.20
}
fn default_wrong_customer_rate() -> f64 {
0.15
}
fn default_duplicate_payment_rate() -> f64 {
0.15
}
impl Default for PaymentCorrectionTypeDistribution {
fn default() -> Self {
Self {
nsf: default_nsf_rate(),
chargeback: default_chargeback_rate(),
wrong_amount: default_wrong_amount_rate(),
wrong_customer: default_wrong_customer_rate(),
duplicate_payment: default_duplicate_payment_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocumentLineCountDistribution {
#[serde(default = "default_min_lines")]
pub min_lines: u32,
#[serde(default = "default_max_lines")]
pub max_lines: u32,
#[serde(default = "default_mode_lines")]
pub mode_lines: u32,
}
fn default_min_lines() -> u32 {
1
}
fn default_max_lines() -> u32 {
20
}
fn default_mode_lines() -> u32 {
3
}
impl Default for DocumentLineCountDistribution {
fn default() -> Self {
Self {
min_lines: default_min_lines(),
max_lines: default_max_lines(),
mode_lines: default_mode_lines(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CashDiscountConfig {
#[serde(default = "default_discount_eligible_rate")]
pub eligible_rate: f64,
#[serde(default = "default_discount_taken_rate")]
pub taken_rate: f64,
#[serde(default = "default_discount_percent")]
pub discount_percent: f64,
#[serde(default = "default_discount_days")]
pub discount_days: u32,
}
fn default_discount_eligible_rate() -> f64 {
0.30
}
fn default_discount_taken_rate() -> f64 {
0.60
}
fn default_discount_percent() -> f64 {
0.02
}
fn default_discount_days() -> u32 {
10
}
impl Default for CashDiscountConfig {
fn default() -> Self {
Self {
eligible_rate: default_discount_eligible_rate(),
taken_rate: default_discount_taken_rate(),
discount_percent: default_discount_percent(),
discount_days: default_discount_days(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IntercompanyConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_ic_transaction_rate")]
pub ic_transaction_rate: f64,
#[serde(default)]
pub transfer_pricing_method: TransferPricingMethod,
#[serde(default = "default_markup_percent")]
pub markup_percent: f64,
#[serde(default = "default_true")]
pub generate_matched_pairs: bool,
#[serde(default)]
pub transaction_type_distribution: ICTransactionTypeDistribution,
#[serde(default)]
pub generate_eliminations: bool,
}
fn default_ic_transaction_rate() -> f64 {
0.15
}
fn default_markup_percent() -> f64 {
0.05
}
impl Default for IntercompanyConfig {
fn default() -> Self {
Self {
enabled: false,
ic_transaction_rate: default_ic_transaction_rate(),
transfer_pricing_method: TransferPricingMethod::default(),
markup_percent: default_markup_percent(),
generate_matched_pairs: true,
transaction_type_distribution: ICTransactionTypeDistribution::default(),
generate_eliminations: false,
}
}
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TransferPricingMethod {
#[default]
CostPlus,
ComparableUncontrolled,
ResalePrice,
TransactionalNetMargin,
ProfitSplit,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ICTransactionTypeDistribution {
pub goods_sale: f64,
pub service_provided: f64,
pub loan: f64,
pub dividend: f64,
pub management_fee: f64,
pub royalty: f64,
pub cost_sharing: f64,
}
impl Default for ICTransactionTypeDistribution {
fn default() -> Self {
Self {
goods_sale: 0.35,
service_provided: 0.20,
loan: 0.10,
dividend: 0.05,
management_fee: 0.15,
royalty: 0.10,
cost_sharing: 0.05,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BalanceConfig {
#[serde(default)]
pub generate_opening_balances: bool,
#[serde(default = "default_true")]
pub generate_trial_balances: bool,
#[serde(default = "default_gross_margin")]
pub target_gross_margin: f64,
#[serde(default = "default_dso")]
pub target_dso_days: u32,
#[serde(default = "default_dpo")]
pub target_dpo_days: u32,
#[serde(default = "default_current_ratio")]
pub target_current_ratio: f64,
#[serde(default = "default_debt_equity")]
pub target_debt_to_equity: f64,
#[serde(default = "default_true")]
pub validate_balance_equation: bool,
#[serde(default = "default_true")]
pub reconcile_subledgers: bool,
}
fn default_gross_margin() -> f64 {
0.35
}
fn default_dso() -> u32 {
45
}
fn default_dpo() -> u32 {
30
}
fn default_current_ratio() -> f64 {
1.5
}
fn default_debt_equity() -> f64 {
0.5
}
impl Default for BalanceConfig {
fn default() -> Self {
Self {
generate_opening_balances: false,
generate_trial_balances: true,
target_gross_margin: default_gross_margin(),
target_dso_days: default_dso(),
target_dpo_days: default_dpo(),
target_current_ratio: default_current_ratio(),
target_debt_to_equity: default_debt_equity(),
validate_balance_equation: true,
reconcile_subledgers: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OcpmConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_true")]
pub generate_lifecycle_events: bool,
#[serde(default = "default_true")]
pub include_object_relationships: bool,
#[serde(default = "default_true")]
pub compute_variants: bool,
#[serde(default)]
pub max_variants: usize,
#[serde(default)]
pub p2p_process: OcpmProcessConfig,
#[serde(default)]
pub o2c_process: OcpmProcessConfig,
#[serde(default)]
pub output: OcpmOutputConfig,
}
impl Default for OcpmConfig {
fn default() -> Self {
Self {
enabled: false,
generate_lifecycle_events: true,
include_object_relationships: true,
compute_variants: true,
max_variants: 0,
p2p_process: OcpmProcessConfig::default(),
o2c_process: OcpmProcessConfig::default(),
output: OcpmOutputConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OcpmProcessConfig {
#[serde(default = "default_rework_probability")]
pub rework_probability: f64,
#[serde(default = "default_skip_probability")]
pub skip_step_probability: f64,
#[serde(default = "default_out_of_order_probability")]
pub out_of_order_probability: f64,
}
fn default_rework_probability() -> f64 {
0.05
}
fn default_skip_probability() -> f64 {
0.02
}
fn default_out_of_order_probability() -> f64 {
0.03
}
impl Default for OcpmProcessConfig {
fn default() -> Self {
Self {
rework_probability: default_rework_probability(),
skip_step_probability: default_skip_probability(),
out_of_order_probability: default_out_of_order_probability(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OcpmOutputConfig {
#[serde(default = "default_true")]
pub ocel_json: bool,
#[serde(default)]
pub ocel_xml: bool,
#[serde(default)]
pub xes: bool,
#[serde(default = "default_true")]
pub xes_include_lifecycle: bool,
#[serde(default = "default_true")]
pub xes_include_resources: bool,
#[serde(default = "default_true")]
pub flattened_csv: bool,
#[serde(default = "default_true")]
pub event_object_csv: bool,
#[serde(default = "default_true")]
pub object_relationship_csv: bool,
#[serde(default = "default_true")]
pub variants_csv: bool,
#[serde(default)]
pub export_reference_models: bool,
}
impl Default for OcpmOutputConfig {
fn default() -> Self {
Self {
ocel_json: true,
ocel_xml: false,
xes: false,
xes_include_lifecycle: true,
xes_include_resources: true,
flattened_csv: true,
event_object_csv: true,
object_relationship_csv: true,
variants_csv: true,
export_reference_models: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditGenerationConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_true")]
pub generate_workpapers: bool,
#[serde(default)]
pub engagement_types: AuditEngagementTypesConfig,
#[serde(default)]
pub workpapers: WorkpaperConfig,
#[serde(default)]
pub team: AuditTeamConfig,
#[serde(default)]
pub review: ReviewWorkflowConfig,
#[serde(default)]
pub fsm: Option<AuditFsmConfig>,
}
impl Default for AuditGenerationConfig {
fn default() -> Self {
Self {
enabled: false,
generate_workpapers: true,
engagement_types: AuditEngagementTypesConfig::default(),
workpapers: WorkpaperConfig::default(),
team: AuditTeamConfig::default(),
review: ReviewWorkflowConfig::default(),
fsm: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditFsmConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_audit_fsm_blueprint")]
pub blueprint: String,
#[serde(default = "default_audit_fsm_overlay")]
pub overlay: String,
#[serde(default)]
pub depth: Option<String>,
#[serde(default)]
pub discriminators: std::collections::HashMap<String, Vec<String>>,
#[serde(default)]
pub event_trail: AuditEventTrailConfig,
#[serde(default)]
pub seed: Option<u64>,
}
impl Default for AuditFsmConfig {
fn default() -> Self {
Self {
enabled: false,
blueprint: default_audit_fsm_blueprint(),
overlay: default_audit_fsm_overlay(),
depth: None,
discriminators: std::collections::HashMap::new(),
event_trail: AuditEventTrailConfig::default(),
seed: None,
}
}
}
fn default_audit_fsm_blueprint() -> String {
"builtin:fsa".to_string()
}
fn default_audit_fsm_overlay() -> String {
"builtin:default".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEventTrailConfig {
#[serde(default = "default_true")]
pub flat_log: bool,
#[serde(default)]
pub ocel_projection: bool,
}
impl Default for AuditEventTrailConfig {
fn default() -> Self {
Self {
flat_log: true,
ocel_projection: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEngagementTypesConfig {
#[serde(default = "default_financial_audit_prob")]
pub financial_statement: f64,
#[serde(default = "default_sox_audit_prob")]
pub sox_icfr: f64,
#[serde(default = "default_integrated_audit_prob")]
pub integrated: f64,
#[serde(default = "default_review_prob")]
pub review: f64,
#[serde(default = "default_aup_prob")]
pub agreed_upon_procedures: f64,
}
fn default_financial_audit_prob() -> f64 {
0.40
}
fn default_sox_audit_prob() -> f64 {
0.20
}
fn default_integrated_audit_prob() -> f64 {
0.25
}
fn default_review_prob() -> f64 {
0.10
}
fn default_aup_prob() -> f64 {
0.05
}
impl Default for AuditEngagementTypesConfig {
fn default() -> Self {
Self {
financial_statement: default_financial_audit_prob(),
sox_icfr: default_sox_audit_prob(),
integrated: default_integrated_audit_prob(),
review: default_review_prob(),
agreed_upon_procedures: default_aup_prob(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkpaperConfig {
#[serde(default = "default_workpapers_per_phase")]
pub average_per_phase: usize,
#[serde(default = "default_true")]
pub include_isa_references: bool,
#[serde(default = "default_true")]
pub include_sample_details: bool,
#[serde(default = "default_true")]
pub include_cross_references: bool,
#[serde(default)]
pub sampling: SamplingConfig,
}
fn default_workpapers_per_phase() -> usize {
5
}
impl Default for WorkpaperConfig {
fn default() -> Self {
Self {
average_per_phase: default_workpapers_per_phase(),
include_isa_references: true,
include_sample_details: true,
include_cross_references: true,
sampling: SamplingConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SamplingConfig {
#[serde(default = "default_statistical_rate")]
pub statistical_rate: f64,
#[serde(default = "default_judgmental_rate")]
pub judgmental_rate: f64,
#[serde(default = "default_haphazard_rate")]
pub haphazard_rate: f64,
#[serde(default = "default_complete_examination_rate")]
pub complete_examination_rate: f64,
}
fn default_statistical_rate() -> f64 {
0.40
}
fn default_judgmental_rate() -> f64 {
0.30
}
fn default_haphazard_rate() -> f64 {
0.20
}
fn default_complete_examination_rate() -> f64 {
0.10
}
impl Default for SamplingConfig {
fn default() -> Self {
Self {
statistical_rate: default_statistical_rate(),
judgmental_rate: default_judgmental_rate(),
haphazard_rate: default_haphazard_rate(),
complete_examination_rate: default_complete_examination_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditTeamConfig {
#[serde(default = "default_min_team_size")]
pub min_team_size: usize,
#[serde(default = "default_max_team_size")]
pub max_team_size: usize,
#[serde(default = "default_specialist_probability")]
pub specialist_probability: f64,
}
fn default_min_team_size() -> usize {
3
}
fn default_max_team_size() -> usize {
8
}
fn default_specialist_probability() -> f64 {
0.30
}
impl Default for AuditTeamConfig {
fn default() -> Self {
Self {
min_team_size: default_min_team_size(),
max_team_size: default_max_team_size(),
specialist_probability: default_specialist_probability(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReviewWorkflowConfig {
#[serde(default = "default_review_delay_days")]
pub average_review_delay_days: u32,
#[serde(default = "default_rework_probability_review")]
pub rework_probability: f64,
#[serde(default = "default_true")]
pub require_partner_signoff: bool,
}
fn default_review_delay_days() -> u32 {
2
}
fn default_rework_probability_review() -> f64 {
0.15
}
impl Default for ReviewWorkflowConfig {
fn default() -> Self {
Self {
average_review_delay_days: default_review_delay_days(),
rework_probability: default_rework_probability_review(),
require_partner_signoff: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataQualitySchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub preset: DataQualityPreset,
#[serde(default)]
pub missing_values: MissingValuesSchemaConfig,
#[serde(default)]
pub typos: TypoSchemaConfig,
#[serde(default)]
pub format_variations: FormatVariationSchemaConfig,
#[serde(default)]
pub duplicates: DuplicateSchemaConfig,
#[serde(default)]
pub encoding_issues: EncodingIssueSchemaConfig,
#[serde(default)]
pub generate_labels: bool,
#[serde(default)]
pub sink_profiles: SinkQualityProfiles,
}
impl Default for DataQualitySchemaConfig {
fn default() -> Self {
Self {
enabled: false,
preset: DataQualityPreset::None,
missing_values: MissingValuesSchemaConfig::default(),
typos: TypoSchemaConfig::default(),
format_variations: FormatVariationSchemaConfig::default(),
duplicates: DuplicateSchemaConfig::default(),
encoding_issues: EncodingIssueSchemaConfig::default(),
generate_labels: true,
sink_profiles: SinkQualityProfiles::default(),
}
}
}
impl DataQualitySchemaConfig {
pub fn with_preset(preset: DataQualityPreset) -> Self {
let mut config = Self {
preset,
..Default::default()
};
config.apply_preset();
config
}
pub fn apply_preset(&mut self) {
if !self.preset.overrides_settings() {
return;
}
self.enabled = true;
self.missing_values.enabled = self.preset.missing_rate() > 0.0;
self.missing_values.rate = self.preset.missing_rate();
self.typos.enabled = self.preset.typo_rate() > 0.0;
self.typos.char_error_rate = self.preset.typo_rate();
self.duplicates.enabled = self.preset.duplicate_rate() > 0.0;
self.duplicates.exact_duplicate_ratio = self.preset.duplicate_rate() * 0.4;
self.duplicates.near_duplicate_ratio = self.preset.duplicate_rate() * 0.4;
self.duplicates.fuzzy_duplicate_ratio = self.preset.duplicate_rate() * 0.2;
self.format_variations.enabled = self.preset.format_variations_enabled();
self.encoding_issues.enabled = self.preset.encoding_issues_enabled();
self.encoding_issues.rate = self.preset.encoding_issue_rate();
if self.preset.ocr_errors_enabled() {
self.typos.type_weights.ocr_errors = 0.3;
}
}
pub fn effective_missing_rate(&self) -> f64 {
if self.preset.overrides_settings() {
self.preset.missing_rate()
} else {
self.missing_values.rate
}
}
pub fn effective_typo_rate(&self) -> f64 {
if self.preset.overrides_settings() {
self.preset.typo_rate()
} else {
self.typos.char_error_rate
}
}
pub fn effective_duplicate_rate(&self) -> f64 {
if self.preset.overrides_settings() {
self.preset.duplicate_rate()
} else {
self.duplicates.exact_duplicate_ratio
+ self.duplicates.near_duplicate_ratio
+ self.duplicates.fuzzy_duplicate_ratio
}
}
pub fn clean() -> Self {
Self::with_preset(DataQualityPreset::Clean)
}
pub fn noisy() -> Self {
Self::with_preset(DataQualityPreset::Noisy)
}
pub fn legacy() -> Self {
Self::with_preset(DataQualityPreset::Legacy)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum DataQualityPreset {
#[default]
None,
Minimal,
Normal,
High,
Custom,
Clean,
Noisy,
Legacy,
}
impl DataQualityPreset {
pub fn missing_rate(&self) -> f64 {
match self {
DataQualityPreset::None => 0.0,
DataQualityPreset::Minimal => 0.005,
DataQualityPreset::Normal => 0.02,
DataQualityPreset::High => 0.08,
DataQualityPreset::Custom => 0.01, DataQualityPreset::Clean => 0.001,
DataQualityPreset::Noisy => 0.05,
DataQualityPreset::Legacy => 0.10,
}
}
pub fn typo_rate(&self) -> f64 {
match self {
DataQualityPreset::None => 0.0,
DataQualityPreset::Minimal => 0.0005,
DataQualityPreset::Normal => 0.002,
DataQualityPreset::High => 0.01,
DataQualityPreset::Custom => 0.001, DataQualityPreset::Clean => 0.0005,
DataQualityPreset::Noisy => 0.02,
DataQualityPreset::Legacy => 0.05,
}
}
pub fn duplicate_rate(&self) -> f64 {
match self {
DataQualityPreset::None => 0.0,
DataQualityPreset::Minimal => 0.001,
DataQualityPreset::Normal => 0.005,
DataQualityPreset::High => 0.02,
DataQualityPreset::Custom => 0.0, DataQualityPreset::Clean => 0.0,
DataQualityPreset::Noisy => 0.01,
DataQualityPreset::Legacy => 0.03,
}
}
pub fn format_variations_enabled(&self) -> bool {
match self {
DataQualityPreset::None | DataQualityPreset::Clean => false,
DataQualityPreset::Minimal => true,
DataQualityPreset::Normal => true,
DataQualityPreset::High => true,
DataQualityPreset::Custom => true,
DataQualityPreset::Noisy => true,
DataQualityPreset::Legacy => true,
}
}
pub fn ocr_errors_enabled(&self) -> bool {
matches!(self, DataQualityPreset::Legacy | DataQualityPreset::High)
}
pub fn encoding_issues_enabled(&self) -> bool {
matches!(
self,
DataQualityPreset::Legacy | DataQualityPreset::High | DataQualityPreset::Noisy
)
}
pub fn encoding_issue_rate(&self) -> f64 {
match self {
DataQualityPreset::None | DataQualityPreset::Clean | DataQualityPreset::Minimal => 0.0,
DataQualityPreset::Normal => 0.002,
DataQualityPreset::High => 0.01,
DataQualityPreset::Custom => 0.0,
DataQualityPreset::Noisy => 0.005,
DataQualityPreset::Legacy => 0.02,
}
}
pub fn overrides_settings(&self) -> bool {
!matches!(self, DataQualityPreset::Custom | DataQualityPreset::None)
}
pub fn description(&self) -> &'static str {
match self {
DataQualityPreset::None => "No data quality issues (pristine data)",
DataQualityPreset::Minimal => "Very rare data quality issues",
DataQualityPreset::Normal => "Realistic enterprise data quality",
DataQualityPreset::High => "Messy data for stress testing",
DataQualityPreset::Custom => "Custom settings from configuration",
DataQualityPreset::Clean => "ML-ready clean data with minimal issues",
DataQualityPreset::Noisy => "Typical production data with moderate issues",
DataQualityPreset::Legacy => "Legacy/migrated data with heavy issues and OCR errors",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MissingValuesSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_missing_rate")]
pub rate: f64,
#[serde(default)]
pub strategy: MissingValueStrategy,
#[serde(default)]
pub field_rates: std::collections::HashMap<String, f64>,
#[serde(default)]
pub protected_fields: Vec<String>,
}
fn default_missing_rate() -> f64 {
0.01
}
impl Default for MissingValuesSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
rate: default_missing_rate(),
strategy: MissingValueStrategy::Mcar,
field_rates: std::collections::HashMap::new(),
protected_fields: vec![
"document_id".to_string(),
"company_code".to_string(),
"posting_date".to_string(),
],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum MissingValueStrategy {
#[default]
Mcar,
Mar,
Mnar,
Systematic,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypoSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_typo_rate")]
pub char_error_rate: f64,
#[serde(default)]
pub type_weights: TypoTypeWeights,
#[serde(default)]
pub protected_fields: Vec<String>,
}
fn default_typo_rate() -> f64 {
0.001
}
impl Default for TypoSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
char_error_rate: default_typo_rate(),
type_weights: TypoTypeWeights::default(),
protected_fields: vec![
"document_id".to_string(),
"gl_account".to_string(),
"company_code".to_string(),
],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypoTypeWeights {
#[serde(default = "default_substitution_weight")]
pub substitution: f64,
#[serde(default = "default_transposition_weight")]
pub transposition: f64,
#[serde(default = "default_insertion_weight")]
pub insertion: f64,
#[serde(default = "default_deletion_weight")]
pub deletion: f64,
#[serde(default = "default_ocr_weight")]
pub ocr_errors: f64,
#[serde(default = "default_homophone_weight")]
pub homophones: f64,
}
fn default_substitution_weight() -> f64 {
0.35
}
fn default_transposition_weight() -> f64 {
0.25
}
fn default_insertion_weight() -> f64 {
0.10
}
fn default_deletion_weight() -> f64 {
0.15
}
fn default_ocr_weight() -> f64 {
0.10
}
fn default_homophone_weight() -> f64 {
0.05
}
impl Default for TypoTypeWeights {
fn default() -> Self {
Self {
substitution: default_substitution_weight(),
transposition: default_transposition_weight(),
insertion: default_insertion_weight(),
deletion: default_deletion_weight(),
ocr_errors: default_ocr_weight(),
homophones: default_homophone_weight(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FormatVariationSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub dates: DateFormatVariationConfig,
#[serde(default)]
pub amounts: AmountFormatVariationConfig,
#[serde(default)]
pub identifiers: IdentifierFormatVariationConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DateFormatVariationConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_date_variation_rate")]
pub rate: f64,
#[serde(default = "default_true")]
pub iso_format: bool,
#[serde(default)]
pub us_format: bool,
#[serde(default)]
pub eu_format: bool,
#[serde(default)]
pub long_format: bool,
}
fn default_date_variation_rate() -> f64 {
0.05
}
impl Default for DateFormatVariationConfig {
fn default() -> Self {
Self {
enabled: false,
rate: default_date_variation_rate(),
iso_format: true,
us_format: false,
eu_format: false,
long_format: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AmountFormatVariationConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_amount_variation_rate")]
pub rate: f64,
#[serde(default)]
pub us_comma_format: bool,
#[serde(default)]
pub eu_format: bool,
#[serde(default)]
pub currency_prefix: bool,
#[serde(default)]
pub accounting_format: bool,
}
fn default_amount_variation_rate() -> f64 {
0.02
}
impl Default for AmountFormatVariationConfig {
fn default() -> Self {
Self {
enabled: false,
rate: default_amount_variation_rate(),
us_comma_format: false,
eu_format: false,
currency_prefix: false,
accounting_format: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IdentifierFormatVariationConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_identifier_variation_rate")]
pub rate: f64,
#[serde(default)]
pub case_variations: bool,
#[serde(default)]
pub padding_variations: bool,
#[serde(default)]
pub separator_variations: bool,
}
fn default_identifier_variation_rate() -> f64 {
0.02
}
impl Default for IdentifierFormatVariationConfig {
fn default() -> Self {
Self {
enabled: false,
rate: default_identifier_variation_rate(),
case_variations: false,
padding_variations: false,
separator_variations: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DuplicateSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_duplicate_rate")]
pub rate: f64,
#[serde(default = "default_exact_duplicate_ratio")]
pub exact_duplicate_ratio: f64,
#[serde(default = "default_near_duplicate_ratio")]
pub near_duplicate_ratio: f64,
#[serde(default = "default_fuzzy_duplicate_ratio")]
pub fuzzy_duplicate_ratio: f64,
#[serde(default = "default_max_date_offset")]
pub max_date_offset_days: u32,
#[serde(default = "default_max_amount_variance")]
pub max_amount_variance: f64,
}
fn default_duplicate_rate() -> f64 {
0.005
}
fn default_exact_duplicate_ratio() -> f64 {
0.4
}
fn default_near_duplicate_ratio() -> f64 {
0.35
}
fn default_fuzzy_duplicate_ratio() -> f64 {
0.25
}
fn default_max_date_offset() -> u32 {
3
}
fn default_max_amount_variance() -> f64 {
0.01
}
impl Default for DuplicateSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
rate: default_duplicate_rate(),
exact_duplicate_ratio: default_exact_duplicate_ratio(),
near_duplicate_ratio: default_near_duplicate_ratio(),
fuzzy_duplicate_ratio: default_fuzzy_duplicate_ratio(),
max_date_offset_days: default_max_date_offset(),
max_amount_variance: default_max_amount_variance(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EncodingIssueSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_encoding_rate")]
pub rate: f64,
#[serde(default)]
pub mojibake: bool,
#[serde(default)]
pub html_entities: bool,
#[serde(default)]
pub bom_issues: bool,
}
fn default_encoding_rate() -> f64 {
0.001
}
impl Default for EncodingIssueSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
rate: default_encoding_rate(),
mojibake: false,
html_entities: false,
bom_issues: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SinkQualityProfiles {
#[serde(default)]
pub csv: Option<SinkQualityOverride>,
#[serde(default)]
pub json: Option<SinkQualityOverride>,
#[serde(default)]
pub parquet: Option<SinkQualityOverride>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SinkQualityOverride {
pub enabled: Option<bool>,
pub missing_rate: Option<f64>,
pub typo_rate: Option<f64>,
pub format_variation_rate: Option<f64>,
pub duplicate_rate: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AccountingStandardsConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub framework: Option<AccountingFrameworkConfig>,
#[serde(default)]
pub revenue_recognition: RevenueRecognitionConfig,
#[serde(default)]
pub leases: LeaseAccountingConfig,
#[serde(default)]
pub fair_value: FairValueConfig,
#[serde(default)]
pub impairment: ImpairmentConfig,
#[serde(default)]
pub business_combinations: BusinessCombinationsConfig,
#[serde(default)]
pub expected_credit_loss: EclConfig,
#[serde(default)]
pub generate_differences: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum AccountingFrameworkConfig {
#[default]
UsGaap,
Ifrs,
DualReporting,
FrenchGaap,
GermanGaap,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RevenueRecognitionConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_true")]
pub generate_contracts: bool,
#[serde(default = "default_avg_obligations")]
pub avg_obligations_per_contract: f64,
#[serde(default = "default_variable_consideration_rate")]
pub variable_consideration_rate: f64,
#[serde(default = "default_over_time_rate")]
pub over_time_recognition_rate: f64,
#[serde(default = "default_contract_count")]
pub contract_count: usize,
}
fn default_avg_obligations() -> f64 {
2.0
}
fn default_variable_consideration_rate() -> f64 {
0.15
}
fn default_over_time_rate() -> f64 {
0.30
}
fn default_contract_count() -> usize {
100
}
impl Default for RevenueRecognitionConfig {
fn default() -> Self {
Self {
enabled: false,
generate_contracts: true,
avg_obligations_per_contract: default_avg_obligations(),
variable_consideration_rate: default_variable_consideration_rate(),
over_time_recognition_rate: default_over_time_rate(),
contract_count: default_contract_count(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LeaseAccountingConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_lease_count")]
pub lease_count: usize,
#[serde(default = "default_finance_lease_pct")]
pub finance_lease_percent: f64,
#[serde(default = "default_avg_lease_term")]
pub avg_lease_term_months: u32,
#[serde(default = "default_true")]
pub generate_amortization: bool,
#[serde(default = "default_real_estate_pct")]
pub real_estate_percent: f64,
}
fn default_lease_count() -> usize {
50
}
fn default_finance_lease_pct() -> f64 {
0.30
}
fn default_avg_lease_term() -> u32 {
60
}
fn default_real_estate_pct() -> f64 {
0.40
}
impl Default for LeaseAccountingConfig {
fn default() -> Self {
Self {
enabled: false,
lease_count: default_lease_count(),
finance_lease_percent: default_finance_lease_pct(),
avg_lease_term_months: default_avg_lease_term(),
generate_amortization: true,
real_estate_percent: default_real_estate_pct(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FairValueConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_fv_count")]
pub measurement_count: usize,
#[serde(default = "default_level1_pct")]
pub level1_percent: f64,
#[serde(default = "default_level2_pct")]
pub level2_percent: f64,
#[serde(default = "default_level3_pct")]
pub level3_percent: f64,
#[serde(default)]
pub include_sensitivity_analysis: bool,
}
fn default_fv_count() -> usize {
25
}
fn default_level1_pct() -> f64 {
0.40
}
fn default_level2_pct() -> f64 {
0.35
}
fn default_level3_pct() -> f64 {
0.25
}
impl Default for FairValueConfig {
fn default() -> Self {
Self {
enabled: false,
measurement_count: default_fv_count(),
level1_percent: default_level1_pct(),
level2_percent: default_level2_pct(),
level3_percent: default_level3_pct(),
include_sensitivity_analysis: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImpairmentConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_impairment_count")]
pub test_count: usize,
#[serde(default = "default_impairment_rate")]
pub impairment_rate: f64,
#[serde(default = "default_true")]
pub generate_projections: bool,
#[serde(default)]
pub include_goodwill: bool,
}
fn default_impairment_count() -> usize {
15
}
fn default_impairment_rate() -> f64 {
0.10
}
impl Default for ImpairmentConfig {
fn default() -> Self {
Self {
enabled: false,
test_count: default_impairment_count(),
impairment_rate: default_impairment_rate(),
generate_projections: true,
include_goodwill: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BusinessCombinationsConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_bc_acquisition_count")]
pub acquisition_count: usize,
}
fn default_bc_acquisition_count() -> usize {
2
}
impl Default for BusinessCombinationsConfig {
fn default() -> Self {
Self {
enabled: false,
acquisition_count: default_bc_acquisition_count(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EclConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_ecl_base_weight")]
pub base_scenario_weight: f64,
#[serde(default = "default_ecl_base_multiplier")]
pub base_scenario_multiplier: f64,
#[serde(default = "default_ecl_optimistic_weight")]
pub optimistic_scenario_weight: f64,
#[serde(default = "default_ecl_optimistic_multiplier")]
pub optimistic_scenario_multiplier: f64,
#[serde(default = "default_ecl_pessimistic_weight")]
pub pessimistic_scenario_weight: f64,
#[serde(default = "default_ecl_pessimistic_multiplier")]
pub pessimistic_scenario_multiplier: f64,
}
fn default_ecl_base_weight() -> f64 {
0.50
}
fn default_ecl_base_multiplier() -> f64 {
1.0
}
fn default_ecl_optimistic_weight() -> f64 {
0.30
}
fn default_ecl_optimistic_multiplier() -> f64 {
0.8
}
fn default_ecl_pessimistic_weight() -> f64 {
0.20
}
fn default_ecl_pessimistic_multiplier() -> f64 {
1.4
}
impl Default for EclConfig {
fn default() -> Self {
Self {
enabled: false,
base_scenario_weight: default_ecl_base_weight(),
base_scenario_multiplier: default_ecl_base_multiplier(),
optimistic_scenario_weight: default_ecl_optimistic_weight(),
optimistic_scenario_multiplier: default_ecl_optimistic_multiplier(),
pessimistic_scenario_weight: default_ecl_pessimistic_weight(),
pessimistic_scenario_multiplier: default_ecl_pessimistic_multiplier(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AuditStandardsConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub isa_compliance: IsaComplianceConfig,
#[serde(default)]
pub analytical_procedures: AnalyticalProceduresConfig,
#[serde(default)]
pub confirmations: ConfirmationsConfig,
#[serde(default)]
pub opinion: AuditOpinionConfig,
#[serde(default)]
pub generate_audit_trail: bool,
#[serde(default)]
pub sox: SoxComplianceConfig,
#[serde(default)]
pub pcaob: PcaobConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IsaComplianceConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_compliance_level")]
pub compliance_level: String,
#[serde(default = "default_true")]
pub generate_isa_mappings: bool,
#[serde(default = "default_true")]
pub generate_coverage_summary: bool,
#[serde(default)]
pub include_pcaob: bool,
#[serde(default = "default_audit_framework")]
pub framework: String,
}
fn default_compliance_level() -> String {
"standard".to_string()
}
fn default_audit_framework() -> String {
"isa".to_string()
}
impl Default for IsaComplianceConfig {
fn default() -> Self {
Self {
enabled: false,
compliance_level: default_compliance_level(),
generate_isa_mappings: true,
generate_coverage_summary: true,
include_pcaob: false,
framework: default_audit_framework(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalyticalProceduresConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_procedures_per_account")]
pub procedures_per_account: usize,
#[serde(default = "default_variance_probability")]
pub variance_probability: f64,
#[serde(default = "default_true")]
pub generate_investigations: bool,
#[serde(default = "default_true")]
pub include_ratio_analysis: bool,
}
fn default_procedures_per_account() -> usize {
3
}
fn default_variance_probability() -> f64 {
0.20
}
impl Default for AnalyticalProceduresConfig {
fn default() -> Self {
Self {
enabled: false,
procedures_per_account: default_procedures_per_account(),
variance_probability: default_variance_probability(),
generate_investigations: true,
include_ratio_analysis: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfirmationsConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_confirmation_count")]
pub confirmation_count: usize,
#[serde(default = "default_positive_response_rate")]
pub positive_response_rate: f64,
#[serde(default = "default_exception_rate_confirm")]
pub exception_rate: f64,
#[serde(default = "default_non_response_rate")]
pub non_response_rate: f64,
#[serde(default = "default_true")]
pub generate_alternative_procedures: bool,
}
fn default_confirmation_count() -> usize {
50
}
fn default_positive_response_rate() -> f64 {
0.85
}
fn default_exception_rate_confirm() -> f64 {
0.10
}
fn default_non_response_rate() -> f64 {
0.05
}
impl Default for ConfirmationsConfig {
fn default() -> Self {
Self {
enabled: false,
confirmation_count: default_confirmation_count(),
positive_response_rate: default_positive_response_rate(),
exception_rate: default_exception_rate_confirm(),
non_response_rate: default_non_response_rate(),
generate_alternative_procedures: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditOpinionConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_true")]
pub generate_kam: bool,
#[serde(default = "default_kam_count")]
pub average_kam_count: usize,
#[serde(default = "default_modified_opinion_rate")]
pub modified_opinion_rate: f64,
#[serde(default)]
pub include_emphasis_of_matter: bool,
#[serde(default = "default_true")]
pub include_going_concern: bool,
}
fn default_kam_count() -> usize {
3
}
fn default_modified_opinion_rate() -> f64 {
0.05
}
impl Default for AuditOpinionConfig {
fn default() -> Self {
Self {
enabled: false,
generate_kam: true,
average_kam_count: default_kam_count(),
modified_opinion_rate: default_modified_opinion_rate(),
include_emphasis_of_matter: false,
include_going_concern: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SoxComplianceConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_true")]
pub generate_302_certifications: bool,
#[serde(default = "default_true")]
pub generate_404_assessments: bool,
#[serde(default = "default_sox_materiality_threshold")]
pub materiality_threshold: f64,
#[serde(default = "default_material_weakness_rate")]
pub material_weakness_rate: f64,
#[serde(default = "default_significant_deficiency_rate")]
pub significant_deficiency_rate: f64,
}
fn default_material_weakness_rate() -> f64 {
0.02
}
fn default_significant_deficiency_rate() -> f64 {
0.08
}
impl Default for SoxComplianceConfig {
fn default() -> Self {
Self {
enabled: false,
generate_302_certifications: true,
generate_404_assessments: true,
materiality_threshold: default_sox_materiality_threshold(),
material_weakness_rate: default_material_weakness_rate(),
significant_deficiency_rate: default_significant_deficiency_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PcaobConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub is_pcaob_audit: bool,
#[serde(default = "default_true")]
pub generate_cam: bool,
#[serde(default)]
pub include_icfr_opinion: bool,
#[serde(default)]
pub generate_standard_mappings: bool,
}
impl Default for PcaobConfig {
fn default() -> Self {
Self {
enabled: false,
is_pcaob_audit: false,
generate_cam: true,
include_icfr_opinion: false,
generate_standard_mappings: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AdvancedDistributionConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub amounts: MixtureDistributionSchemaConfig,
#[serde(default)]
pub correlations: CorrelationSchemaConfig,
#[serde(default)]
pub conditional: Vec<ConditionalDistributionSchemaConfig>,
#[serde(default)]
pub regime_changes: RegimeChangeSchemaConfig,
#[serde(default)]
pub industry_profile: Option<IndustryProfileType>,
#[serde(default)]
pub validation: StatisticalValidationSchemaConfig,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum IndustryProfileType {
Retail,
Manufacturing,
FinancialServices,
Healthcare,
Technology,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MixtureDistributionSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_mixture_type")]
pub distribution_type: MixtureDistributionType,
#[serde(default)]
pub components: Vec<MixtureComponentConfig>,
#[serde(default = "default_min_amount")]
pub min_value: f64,
#[serde(default)]
pub max_value: Option<f64>,
#[serde(default = "default_decimal_places")]
pub decimal_places: u8,
}
fn default_mixture_type() -> MixtureDistributionType {
MixtureDistributionType::LogNormal
}
fn default_min_amount() -> f64 {
0.01
}
fn default_decimal_places() -> u8 {
2
}
impl Default for MixtureDistributionSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
distribution_type: MixtureDistributionType::LogNormal,
components: Vec::new(),
min_value: 0.01,
max_value: None,
decimal_places: 2,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum MixtureDistributionType {
Gaussian,
#[default]
LogNormal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MixtureComponentConfig {
pub weight: f64,
pub mu: f64,
pub sigma: f64,
#[serde(default)]
pub label: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorrelationSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub copula_type: CopulaSchemaType,
#[serde(default)]
pub fields: Vec<CorrelatedFieldConfig>,
#[serde(default)]
pub matrix: Vec<f64>,
#[serde(default)]
pub expected_correlations: Vec<ExpectedCorrelationConfig>,
}
impl Default for CorrelationSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
copula_type: CopulaSchemaType::Gaussian,
fields: Vec::new(),
matrix: Vec::new(),
expected_correlations: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum CopulaSchemaType {
#[default]
Gaussian,
Clayton,
Gumbel,
Frank,
StudentT,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorrelatedFieldConfig {
pub name: String,
#[serde(default)]
pub distribution: MarginalDistributionConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum MarginalDistributionConfig {
Normal {
mu: f64,
sigma: f64,
},
LogNormal {
mu: f64,
sigma: f64,
},
Uniform {
min: f64,
max: f64,
},
DiscreteUniform {
min: i32,
max: i32,
},
}
impl Default for MarginalDistributionConfig {
fn default() -> Self {
Self::Normal {
mu: 0.0,
sigma: 1.0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExpectedCorrelationConfig {
pub field1: String,
pub field2: String,
pub expected_r: f64,
#[serde(default = "default_correlation_tolerance")]
pub tolerance: f64,
}
fn default_correlation_tolerance() -> f64 {
0.10
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConditionalDistributionSchemaConfig {
pub output_field: String,
pub input_field: String,
#[serde(default)]
pub breakpoints: Vec<ConditionalBreakpointConfig>,
#[serde(default)]
pub default_distribution: ConditionalDistributionParamsConfig,
#[serde(default)]
pub min_value: Option<f64>,
#[serde(default)]
pub max_value: Option<f64>,
#[serde(default = "default_decimal_places")]
pub decimal_places: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConditionalBreakpointConfig {
pub threshold: f64,
pub distribution: ConditionalDistributionParamsConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ConditionalDistributionParamsConfig {
Fixed {
value: f64,
},
Normal {
mu: f64,
sigma: f64,
},
LogNormal {
mu: f64,
sigma: f64,
},
Uniform {
min: f64,
max: f64,
},
Beta {
alpha: f64,
beta: f64,
min: f64,
max: f64,
},
Discrete {
values: Vec<f64>,
weights: Vec<f64>,
},
}
impl Default for ConditionalDistributionParamsConfig {
fn default() -> Self {
Self::Normal {
mu: 0.0,
sigma: 1.0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct RegimeChangeSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub changes: Vec<RegimeChangeEventConfig>,
#[serde(default)]
pub economic_cycle: Option<EconomicCycleSchemaConfig>,
#[serde(default)]
pub parameter_drifts: Vec<ParameterDriftSchemaConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RegimeChangeEventConfig {
pub date: String,
pub change_type: RegimeChangeTypeConfig,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub effects: Vec<RegimeEffectConfig>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RegimeChangeTypeConfig {
Acquisition,
Divestiture,
PriceIncrease,
PriceDecrease,
ProductLaunch,
ProductDiscontinuation,
PolicyChange,
CompetitorEntry,
Custom,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RegimeEffectConfig {
pub field: String,
pub multiplier: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EconomicCycleSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_cycle_period")]
pub period_months: u32,
#[serde(default = "default_cycle_amplitude")]
pub amplitude: f64,
#[serde(default)]
pub phase_offset: u32,
#[serde(default)]
pub recessions: Vec<RecessionPeriodConfig>,
}
fn default_cycle_period() -> u32 {
48
}
fn default_cycle_amplitude() -> f64 {
0.15
}
impl Default for EconomicCycleSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
period_months: 48,
amplitude: 0.15,
phase_offset: 0,
recessions: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecessionPeriodConfig {
pub start_month: u32,
pub duration_months: u32,
#[serde(default = "default_recession_severity")]
pub severity: f64,
}
fn default_recession_severity() -> f64 {
0.20
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParameterDriftSchemaConfig {
pub parameter: String,
pub drift_type: ParameterDriftTypeConfig,
pub start_value: f64,
pub end_value: f64,
#[serde(default)]
pub start_period: u32,
#[serde(default)]
pub end_period: Option<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum ParameterDriftTypeConfig {
#[default]
Linear,
Exponential,
Logistic,
Step,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct StatisticalValidationSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub tests: Vec<StatisticalTestConfig>,
#[serde(default)]
pub reporting: ValidationReportingConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum StatisticalTestConfig {
BenfordFirstDigit {
#[serde(default = "default_benford_threshold")]
threshold_mad: f64,
#[serde(default = "default_benford_warning")]
warning_mad: f64,
},
DistributionFit {
target: TargetDistributionConfig,
#[serde(default = "default_ks_significance")]
ks_significance: f64,
#[serde(default)]
method: DistributionFitMethod,
},
CorrelationCheck {
expected_correlations: Vec<ExpectedCorrelationConfig>,
},
ChiSquared {
#[serde(default = "default_chi_squared_bins")]
bins: usize,
#[serde(default = "default_chi_squared_significance")]
significance: f64,
},
AndersonDarling {
target: TargetDistributionConfig,
#[serde(default = "default_ad_significance")]
significance: f64,
},
}
fn default_benford_threshold() -> f64 {
0.015
}
fn default_benford_warning() -> f64 {
0.010
}
fn default_ks_significance() -> f64 {
0.05
}
fn default_chi_squared_bins() -> usize {
10
}
fn default_chi_squared_significance() -> f64 {
0.05
}
fn default_ad_significance() -> f64 {
0.05
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum TargetDistributionConfig {
Normal,
#[default]
LogNormal,
Exponential,
Uniform,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum DistributionFitMethod {
#[default]
KolmogorovSmirnov,
AndersonDarling,
ChiSquared,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationReportingConfig {
#[serde(default)]
pub output_report: bool,
#[serde(default)]
pub format: ValidationReportFormat,
#[serde(default)]
pub fail_on_error: bool,
#[serde(default = "default_true")]
pub include_details: bool,
}
impl Default for ValidationReportingConfig {
fn default() -> Self {
Self {
output_report: false,
format: ValidationReportFormat::Json,
fail_on_error: false,
include_details: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum ValidationReportFormat {
#[default]
Json,
Yaml,
Html,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TemporalPatternsConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub business_days: BusinessDaySchemaConfig,
#[serde(default)]
pub calendars: CalendarSchemaConfig,
#[serde(default)]
pub period_end: PeriodEndSchemaConfig,
#[serde(default)]
pub processing_lags: ProcessingLagSchemaConfig,
#[serde(default)]
pub fiscal_calendar: FiscalCalendarSchemaConfig,
#[serde(default)]
pub intraday: IntraDaySchemaConfig,
#[serde(default)]
pub timezones: TimezoneSchemaConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BusinessDaySchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_half_day_policy")]
pub half_day_policy: String,
#[serde(default)]
pub settlement_rules: SettlementRulesSchemaConfig,
#[serde(default = "default_month_end_convention")]
pub month_end_convention: String,
#[serde(default)]
pub weekend_days: Option<Vec<String>>,
}
fn default_half_day_policy() -> String {
"half_day".to_string()
}
fn default_month_end_convention() -> String {
"modified_following".to_string()
}
impl Default for BusinessDaySchemaConfig {
fn default() -> Self {
Self {
enabled: true,
half_day_policy: "half_day".to_string(),
settlement_rules: SettlementRulesSchemaConfig::default(),
month_end_convention: "modified_following".to_string(),
weekend_days: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SettlementRulesSchemaConfig {
#[serde(default = "default_settlement_2")]
pub equity_days: i32,
#[serde(default = "default_settlement_1")]
pub government_bonds_days: i32,
#[serde(default = "default_settlement_2")]
pub fx_spot_days: i32,
#[serde(default = "default_settlement_2")]
pub corporate_bonds_days: i32,
#[serde(default = "default_wire_cutoff")]
pub wire_cutoff_time: String,
#[serde(default = "default_settlement_1")]
pub wire_international_days: i32,
#[serde(default = "default_settlement_1")]
pub ach_days: i32,
}
fn default_settlement_1() -> i32 {
1
}
fn default_settlement_2() -> i32 {
2
}
fn default_wire_cutoff() -> String {
"14:00".to_string()
}
impl Default for SettlementRulesSchemaConfig {
fn default() -> Self {
Self {
equity_days: 2,
government_bonds_days: 1,
fx_spot_days: 2,
corporate_bonds_days: 2,
wire_cutoff_time: "14:00".to_string(),
wire_international_days: 1,
ach_days: 1,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CalendarSchemaConfig {
#[serde(default)]
pub regions: Vec<String>,
#[serde(default)]
pub custom_holidays: Vec<CustomHolidaySchemaConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomHolidaySchemaConfig {
pub name: String,
pub month: u8,
pub day: u8,
#[serde(default = "default_holiday_multiplier")]
pub activity_multiplier: f64,
}
fn default_holiday_multiplier() -> f64 {
0.05
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PeriodEndSchemaConfig {
#[serde(default)]
pub model: Option<String>,
#[serde(default)]
pub month_end: Option<PeriodEndModelSchemaConfig>,
#[serde(default)]
pub quarter_end: Option<PeriodEndModelSchemaConfig>,
#[serde(default)]
pub year_end: Option<PeriodEndModelSchemaConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PeriodEndModelSchemaConfig {
#[serde(default)]
pub inherit_from: Option<String>,
#[serde(default)]
pub additional_multiplier: Option<f64>,
#[serde(default)]
pub start_day: Option<i32>,
#[serde(default)]
pub base_multiplier: Option<f64>,
#[serde(default)]
pub peak_multiplier: Option<f64>,
#[serde(default)]
pub decay_rate: Option<f64>,
#[serde(default)]
pub sustained_high_days: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessingLagSchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default)]
pub sales_order_lag: Option<LagDistributionSchemaConfig>,
#[serde(default)]
pub purchase_order_lag: Option<LagDistributionSchemaConfig>,
#[serde(default)]
pub goods_receipt_lag: Option<LagDistributionSchemaConfig>,
#[serde(default)]
pub invoice_receipt_lag: Option<LagDistributionSchemaConfig>,
#[serde(default)]
pub invoice_issue_lag: Option<LagDistributionSchemaConfig>,
#[serde(default)]
pub payment_lag: Option<LagDistributionSchemaConfig>,
#[serde(default)]
pub journal_entry_lag: Option<LagDistributionSchemaConfig>,
#[serde(default)]
pub cross_day_posting: Option<CrossDayPostingSchemaConfig>,
}
impl Default for ProcessingLagSchemaConfig {
fn default() -> Self {
Self {
enabled: true,
sales_order_lag: None,
purchase_order_lag: None,
goods_receipt_lag: None,
invoice_receipt_lag: None,
invoice_issue_lag: None,
payment_lag: None,
journal_entry_lag: None,
cross_day_posting: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LagDistributionSchemaConfig {
pub mu: f64,
pub sigma: f64,
#[serde(default)]
pub min_hours: Option<f64>,
#[serde(default)]
pub max_hours: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrossDayPostingSchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default)]
pub probability_by_hour: std::collections::HashMap<u8, f64>,
}
impl Default for CrossDayPostingSchemaConfig {
fn default() -> Self {
let mut probability_by_hour = std::collections::HashMap::new();
probability_by_hour.insert(17, 0.3);
probability_by_hour.insert(18, 0.6);
probability_by_hour.insert(19, 0.8);
probability_by_hour.insert(20, 0.9);
probability_by_hour.insert(21, 0.95);
probability_by_hour.insert(22, 0.99);
Self {
enabled: true,
probability_by_hour,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FiscalCalendarSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_fiscal_calendar_type")]
pub calendar_type: String,
#[serde(default)]
pub year_start_month: Option<u8>,
#[serde(default)]
pub year_start_day: Option<u8>,
#[serde(default)]
pub four_four_five: Option<FourFourFiveSchemaConfig>,
}
fn default_fiscal_calendar_type() -> String {
"calendar_year".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FourFourFiveSchemaConfig {
#[serde(default = "default_week_pattern")]
pub pattern: String,
#[serde(default = "default_anchor_type")]
pub anchor_type: String,
#[serde(default = "default_anchor_month")]
pub anchor_month: u8,
#[serde(default = "default_leap_week_placement")]
pub leap_week_placement: String,
}
fn default_week_pattern() -> String {
"four_four_five".to_string()
}
fn default_anchor_type() -> String {
"last_saturday".to_string()
}
fn default_anchor_month() -> u8 {
1 }
fn default_leap_week_placement() -> String {
"q4_period3".to_string()
}
impl Default for FourFourFiveSchemaConfig {
fn default() -> Self {
Self {
pattern: "four_four_five".to_string(),
anchor_type: "last_saturday".to_string(),
anchor_month: 1,
leap_week_placement: "q4_period3".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct IntraDaySchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub segments: Vec<IntraDaySegmentSchemaConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IntraDaySegmentSchemaConfig {
pub name: String,
pub start: String,
pub end: String,
#[serde(default = "default_multiplier")]
pub multiplier: f64,
#[serde(default = "default_posting_type")]
pub posting_type: String,
}
fn default_multiplier() -> f64 {
1.0
}
fn default_posting_type() -> String {
"both".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TimezoneSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_timezone")]
pub default_timezone: String,
#[serde(default = "default_consolidation_timezone")]
pub consolidation_timezone: String,
#[serde(default)]
pub entity_mappings: Vec<EntityTimezoneMapping>,
}
fn default_timezone() -> String {
"America/New_York".to_string()
}
fn default_consolidation_timezone() -> String {
"UTC".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntityTimezoneMapping {
pub pattern: String,
pub timezone: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VendorNetworkSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_vendor_tier_depth")]
pub depth: u8,
#[serde(default)]
pub tier1: TierCountSchemaConfig,
#[serde(default)]
pub tier2_per_parent: TierCountSchemaConfig,
#[serde(default)]
pub tier3_per_parent: TierCountSchemaConfig,
#[serde(default)]
pub clusters: VendorClusterSchemaConfig,
#[serde(default)]
pub dependencies: DependencySchemaConfig,
}
fn default_vendor_tier_depth() -> u8 {
3
}
impl Default for VendorNetworkSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
depth: 3,
tier1: TierCountSchemaConfig { min: 50, max: 100 },
tier2_per_parent: TierCountSchemaConfig { min: 4, max: 10 },
tier3_per_parent: TierCountSchemaConfig { min: 2, max: 5 },
clusters: VendorClusterSchemaConfig::default(),
dependencies: DependencySchemaConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TierCountSchemaConfig {
#[serde(default = "default_tier_min")]
pub min: usize,
#[serde(default = "default_tier_max")]
pub max: usize,
}
fn default_tier_min() -> usize {
5
}
fn default_tier_max() -> usize {
20
}
impl Default for TierCountSchemaConfig {
fn default() -> Self {
Self {
min: default_tier_min(),
max: default_tier_max(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VendorClusterSchemaConfig {
#[serde(default = "default_reliable_strategic")]
pub reliable_strategic: f64,
#[serde(default = "default_standard_operational")]
pub standard_operational: f64,
#[serde(default = "default_transactional")]
pub transactional: f64,
#[serde(default = "default_problematic")]
pub problematic: f64,
}
fn default_reliable_strategic() -> f64 {
0.20
}
fn default_standard_operational() -> f64 {
0.50
}
fn default_transactional() -> f64 {
0.25
}
fn default_problematic() -> f64 {
0.05
}
impl Default for VendorClusterSchemaConfig {
fn default() -> Self {
Self {
reliable_strategic: 0.20,
standard_operational: 0.50,
transactional: 0.25,
problematic: 0.05,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencySchemaConfig {
#[serde(default = "default_max_single_vendor")]
pub max_single_vendor_concentration: f64,
#[serde(default = "default_max_top5")]
pub top_5_concentration: f64,
#[serde(default = "default_single_source_percent")]
pub single_source_percent: f64,
}
fn default_max_single_vendor() -> f64 {
0.15
}
fn default_max_top5() -> f64 {
0.45
}
fn default_single_source_percent() -> f64 {
0.05
}
impl Default for DependencySchemaConfig {
fn default() -> Self {
Self {
max_single_vendor_concentration: 0.15,
top_5_concentration: 0.45,
single_source_percent: 0.05,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CustomerSegmentationSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub value_segments: ValueSegmentsSchemaConfig,
#[serde(default)]
pub lifecycle: LifecycleSchemaConfig,
#[serde(default)]
pub networks: CustomerNetworksSchemaConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValueSegmentsSchemaConfig {
#[serde(default)]
pub enterprise: SegmentDetailSchemaConfig,
#[serde(default)]
pub mid_market: SegmentDetailSchemaConfig,
#[serde(default)]
pub smb: SegmentDetailSchemaConfig,
#[serde(default)]
pub consumer: SegmentDetailSchemaConfig,
}
impl Default for ValueSegmentsSchemaConfig {
fn default() -> Self {
Self {
enterprise: SegmentDetailSchemaConfig {
revenue_share: 0.40,
customer_share: 0.05,
avg_order_value_range: "50000+".to_string(),
},
mid_market: SegmentDetailSchemaConfig {
revenue_share: 0.35,
customer_share: 0.20,
avg_order_value_range: "5000-50000".to_string(),
},
smb: SegmentDetailSchemaConfig {
revenue_share: 0.20,
customer_share: 0.50,
avg_order_value_range: "500-5000".to_string(),
},
consumer: SegmentDetailSchemaConfig {
revenue_share: 0.05,
customer_share: 0.25,
avg_order_value_range: "50-500".to_string(),
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SegmentDetailSchemaConfig {
#[serde(default)]
pub revenue_share: f64,
#[serde(default)]
pub customer_share: f64,
#[serde(default)]
pub avg_order_value_range: String,
}
impl Default for SegmentDetailSchemaConfig {
fn default() -> Self {
Self {
revenue_share: 0.25,
customer_share: 0.25,
avg_order_value_range: "1000-10000".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LifecycleSchemaConfig {
#[serde(default)]
pub prospect_rate: f64,
#[serde(default = "default_new_rate")]
pub new_rate: f64,
#[serde(default = "default_growth_rate")]
pub growth_rate: f64,
#[serde(default = "default_mature_rate")]
pub mature_rate: f64,
#[serde(default = "default_at_risk_rate")]
pub at_risk_rate: f64,
#[serde(default = "default_churned_rate")]
pub churned_rate: f64,
#[serde(default)]
pub won_back_rate: f64,
}
fn default_new_rate() -> f64 {
0.10
}
fn default_growth_rate() -> f64 {
0.15
}
fn default_mature_rate() -> f64 {
0.60
}
fn default_at_risk_rate() -> f64 {
0.10
}
fn default_churned_rate() -> f64 {
0.05
}
impl Default for LifecycleSchemaConfig {
fn default() -> Self {
Self {
prospect_rate: 0.0,
new_rate: 0.10,
growth_rate: 0.15,
mature_rate: 0.60,
at_risk_rate: 0.10,
churned_rate: 0.05,
won_back_rate: 0.0,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CustomerNetworksSchemaConfig {
#[serde(default)]
pub referrals: ReferralSchemaConfig,
#[serde(default)]
pub corporate_hierarchies: HierarchySchemaConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReferralSchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_referral_rate")]
pub referral_rate: f64,
}
fn default_referral_rate() -> f64 {
0.15
}
impl Default for ReferralSchemaConfig {
fn default() -> Self {
Self {
enabled: true,
referral_rate: 0.15,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HierarchySchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_hierarchy_rate")]
pub probability: f64,
}
fn default_hierarchy_rate() -> f64 {
0.30
}
impl Default for HierarchySchemaConfig {
fn default() -> Self {
Self {
enabled: true,
probability: 0.30,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RelationshipStrengthSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub calculation: StrengthCalculationSchemaConfig,
#[serde(default)]
pub thresholds: StrengthThresholdsSchemaConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrengthCalculationSchemaConfig {
#[serde(default = "default_volume_weight")]
pub transaction_volume_weight: f64,
#[serde(default = "default_count_weight")]
pub transaction_count_weight: f64,
#[serde(default = "default_duration_weight")]
pub relationship_duration_weight: f64,
#[serde(default = "default_recency_weight")]
pub recency_weight: f64,
#[serde(default = "default_mutual_weight")]
pub mutual_connections_weight: f64,
#[serde(default = "default_recency_half_life")]
pub recency_half_life_days: u32,
}
fn default_volume_weight() -> f64 {
0.30
}
fn default_count_weight() -> f64 {
0.25
}
fn default_duration_weight() -> f64 {
0.20
}
fn default_recency_weight() -> f64 {
0.15
}
fn default_mutual_weight() -> f64 {
0.10
}
fn default_recency_half_life() -> u32 {
90
}
impl Default for StrengthCalculationSchemaConfig {
fn default() -> Self {
Self {
transaction_volume_weight: 0.30,
transaction_count_weight: 0.25,
relationship_duration_weight: 0.20,
recency_weight: 0.15,
mutual_connections_weight: 0.10,
recency_half_life_days: 90,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrengthThresholdsSchemaConfig {
#[serde(default = "default_strong_threshold")]
pub strong: f64,
#[serde(default = "default_moderate_threshold")]
pub moderate: f64,
#[serde(default = "default_weak_threshold")]
pub weak: f64,
}
fn default_strong_threshold() -> f64 {
0.7
}
fn default_moderate_threshold() -> f64 {
0.4
}
fn default_weak_threshold() -> f64 {
0.1
}
impl Default for StrengthThresholdsSchemaConfig {
fn default() -> Self {
Self {
strong: 0.7,
moderate: 0.4,
weak: 0.1,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrossProcessLinksSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_true")]
pub inventory_p2p_o2c: bool,
#[serde(default = "default_true")]
pub payment_bank_reconciliation: bool,
#[serde(default = "default_true")]
pub intercompany_bilateral: bool,
#[serde(default = "default_inventory_link_rate")]
pub inventory_link_rate: f64,
}
fn default_inventory_link_rate() -> f64 {
0.30
}
impl Default for CrossProcessLinksSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
inventory_p2p_o2c: true,
payment_bank_reconciliation: true,
intercompany_bilateral: true,
inventory_link_rate: 0.30,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct OrganizationalEventsSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub effect_blending: EffectBlendingModeConfig,
#[serde(default)]
pub events: Vec<OrganizationalEventSchemaConfig>,
#[serde(default)]
pub process_evolution: Vec<ProcessEvolutionSchemaConfig>,
#[serde(default)]
pub technology_transitions: Vec<TechnologyTransitionSchemaConfig>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum EffectBlendingModeConfig {
#[default]
Multiplicative,
Additive,
Maximum,
Minimum,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrganizationalEventSchemaConfig {
pub id: String,
pub event_type: OrganizationalEventTypeSchemaConfig,
pub effective_date: String,
#[serde(default = "default_org_transition_months")]
pub transition_months: u32,
#[serde(default)]
pub description: Option<String>,
}
fn default_org_transition_months() -> u32 {
6
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum OrganizationalEventTypeSchemaConfig {
Acquisition {
acquired_entity: String,
#[serde(default = "default_acquisition_volume")]
volume_increase: f64,
#[serde(default = "default_acquisition_error")]
integration_error_rate: f64,
#[serde(default = "default_parallel_days")]
parallel_posting_days: u32,
},
Divestiture {
divested_entity: String,
#[serde(default = "default_divestiture_volume")]
volume_reduction: f64,
#[serde(default = "default_true_val")]
remove_entity: bool,
},
Reorganization {
#[serde(default)]
cost_center_remapping: std::collections::HashMap<String, String>,
#[serde(default = "default_reorg_error")]
transition_error_rate: f64,
},
LeadershipChange {
role: String,
#[serde(default)]
policy_changes: Vec<String>,
},
WorkforceReduction {
#[serde(default = "default_workforce_reduction")]
reduction_percent: f64,
#[serde(default = "default_workforce_error")]
error_rate_increase: f64,
},
Merger {
merged_entity: String,
#[serde(default = "default_merger_volume")]
volume_increase: f64,
},
}
fn default_acquisition_volume() -> f64 {
1.35
}
fn default_acquisition_error() -> f64 {
0.05
}
fn default_parallel_days() -> u32 {
30
}
fn default_divestiture_volume() -> f64 {
0.70
}
fn default_true_val() -> bool {
true
}
fn default_reorg_error() -> f64 {
0.04
}
fn default_workforce_reduction() -> f64 {
0.10
}
fn default_workforce_error() -> f64 {
0.05
}
fn default_merger_volume() -> f64 {
1.80
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessEvolutionSchemaConfig {
pub id: String,
pub event_type: ProcessEvolutionTypeSchemaConfig,
pub effective_date: String,
#[serde(default)]
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ProcessEvolutionTypeSchemaConfig {
ProcessAutomation {
process_name: String,
#[serde(default = "default_manual_before")]
manual_rate_before: f64,
#[serde(default = "default_manual_after")]
manual_rate_after: f64,
},
ApprovalWorkflowChange {
description: String,
},
ControlEnhancement {
control_id: String,
#[serde(default = "default_error_reduction")]
error_reduction: f64,
},
}
fn default_manual_before() -> f64 {
0.80
}
fn default_manual_after() -> f64 {
0.15
}
fn default_error_reduction() -> f64 {
0.02
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TechnologyTransitionSchemaConfig {
pub id: String,
pub event_type: TechnologyTransitionTypeSchemaConfig,
#[serde(default)]
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum TechnologyTransitionTypeSchemaConfig {
ErpMigration {
source_system: String,
target_system: String,
cutover_date: String,
stabilization_end: String,
#[serde(default = "default_erp_duplicate_rate")]
duplicate_rate: f64,
#[serde(default = "default_format_mismatch")]
format_mismatch_rate: f64,
},
ModuleImplementation {
module_name: String,
go_live_date: String,
},
}
fn default_erp_duplicate_rate() -> f64 {
0.02
}
fn default_format_mismatch() -> f64 {
0.03
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct BehavioralDriftSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub vendor_behavior: VendorBehaviorSchemaConfig,
#[serde(default)]
pub customer_behavior: CustomerBehaviorSchemaConfig,
#[serde(default)]
pub employee_behavior: EmployeeBehaviorSchemaConfig,
#[serde(default)]
pub collective: CollectiveBehaviorSchemaConfig,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct VendorBehaviorSchemaConfig {
#[serde(default)]
pub payment_terms_drift: PaymentTermsDriftSchemaConfig,
#[serde(default)]
pub quality_drift: QualityDriftSchemaConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentTermsDriftSchemaConfig {
#[serde(default = "default_extension_rate")]
pub extension_rate_per_year: f64,
#[serde(default = "default_economic_sensitivity")]
pub economic_sensitivity: f64,
}
fn default_extension_rate() -> f64 {
2.5
}
fn default_economic_sensitivity() -> f64 {
1.0
}
impl Default for PaymentTermsDriftSchemaConfig {
fn default() -> Self {
Self {
extension_rate_per_year: 2.5,
economic_sensitivity: 1.0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityDriftSchemaConfig {
#[serde(default = "default_improvement_rate")]
pub new_vendor_improvement_rate: f64,
#[serde(default = "default_decline_rate")]
pub complacency_decline_rate: f64,
}
fn default_improvement_rate() -> f64 {
0.02
}
fn default_decline_rate() -> f64 {
0.01
}
impl Default for QualityDriftSchemaConfig {
fn default() -> Self {
Self {
new_vendor_improvement_rate: 0.02,
complacency_decline_rate: 0.01,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CustomerBehaviorSchemaConfig {
#[serde(default)]
pub payment_drift: CustomerPaymentDriftSchemaConfig,
#[serde(default)]
pub order_drift: OrderDriftSchemaConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomerPaymentDriftSchemaConfig {
#[serde(default = "default_downturn_extension")]
pub downturn_days_extension: (u32, u32),
#[serde(default = "default_bad_debt_increase")]
pub downturn_bad_debt_increase: f64,
}
fn default_downturn_extension() -> (u32, u32) {
(5, 15)
}
fn default_bad_debt_increase() -> f64 {
0.02
}
impl Default for CustomerPaymentDriftSchemaConfig {
fn default() -> Self {
Self {
downturn_days_extension: (5, 15),
downturn_bad_debt_increase: 0.02,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrderDriftSchemaConfig {
#[serde(default = "default_digital_shift")]
pub digital_shift_rate: f64,
}
fn default_digital_shift() -> f64 {
0.05
}
impl Default for OrderDriftSchemaConfig {
fn default() -> Self {
Self {
digital_shift_rate: 0.05,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EmployeeBehaviorSchemaConfig {
#[serde(default)]
pub approval_drift: ApprovalDriftSchemaConfig,
#[serde(default)]
pub error_drift: ErrorDriftSchemaConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApprovalDriftSchemaConfig {
#[serde(default = "default_eom_intensity")]
pub eom_intensity_increase_per_year: f64,
#[serde(default = "default_rubber_stamp")]
pub rubber_stamp_volume_threshold: u32,
}
fn default_eom_intensity() -> f64 {
0.05
}
fn default_rubber_stamp() -> u32 {
50
}
impl Default for ApprovalDriftSchemaConfig {
fn default() -> Self {
Self {
eom_intensity_increase_per_year: 0.05,
rubber_stamp_volume_threshold: 50,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorDriftSchemaConfig {
#[serde(default = "default_new_error")]
pub new_employee_error_rate: f64,
#[serde(default = "default_learning_months")]
pub learning_curve_months: u32,
}
fn default_new_error() -> f64 {
0.08
}
fn default_learning_months() -> u32 {
6
}
impl Default for ErrorDriftSchemaConfig {
fn default() -> Self {
Self {
new_employee_error_rate: 0.08,
learning_curve_months: 6,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CollectiveBehaviorSchemaConfig {
#[serde(default)]
pub automation_adoption: AutomationAdoptionSchemaConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AutomationAdoptionSchemaConfig {
#[serde(default)]
pub s_curve_enabled: bool,
#[serde(default = "default_midpoint")]
pub adoption_midpoint_months: u32,
#[serde(default = "default_steepness")]
pub steepness: f64,
}
fn default_midpoint() -> u32 {
24
}
fn default_steepness() -> f64 {
0.15
}
impl Default for AutomationAdoptionSchemaConfig {
fn default() -> Self {
Self {
s_curve_enabled: false,
adoption_midpoint_months: 24,
steepness: 0.15,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MarketDriftSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub economic_cycle: MarketEconomicCycleSchemaConfig,
#[serde(default)]
pub industry_cycles: std::collections::HashMap<String, IndustryCycleSchemaConfig>,
#[serde(default)]
pub commodities: CommoditiesSchemaConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketEconomicCycleSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub cycle_type: CycleTypeSchemaConfig,
#[serde(default = "default_market_cycle_period")]
pub period_months: u32,
#[serde(default = "default_market_amplitude")]
pub amplitude: f64,
#[serde(default)]
pub recession: RecessionSchemaConfig,
}
fn default_market_cycle_period() -> u32 {
48
}
fn default_market_amplitude() -> f64 {
0.15
}
impl Default for MarketEconomicCycleSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
cycle_type: CycleTypeSchemaConfig::Sinusoidal,
period_months: 48,
amplitude: 0.15,
recession: RecessionSchemaConfig::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum CycleTypeSchemaConfig {
#[default]
Sinusoidal,
Asymmetric,
MeanReverting,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecessionSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_recession_prob")]
pub probability_per_year: f64,
#[serde(default)]
pub severity: RecessionSeveritySchemaConfig,
#[serde(default)]
pub recession_periods: Vec<RecessionPeriodSchemaConfig>,
}
fn default_recession_prob() -> f64 {
0.10
}
impl Default for RecessionSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
probability_per_year: 0.10,
severity: RecessionSeveritySchemaConfig::Moderate,
recession_periods: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum RecessionSeveritySchemaConfig {
Mild,
#[default]
Moderate,
Severe,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecessionPeriodSchemaConfig {
pub start_month: u32,
pub duration_months: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndustryCycleSchemaConfig {
#[serde(default = "default_industry_period")]
pub period_months: u32,
#[serde(default = "default_industry_amp")]
pub amplitude: f64,
}
fn default_industry_period() -> u32 {
36
}
fn default_industry_amp() -> f64 {
0.20
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CommoditiesSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub items: Vec<CommodityItemSchemaConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommodityItemSchemaConfig {
pub name: String,
#[serde(default = "default_volatility")]
pub volatility: f64,
#[serde(default)]
pub cogs_pass_through: f64,
#[serde(default)]
pub overhead_pass_through: f64,
}
fn default_volatility() -> f64 {
0.20
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DriftLabelingSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub statistical: StatisticalDriftLabelingSchemaConfig,
#[serde(default)]
pub categorical: CategoricalDriftLabelingSchemaConfig,
#[serde(default)]
pub temporal: TemporalDriftLabelingSchemaConfig,
#[serde(default)]
pub regulatory_calendar_preset: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatisticalDriftLabelingSchemaConfig {
#[serde(default = "default_true_val")]
pub enabled: bool,
#[serde(default = "default_min_magnitude")]
pub min_magnitude_threshold: f64,
}
fn default_min_magnitude() -> f64 {
0.05
}
impl Default for StatisticalDriftLabelingSchemaConfig {
fn default() -> Self {
Self {
enabled: true,
min_magnitude_threshold: 0.05,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CategoricalDriftLabelingSchemaConfig {
#[serde(default = "default_true_val")]
pub enabled: bool,
}
impl Default for CategoricalDriftLabelingSchemaConfig {
fn default() -> Self {
Self { enabled: true }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemporalDriftLabelingSchemaConfig {
#[serde(default = "default_true_val")]
pub enabled: bool,
}
impl Default for TemporalDriftLabelingSchemaConfig {
fn default() -> Self {
Self { enabled: true }
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EnhancedAnomalyConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub rates: AnomalyRateConfig,
#[serde(default)]
pub multi_stage_schemes: MultiStageSchemeConfig,
#[serde(default)]
pub correlated_injection: CorrelatedInjectionConfig,
#[serde(default)]
pub near_miss: NearMissConfig,
#[serde(default)]
pub difficulty_classification: DifficultyClassificationConfig,
#[serde(default)]
pub context_aware: ContextAwareConfig,
#[serde(default)]
pub labeling: EnhancedLabelingConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnomalyRateConfig {
#[serde(default = "default_total_anomaly_rate")]
pub total_rate: f64,
#[serde(default = "default_fraud_anomaly_rate")]
pub fraud_rate: f64,
#[serde(default = "default_error_anomaly_rate")]
pub error_rate: f64,
#[serde(default = "default_process_anomaly_rate")]
pub process_rate: f64,
}
fn default_total_anomaly_rate() -> f64 {
0.03
}
fn default_fraud_anomaly_rate() -> f64 {
0.01
}
fn default_error_anomaly_rate() -> f64 {
0.015
}
fn default_process_anomaly_rate() -> f64 {
0.005
}
impl Default for AnomalyRateConfig {
fn default() -> Self {
Self {
total_rate: default_total_anomaly_rate(),
fraud_rate: default_fraud_anomaly_rate(),
error_rate: default_error_anomaly_rate(),
process_rate: default_process_anomaly_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MultiStageSchemeConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub embezzlement: EmbezzlementSchemeConfig,
#[serde(default)]
pub revenue_manipulation: RevenueManipulationSchemeConfig,
#[serde(default)]
pub kickback: KickbackSchemeConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbezzlementSchemeConfig {
#[serde(default = "default_embezzlement_probability")]
pub probability: f64,
#[serde(default)]
pub testing_stage: SchemeStageConfig,
#[serde(default)]
pub escalation_stage: SchemeStageConfig,
#[serde(default)]
pub acceleration_stage: SchemeStageConfig,
#[serde(default)]
pub desperation_stage: SchemeStageConfig,
}
fn default_embezzlement_probability() -> f64 {
0.02
}
impl Default for EmbezzlementSchemeConfig {
fn default() -> Self {
Self {
probability: default_embezzlement_probability(),
testing_stage: SchemeStageConfig {
duration_months: 2,
amount_min: 100.0,
amount_max: 500.0,
transaction_count_min: 2,
transaction_count_max: 5,
difficulty: "hard".to_string(),
},
escalation_stage: SchemeStageConfig {
duration_months: 6,
amount_min: 500.0,
amount_max: 2000.0,
transaction_count_min: 3,
transaction_count_max: 8,
difficulty: "moderate".to_string(),
},
acceleration_stage: SchemeStageConfig {
duration_months: 3,
amount_min: 2000.0,
amount_max: 10000.0,
transaction_count_min: 5,
transaction_count_max: 12,
difficulty: "easy".to_string(),
},
desperation_stage: SchemeStageConfig {
duration_months: 1,
amount_min: 10000.0,
amount_max: 50000.0,
transaction_count_min: 3,
transaction_count_max: 6,
difficulty: "trivial".to_string(),
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RevenueManipulationSchemeConfig {
#[serde(default = "default_revenue_manipulation_probability")]
pub probability: f64,
#[serde(default = "default_early_recognition_target")]
pub early_recognition_target: f64,
#[serde(default = "default_expense_deferral_target")]
pub expense_deferral_target: f64,
#[serde(default = "default_reserve_release_target")]
pub reserve_release_target: f64,
#[serde(default = "default_channel_stuffing_target")]
pub channel_stuffing_target: f64,
}
fn default_revenue_manipulation_probability() -> f64 {
0.01
}
fn default_early_recognition_target() -> f64 {
0.02
}
fn default_expense_deferral_target() -> f64 {
0.03
}
fn default_reserve_release_target() -> f64 {
0.02
}
fn default_channel_stuffing_target() -> f64 {
0.05
}
impl Default for RevenueManipulationSchemeConfig {
fn default() -> Self {
Self {
probability: default_revenue_manipulation_probability(),
early_recognition_target: default_early_recognition_target(),
expense_deferral_target: default_expense_deferral_target(),
reserve_release_target: default_reserve_release_target(),
channel_stuffing_target: default_channel_stuffing_target(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KickbackSchemeConfig {
#[serde(default = "default_kickback_probability")]
pub probability: f64,
#[serde(default = "default_kickback_inflation_min")]
pub inflation_min: f64,
#[serde(default = "default_kickback_inflation_max")]
pub inflation_max: f64,
#[serde(default = "default_kickback_percent")]
pub kickback_percent: f64,
#[serde(default = "default_kickback_setup_months")]
pub setup_months: u32,
#[serde(default = "default_kickback_operation_months")]
pub operation_months: u32,
}
fn default_kickback_probability() -> f64 {
0.01
}
fn default_kickback_inflation_min() -> f64 {
0.10
}
fn default_kickback_inflation_max() -> f64 {
0.25
}
fn default_kickback_percent() -> f64 {
0.50
}
fn default_kickback_setup_months() -> u32 {
3
}
fn default_kickback_operation_months() -> u32 {
12
}
impl Default for KickbackSchemeConfig {
fn default() -> Self {
Self {
probability: default_kickback_probability(),
inflation_min: default_kickback_inflation_min(),
inflation_max: default_kickback_inflation_max(),
kickback_percent: default_kickback_percent(),
setup_months: default_kickback_setup_months(),
operation_months: default_kickback_operation_months(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemeStageConfig {
pub duration_months: u32,
pub amount_min: f64,
pub amount_max: f64,
pub transaction_count_min: u32,
pub transaction_count_max: u32,
pub difficulty: String,
}
impl Default for SchemeStageConfig {
fn default() -> Self {
Self {
duration_months: 3,
amount_min: 100.0,
amount_max: 1000.0,
transaction_count_min: 2,
transaction_count_max: 10,
difficulty: "moderate".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorrelatedInjectionConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_true_val")]
pub fraud_concealment: bool,
#[serde(default = "default_true_val")]
pub error_cascade: bool,
#[serde(default = "default_true_val")]
pub temporal_clustering: bool,
#[serde(default)]
pub temporal_clustering_config: TemporalClusteringConfig,
#[serde(default)]
pub co_occurrence_patterns: Vec<CoOccurrencePatternConfig>,
}
impl Default for CorrelatedInjectionConfig {
fn default() -> Self {
Self {
enabled: false,
fraud_concealment: true,
error_cascade: true,
temporal_clustering: true,
temporal_clustering_config: TemporalClusteringConfig::default(),
co_occurrence_patterns: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemporalClusteringConfig {
#[serde(default = "default_period_end_multiplier")]
pub period_end_multiplier: f64,
#[serde(default = "default_period_end_days")]
pub period_end_days: u32,
#[serde(default = "default_quarter_end_multiplier")]
pub quarter_end_multiplier: f64,
#[serde(default = "default_year_end_multiplier")]
pub year_end_multiplier: f64,
}
fn default_period_end_multiplier() -> f64 {
2.5
}
fn default_period_end_days() -> u32 {
5
}
fn default_quarter_end_multiplier() -> f64 {
1.5
}
fn default_year_end_multiplier() -> f64 {
2.0
}
impl Default for TemporalClusteringConfig {
fn default() -> Self {
Self {
period_end_multiplier: default_period_end_multiplier(),
period_end_days: default_period_end_days(),
quarter_end_multiplier: default_quarter_end_multiplier(),
year_end_multiplier: default_year_end_multiplier(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoOccurrencePatternConfig {
pub name: String,
pub primary_type: String,
pub correlated: Vec<CorrelatedAnomalyConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorrelatedAnomalyConfig {
pub anomaly_type: String,
pub probability: f64,
pub lag_days_min: i32,
pub lag_days_max: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NearMissConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_near_miss_proportion")]
pub proportion: f64,
#[serde(default = "default_true_val")]
pub near_duplicate: bool,
#[serde(default)]
pub near_duplicate_days: NearDuplicateDaysConfig,
#[serde(default = "default_true_val")]
pub threshold_proximity: bool,
#[serde(default)]
pub threshold_proximity_range: ThresholdProximityRangeConfig,
#[serde(default = "default_true_val")]
pub unusual_legitimate: bool,
#[serde(default = "default_unusual_legitimate_types")]
pub unusual_legitimate_types: Vec<String>,
#[serde(default = "default_true_val")]
pub corrected_errors: bool,
#[serde(default)]
pub corrected_error_lag: CorrectedErrorLagConfig,
}
fn default_near_miss_proportion() -> f64 {
0.30
}
fn default_unusual_legitimate_types() -> Vec<String> {
vec![
"year_end_bonus".to_string(),
"contract_prepayment".to_string(),
"insurance_claim".to_string(),
"settlement_payment".to_string(),
]
}
impl Default for NearMissConfig {
fn default() -> Self {
Self {
enabled: false,
proportion: default_near_miss_proportion(),
near_duplicate: true,
near_duplicate_days: NearDuplicateDaysConfig::default(),
threshold_proximity: true,
threshold_proximity_range: ThresholdProximityRangeConfig::default(),
unusual_legitimate: true,
unusual_legitimate_types: default_unusual_legitimate_types(),
corrected_errors: true,
corrected_error_lag: CorrectedErrorLagConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NearDuplicateDaysConfig {
#[serde(default = "default_near_duplicate_min")]
pub min: u32,
#[serde(default = "default_near_duplicate_max")]
pub max: u32,
}
fn default_near_duplicate_min() -> u32 {
1
}
fn default_near_duplicate_max() -> u32 {
3
}
impl Default for NearDuplicateDaysConfig {
fn default() -> Self {
Self {
min: default_near_duplicate_min(),
max: default_near_duplicate_max(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThresholdProximityRangeConfig {
#[serde(default = "default_threshold_proximity_min")]
pub min: f64,
#[serde(default = "default_threshold_proximity_max")]
pub max: f64,
}
fn default_threshold_proximity_min() -> f64 {
0.90
}
fn default_threshold_proximity_max() -> f64 {
0.99
}
impl Default for ThresholdProximityRangeConfig {
fn default() -> Self {
Self {
min: default_threshold_proximity_min(),
max: default_threshold_proximity_max(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorrectedErrorLagConfig {
#[serde(default = "default_corrected_error_lag_min")]
pub min: u32,
#[serde(default = "default_corrected_error_lag_max")]
pub max: u32,
}
fn default_corrected_error_lag_min() -> u32 {
1
}
fn default_corrected_error_lag_max() -> u32 {
5
}
impl Default for CorrectedErrorLagConfig {
fn default() -> Self {
Self {
min: default_corrected_error_lag_min(),
max: default_corrected_error_lag_max(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DifficultyClassificationConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub target_distribution: DifficultyDistributionConfig,
}
impl Default for DifficultyClassificationConfig {
fn default() -> Self {
Self {
enabled: true,
target_distribution: DifficultyDistributionConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DifficultyDistributionConfig {
#[serde(default = "default_difficulty_trivial")]
pub trivial: f64,
#[serde(default = "default_difficulty_easy")]
pub easy: f64,
#[serde(default = "default_difficulty_moderate")]
pub moderate: f64,
#[serde(default = "default_difficulty_hard")]
pub hard: f64,
#[serde(default = "default_difficulty_expert")]
pub expert: f64,
}
fn default_difficulty_trivial() -> f64 {
0.15
}
fn default_difficulty_easy() -> f64 {
0.25
}
fn default_difficulty_moderate() -> f64 {
0.30
}
fn default_difficulty_hard() -> f64 {
0.20
}
fn default_difficulty_expert() -> f64 {
0.10
}
impl Default for DifficultyDistributionConfig {
fn default() -> Self {
Self {
trivial: default_difficulty_trivial(),
easy: default_difficulty_easy(),
moderate: default_difficulty_moderate(),
hard: default_difficulty_hard(),
expert: default_difficulty_expert(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ContextAwareConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub vendor_rules: VendorAnomalyRulesConfig,
#[serde(default)]
pub employee_rules: EmployeeAnomalyRulesConfig,
#[serde(default)]
pub account_rules: AccountAnomalyRulesConfig,
#[serde(default)]
pub behavioral_baseline: BehavioralBaselineConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VendorAnomalyRulesConfig {
#[serde(default = "default_new_vendor_multiplier")]
pub new_vendor_error_multiplier: f64,
#[serde(default = "default_new_vendor_threshold")]
pub new_vendor_threshold_days: u32,
#[serde(default = "default_international_multiplier")]
pub international_error_multiplier: f64,
#[serde(default = "default_strategic_vendor_types")]
pub strategic_vendor_anomaly_types: Vec<String>,
}
fn default_new_vendor_multiplier() -> f64 {
2.5
}
fn default_new_vendor_threshold() -> u32 {
90
}
fn default_international_multiplier() -> f64 {
1.5
}
fn default_strategic_vendor_types() -> Vec<String> {
vec![
"pricing_dispute".to_string(),
"contract_violation".to_string(),
]
}
impl Default for VendorAnomalyRulesConfig {
fn default() -> Self {
Self {
new_vendor_error_multiplier: default_new_vendor_multiplier(),
new_vendor_threshold_days: default_new_vendor_threshold(),
international_error_multiplier: default_international_multiplier(),
strategic_vendor_anomaly_types: default_strategic_vendor_types(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmployeeAnomalyRulesConfig {
#[serde(default = "default_new_employee_rate")]
pub new_employee_error_rate: f64,
#[serde(default = "default_new_employee_threshold")]
pub new_employee_threshold_days: u32,
#[serde(default = "default_volume_fatigue_threshold")]
pub volume_fatigue_threshold: u32,
#[serde(default = "default_coverage_multiplier")]
pub coverage_error_multiplier: f64,
}
fn default_new_employee_rate() -> f64 {
0.05
}
fn default_new_employee_threshold() -> u32 {
180
}
fn default_volume_fatigue_threshold() -> u32 {
50
}
fn default_coverage_multiplier() -> f64 {
1.8
}
impl Default for EmployeeAnomalyRulesConfig {
fn default() -> Self {
Self {
new_employee_error_rate: default_new_employee_rate(),
new_employee_threshold_days: default_new_employee_threshold(),
volume_fatigue_threshold: default_volume_fatigue_threshold(),
coverage_error_multiplier: default_coverage_multiplier(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccountAnomalyRulesConfig {
#[serde(default = "default_high_risk_multiplier")]
pub high_risk_account_multiplier: f64,
#[serde(default = "default_high_risk_accounts")]
pub high_risk_accounts: Vec<String>,
#[serde(default = "default_suspense_multiplier")]
pub suspense_account_multiplier: f64,
#[serde(default = "default_suspense_accounts")]
pub suspense_accounts: Vec<String>,
#[serde(default = "default_intercompany_multiplier")]
pub intercompany_account_multiplier: f64,
}
fn default_high_risk_multiplier() -> f64 {
2.0
}
fn default_high_risk_accounts() -> Vec<String> {
vec![
"1100".to_string(), "2000".to_string(), "3000".to_string(), ]
}
fn default_suspense_multiplier() -> f64 {
3.0
}
fn default_suspense_accounts() -> Vec<String> {
vec!["9999".to_string(), "9998".to_string()]
}
fn default_intercompany_multiplier() -> f64 {
1.5
}
impl Default for AccountAnomalyRulesConfig {
fn default() -> Self {
Self {
high_risk_account_multiplier: default_high_risk_multiplier(),
high_risk_accounts: default_high_risk_accounts(),
suspense_account_multiplier: default_suspense_multiplier(),
suspense_accounts: default_suspense_accounts(),
intercompany_account_multiplier: default_intercompany_multiplier(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BehavioralBaselineConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_baseline_period")]
pub baseline_period_days: u32,
#[serde(default = "default_deviation_threshold")]
pub deviation_threshold_std: f64,
#[serde(default = "default_frequency_deviation")]
pub frequency_deviation_threshold: f64,
}
fn default_baseline_period() -> u32 {
90
}
fn default_deviation_threshold() -> f64 {
3.0
}
fn default_frequency_deviation() -> f64 {
2.0
}
impl Default for BehavioralBaselineConfig {
fn default() -> Self {
Self {
enabled: false,
baseline_period_days: default_baseline_period(),
deviation_threshold_std: default_deviation_threshold(),
frequency_deviation_threshold: default_frequency_deviation(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnhancedLabelingConfig {
#[serde(default = "default_true_val")]
pub severity_scoring: bool,
#[serde(default = "default_true_val")]
pub difficulty_classification: bool,
#[serde(default)]
pub materiality_thresholds: MaterialityThresholdsConfig,
}
impl Default for EnhancedLabelingConfig {
fn default() -> Self {
Self {
severity_scoring: true,
difficulty_classification: true,
materiality_thresholds: MaterialityThresholdsConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaterialityThresholdsConfig {
#[serde(default = "default_materiality_trivial")]
pub trivial: f64,
#[serde(default = "default_materiality_immaterial")]
pub immaterial: f64,
#[serde(default = "default_materiality_material")]
pub material: f64,
#[serde(default = "default_materiality_highly_material")]
pub highly_material: f64,
}
fn default_materiality_trivial() -> f64 {
0.001
}
fn default_materiality_immaterial() -> f64 {
0.01
}
fn default_materiality_material() -> f64 {
0.05
}
fn default_materiality_highly_material() -> f64 {
0.10
}
impl Default for MaterialityThresholdsConfig {
fn default() -> Self {
Self {
trivial: default_materiality_trivial(),
immaterial: default_materiality_immaterial(),
material: default_materiality_material(),
highly_material: default_materiality_highly_material(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct IndustrySpecificConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub manufacturing: ManufacturingConfig,
#[serde(default)]
pub retail: RetailConfig,
#[serde(default)]
pub healthcare: HealthcareConfig,
#[serde(default)]
pub technology: TechnologyConfig,
#[serde(default)]
pub financial_services: FinancialServicesConfig,
#[serde(default)]
pub professional_services: ProfessionalServicesConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManufacturingConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_bom_depth")]
pub bom_depth: u32,
#[serde(default)]
pub just_in_time: bool,
#[serde(default = "default_production_order_types")]
pub production_order_types: Vec<String>,
#[serde(default)]
pub quality_framework: Option<String>,
#[serde(default = "default_supplier_tiers")]
pub supplier_tiers: u32,
#[serde(default = "default_cost_frequency")]
pub standard_cost_frequency: String,
#[serde(default = "default_yield_rate")]
pub target_yield_rate: f64,
#[serde(default = "default_scrap_threshold")]
pub scrap_alert_threshold: f64,
#[serde(default)]
pub anomaly_rates: ManufacturingAnomalyRates,
#[serde(default)]
pub cost_accounting: ManufacturingCostAccountingConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManufacturingCostAccountingConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_true")]
pub variance_accounts_enabled: bool,
#[serde(default = "default_true")]
pub warranty_provisions_enabled: bool,
#[serde(default = "default_warranty_defect_threshold")]
pub warranty_defect_threshold: f64,
}
fn default_warranty_defect_threshold() -> f64 {
0.01
}
impl Default for ManufacturingCostAccountingConfig {
fn default() -> Self {
Self {
enabled: true,
variance_accounts_enabled: true,
warranty_provisions_enabled: true,
warranty_defect_threshold: 0.01,
}
}
}
fn default_bom_depth() -> u32 {
4
}
fn default_production_order_types() -> Vec<String> {
vec![
"standard".to_string(),
"rework".to_string(),
"prototype".to_string(),
]
}
fn default_supplier_tiers() -> u32 {
2
}
fn default_cost_frequency() -> String {
"quarterly".to_string()
}
fn default_yield_rate() -> f64 {
0.97
}
fn default_scrap_threshold() -> f64 {
0.03
}
impl Default for ManufacturingConfig {
fn default() -> Self {
Self {
enabled: false,
bom_depth: default_bom_depth(),
just_in_time: false,
production_order_types: default_production_order_types(),
quality_framework: Some("ISO_9001".to_string()),
supplier_tiers: default_supplier_tiers(),
standard_cost_frequency: default_cost_frequency(),
target_yield_rate: default_yield_rate(),
scrap_alert_threshold: default_scrap_threshold(),
anomaly_rates: ManufacturingAnomalyRates::default(),
cost_accounting: ManufacturingCostAccountingConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManufacturingAnomalyRates {
#[serde(default = "default_mfg_yield_rate")]
pub yield_manipulation: f64,
#[serde(default = "default_mfg_labor_rate")]
pub labor_misallocation: f64,
#[serde(default = "default_mfg_phantom_rate")]
pub phantom_production: f64,
#[serde(default = "default_mfg_cost_rate")]
pub standard_cost_manipulation: f64,
#[serde(default = "default_mfg_inventory_rate")]
pub inventory_fraud: f64,
}
fn default_mfg_yield_rate() -> f64 {
0.015
}
fn default_mfg_labor_rate() -> f64 {
0.02
}
fn default_mfg_phantom_rate() -> f64 {
0.005
}
fn default_mfg_cost_rate() -> f64 {
0.01
}
fn default_mfg_inventory_rate() -> f64 {
0.008
}
impl Default for ManufacturingAnomalyRates {
fn default() -> Self {
Self {
yield_manipulation: default_mfg_yield_rate(),
labor_misallocation: default_mfg_labor_rate(),
phantom_production: default_mfg_phantom_rate(),
standard_cost_manipulation: default_mfg_cost_rate(),
inventory_fraud: default_mfg_inventory_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetailConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub store_types: RetailStoreTypeConfig,
#[serde(default = "default_retail_daily_txns")]
pub avg_daily_transactions: u32,
#[serde(default = "default_true")]
pub loss_prevention: bool,
#[serde(default = "default_shrinkage_rate")]
pub shrinkage_rate: f64,
#[serde(default)]
pub anomaly_rates: RetailAnomalyRates,
}
fn default_retail_daily_txns() -> u32 {
500
}
fn default_shrinkage_rate() -> f64 {
0.015
}
impl Default for RetailConfig {
fn default() -> Self {
Self {
enabled: false,
store_types: RetailStoreTypeConfig::default(),
avg_daily_transactions: default_retail_daily_txns(),
loss_prevention: true,
shrinkage_rate: default_shrinkage_rate(),
anomaly_rates: RetailAnomalyRates::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetailStoreTypeConfig {
#[serde(default = "default_flagship_pct")]
pub flagship: f64,
#[serde(default = "default_regional_pct")]
pub regional: f64,
#[serde(default = "default_outlet_pct")]
pub outlet: f64,
#[serde(default = "default_ecommerce_pct")]
pub ecommerce: f64,
}
fn default_flagship_pct() -> f64 {
0.10
}
fn default_regional_pct() -> f64 {
0.50
}
fn default_outlet_pct() -> f64 {
0.25
}
fn default_ecommerce_pct() -> f64 {
0.15
}
impl Default for RetailStoreTypeConfig {
fn default() -> Self {
Self {
flagship: default_flagship_pct(),
regional: default_regional_pct(),
outlet: default_outlet_pct(),
ecommerce: default_ecommerce_pct(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetailAnomalyRates {
#[serde(default = "default_sweethearting_rate")]
pub sweethearting: f64,
#[serde(default = "default_skimming_rate")]
pub skimming: f64,
#[serde(default = "default_refund_fraud_rate")]
pub refund_fraud: f64,
#[serde(default = "default_void_abuse_rate")]
pub void_abuse: f64,
#[serde(default = "default_gift_card_rate")]
pub gift_card_fraud: f64,
#[serde(default = "default_retail_kickback_rate")]
pub vendor_kickback: f64,
}
fn default_sweethearting_rate() -> f64 {
0.02
}
fn default_skimming_rate() -> f64 {
0.005
}
fn default_refund_fraud_rate() -> f64 {
0.015
}
fn default_void_abuse_rate() -> f64 {
0.01
}
fn default_gift_card_rate() -> f64 {
0.008
}
fn default_retail_kickback_rate() -> f64 {
0.003
}
impl Default for RetailAnomalyRates {
fn default() -> Self {
Self {
sweethearting: default_sweethearting_rate(),
skimming: default_skimming_rate(),
refund_fraud: default_refund_fraud_rate(),
void_abuse: default_void_abuse_rate(),
gift_card_fraud: default_gift_card_rate(),
vendor_kickback: default_retail_kickback_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthcareConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_facility_type")]
pub facility_type: String,
#[serde(default)]
pub payer_mix: HealthcarePayerMix,
#[serde(default)]
pub coding_systems: HealthcareCodingSystems,
#[serde(default)]
pub compliance: HealthcareComplianceConfig,
#[serde(default = "default_daily_encounters")]
pub avg_daily_encounters: u32,
#[serde(default = "default_charges_per_encounter")]
pub avg_charges_per_encounter: u32,
#[serde(default = "default_hc_denial_rate")]
pub denial_rate: f64,
#[serde(default = "default_hc_bad_debt_rate")]
pub bad_debt_rate: f64,
#[serde(default = "default_hc_charity_care_rate")]
pub charity_care_rate: f64,
#[serde(default)]
pub anomaly_rates: HealthcareAnomalyRates,
}
fn default_facility_type() -> String {
"hospital".to_string()
}
fn default_daily_encounters() -> u32 {
150
}
fn default_charges_per_encounter() -> u32 {
8
}
fn default_hc_denial_rate() -> f64 {
0.05
}
fn default_hc_bad_debt_rate() -> f64 {
0.03
}
fn default_hc_charity_care_rate() -> f64 {
0.02
}
impl Default for HealthcareConfig {
fn default() -> Self {
Self {
enabled: false,
facility_type: default_facility_type(),
payer_mix: HealthcarePayerMix::default(),
coding_systems: HealthcareCodingSystems::default(),
compliance: HealthcareComplianceConfig::default(),
avg_daily_encounters: default_daily_encounters(),
avg_charges_per_encounter: default_charges_per_encounter(),
denial_rate: default_hc_denial_rate(),
bad_debt_rate: default_hc_bad_debt_rate(),
charity_care_rate: default_hc_charity_care_rate(),
anomaly_rates: HealthcareAnomalyRates::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthcarePayerMix {
#[serde(default = "default_medicare_pct")]
pub medicare: f64,
#[serde(default = "default_medicaid_pct")]
pub medicaid: f64,
#[serde(default = "default_commercial_pct")]
pub commercial: f64,
#[serde(default = "default_self_pay_pct")]
pub self_pay: f64,
}
fn default_medicare_pct() -> f64 {
0.40
}
fn default_medicaid_pct() -> f64 {
0.20
}
fn default_commercial_pct() -> f64 {
0.30
}
fn default_self_pay_pct() -> f64 {
0.10
}
impl Default for HealthcarePayerMix {
fn default() -> Self {
Self {
medicare: default_medicare_pct(),
medicaid: default_medicaid_pct(),
commercial: default_commercial_pct(),
self_pay: default_self_pay_pct(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthcareCodingSystems {
#[serde(default = "default_true")]
pub icd10: bool,
#[serde(default = "default_true")]
pub cpt: bool,
#[serde(default = "default_true")]
pub drg: bool,
#[serde(default = "default_true")]
pub hcpcs: bool,
#[serde(default = "default_true")]
pub revenue_codes: bool,
}
impl Default for HealthcareCodingSystems {
fn default() -> Self {
Self {
icd10: true,
cpt: true,
drg: true,
hcpcs: true,
revenue_codes: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthcareComplianceConfig {
#[serde(default = "default_true")]
pub hipaa: bool,
#[serde(default = "default_true")]
pub stark_law: bool,
#[serde(default = "default_true")]
pub anti_kickback: bool,
#[serde(default = "default_true")]
pub false_claims_act: bool,
#[serde(default = "default_true")]
pub emtala: bool,
}
impl Default for HealthcareComplianceConfig {
fn default() -> Self {
Self {
hipaa: true,
stark_law: true,
anti_kickback: true,
false_claims_act: true,
emtala: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthcareAnomalyRates {
#[serde(default = "default_upcoding_rate")]
pub upcoding: f64,
#[serde(default = "default_unbundling_rate")]
pub unbundling: f64,
#[serde(default = "default_phantom_billing_rate")]
pub phantom_billing: f64,
#[serde(default = "default_healthcare_kickback_rate")]
pub kickbacks: f64,
#[serde(default = "default_duplicate_billing_rate")]
pub duplicate_billing: f64,
#[serde(default = "default_med_necessity_rate")]
pub medical_necessity_abuse: f64,
}
fn default_upcoding_rate() -> f64 {
0.02
}
fn default_unbundling_rate() -> f64 {
0.015
}
fn default_phantom_billing_rate() -> f64 {
0.005
}
fn default_healthcare_kickback_rate() -> f64 {
0.003
}
fn default_duplicate_billing_rate() -> f64 {
0.008
}
fn default_med_necessity_rate() -> f64 {
0.01
}
impl Default for HealthcareAnomalyRates {
fn default() -> Self {
Self {
upcoding: default_upcoding_rate(),
unbundling: default_unbundling_rate(),
phantom_billing: default_phantom_billing_rate(),
kickbacks: default_healthcare_kickback_rate(),
duplicate_billing: default_duplicate_billing_rate(),
medical_necessity_abuse: default_med_necessity_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TechnologyConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_revenue_model")]
pub revenue_model: String,
#[serde(default = "default_subscription_pct")]
pub subscription_revenue_pct: f64,
#[serde(default = "default_license_pct")]
pub license_revenue_pct: f64,
#[serde(default = "default_services_pct")]
pub services_revenue_pct: f64,
#[serde(default)]
pub rd_capitalization: RdCapitalizationConfig,
#[serde(default)]
pub anomaly_rates: TechnologyAnomalyRates,
}
fn default_revenue_model() -> String {
"saas".to_string()
}
fn default_subscription_pct() -> f64 {
0.60
}
fn default_license_pct() -> f64 {
0.25
}
fn default_services_pct() -> f64 {
0.15
}
impl Default for TechnologyConfig {
fn default() -> Self {
Self {
enabled: false,
revenue_model: default_revenue_model(),
subscription_revenue_pct: default_subscription_pct(),
license_revenue_pct: default_license_pct(),
services_revenue_pct: default_services_pct(),
rd_capitalization: RdCapitalizationConfig::default(),
anomaly_rates: TechnologyAnomalyRates::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RdCapitalizationConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_cap_rate")]
pub capitalization_rate: f64,
#[serde(default = "default_useful_life")]
pub useful_life_years: u32,
}
fn default_cap_rate() -> f64 {
0.30
}
fn default_useful_life() -> u32 {
3
}
impl Default for RdCapitalizationConfig {
fn default() -> Self {
Self {
enabled: true,
capitalization_rate: default_cap_rate(),
useful_life_years: default_useful_life(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TechnologyAnomalyRates {
#[serde(default = "default_premature_rev_rate")]
pub premature_revenue: f64,
#[serde(default = "default_side_letter_rate")]
pub side_letter_abuse: f64,
#[serde(default = "default_channel_stuffing_rate")]
pub channel_stuffing: f64,
#[serde(default = "default_improper_cap_rate")]
pub improper_capitalization: f64,
}
fn default_premature_rev_rate() -> f64 {
0.015
}
fn default_side_letter_rate() -> f64 {
0.008
}
fn default_channel_stuffing_rate() -> f64 {
0.01
}
fn default_improper_cap_rate() -> f64 {
0.012
}
impl Default for TechnologyAnomalyRates {
fn default() -> Self {
Self {
premature_revenue: default_premature_rev_rate(),
side_letter_abuse: default_side_letter_rate(),
channel_stuffing: default_channel_stuffing_rate(),
improper_capitalization: default_improper_cap_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FinancialServicesConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_fi_type")]
pub institution_type: String,
#[serde(default = "default_fi_regulatory")]
pub regulatory_framework: String,
#[serde(default)]
pub anomaly_rates: FinancialServicesAnomalyRates,
}
fn default_fi_type() -> String {
"commercial_bank".to_string()
}
fn default_fi_regulatory() -> String {
"us_banking".to_string()
}
impl Default for FinancialServicesConfig {
fn default() -> Self {
Self {
enabled: false,
institution_type: default_fi_type(),
regulatory_framework: default_fi_regulatory(),
anomaly_rates: FinancialServicesAnomalyRates::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FinancialServicesAnomalyRates {
#[serde(default = "default_loan_fraud_rate")]
pub loan_fraud: f64,
#[serde(default = "default_trading_fraud_rate")]
pub trading_fraud: f64,
#[serde(default = "default_insurance_fraud_rate")]
pub insurance_fraud: f64,
#[serde(default = "default_account_manip_rate")]
pub account_manipulation: f64,
}
fn default_loan_fraud_rate() -> f64 {
0.01
}
fn default_trading_fraud_rate() -> f64 {
0.008
}
fn default_insurance_fraud_rate() -> f64 {
0.012
}
fn default_account_manip_rate() -> f64 {
0.005
}
impl Default for FinancialServicesAnomalyRates {
fn default() -> Self {
Self {
loan_fraud: default_loan_fraud_rate(),
trading_fraud: default_trading_fraud_rate(),
insurance_fraud: default_insurance_fraud_rate(),
account_manipulation: default_account_manip_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfessionalServicesConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_firm_type")]
pub firm_type: String,
#[serde(default = "default_billing_model")]
pub billing_model: String,
#[serde(default = "default_hourly_rate")]
pub avg_hourly_rate: f64,
#[serde(default)]
pub trust_accounting: TrustAccountingConfig,
#[serde(default)]
pub anomaly_rates: ProfessionalServicesAnomalyRates,
}
fn default_firm_type() -> String {
"consulting".to_string()
}
fn default_billing_model() -> String {
"time_and_materials".to_string()
}
fn default_hourly_rate() -> f64 {
250.0
}
impl Default for ProfessionalServicesConfig {
fn default() -> Self {
Self {
enabled: false,
firm_type: default_firm_type(),
billing_model: default_billing_model(),
avg_hourly_rate: default_hourly_rate(),
trust_accounting: TrustAccountingConfig::default(),
anomaly_rates: ProfessionalServicesAnomalyRates::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrustAccountingConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_true")]
pub require_three_way_reconciliation: bool,
}
impl Default for TrustAccountingConfig {
fn default() -> Self {
Self {
enabled: false,
require_three_way_reconciliation: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfessionalServicesAnomalyRates {
#[serde(default = "default_time_fraud_rate")]
pub time_billing_fraud: f64,
#[serde(default = "default_expense_fraud_rate")]
pub expense_fraud: f64,
#[serde(default = "default_trust_misappropriation_rate")]
pub trust_misappropriation: f64,
}
fn default_time_fraud_rate() -> f64 {
0.02
}
fn default_expense_fraud_rate() -> f64 {
0.015
}
fn default_trust_misappropriation_rate() -> f64 {
0.003
}
impl Default for ProfessionalServicesAnomalyRates {
fn default() -> Self {
Self {
time_billing_fraud: default_time_fraud_rate(),
expense_fraud: default_expense_fraud_rate(),
trust_misappropriation: default_trust_misappropriation_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FingerprintPrivacyConfig {
#[serde(default)]
pub level: String,
#[serde(default = "default_epsilon")]
pub epsilon: f64,
#[serde(default = "default_delta")]
pub delta: f64,
#[serde(default = "default_k_anonymity")]
pub k_anonymity: u32,
#[serde(default)]
pub composition_method: String,
}
fn default_epsilon() -> f64 {
1.0
}
fn default_delta() -> f64 {
1e-5
}
fn default_k_anonymity() -> u32 {
5
}
impl Default for FingerprintPrivacyConfig {
fn default() -> Self {
Self {
level: "standard".to_string(),
epsilon: default_epsilon(),
delta: default_delta(),
k_anonymity: default_k_anonymity(),
composition_method: "naive".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityGatesSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_gate_profile_name")]
pub profile: String,
#[serde(default)]
pub fail_on_violation: bool,
#[serde(default)]
pub custom_gates: Vec<QualityGateEntry>,
}
fn default_gate_profile_name() -> String {
"default".to_string()
}
impl Default for QualityGatesSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
profile: default_gate_profile_name(),
fail_on_violation: false,
custom_gates: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityGateEntry {
pub name: String,
pub metric: String,
pub threshold: f64,
#[serde(default)]
pub upper_threshold: Option<f64>,
#[serde(default = "default_gate_comparison")]
pub comparison: String,
}
fn default_gate_comparison() -> String {
"gte".to_string()
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ComplianceSchemaConfig {
#[serde(default)]
pub content_marking: ContentMarkingSchemaConfig,
#[serde(default)]
pub article10_report: bool,
#[serde(default)]
pub certificates: CertificateSchemaConfig,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CertificateSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub signing_key_env: Option<String>,
#[serde(default)]
pub include_quality_metrics: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentMarkingSchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_marking_format")]
pub format: String,
}
fn default_marking_format() -> String {
"embedded".to_string()
}
impl Default for ContentMarkingSchemaConfig {
fn default() -> Self {
Self {
enabled: true,
format: default_marking_format(),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct WebhookSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub endpoints: Vec<WebhookEndpointConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebhookEndpointConfig {
pub url: String,
#[serde(default)]
pub events: Vec<String>,
#[serde(default)]
pub secret: Option<String>,
#[serde(default = "default_webhook_retries")]
pub max_retries: u32,
#[serde(default = "default_webhook_timeout")]
pub timeout_secs: u64,
}
fn default_webhook_retries() -> u32 {
3
}
fn default_webhook_timeout() -> u64 {
10
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SourceToPayConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub spend_analysis: SpendAnalysisConfig,
#[serde(default)]
pub sourcing: SourcingConfig,
#[serde(default)]
pub qualification: QualificationConfig,
#[serde(default)]
pub rfx: RfxConfig,
#[serde(default)]
pub contracts: ContractConfig,
#[serde(default)]
pub catalog: CatalogConfig,
#[serde(default)]
pub scorecards: ScorecardConfig,
#[serde(default)]
pub p2p_integration: P2PIntegrationConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpendAnalysisConfig {
#[serde(default = "default_hhi_threshold")]
pub hhi_threshold: f64,
#[serde(default = "default_contract_coverage_target")]
pub contract_coverage_target: f64,
}
impl Default for SpendAnalysisConfig {
fn default() -> Self {
Self {
hhi_threshold: default_hhi_threshold(),
contract_coverage_target: default_contract_coverage_target(),
}
}
}
fn default_hhi_threshold() -> f64 {
2500.0
}
fn default_contract_coverage_target() -> f64 {
0.80
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourcingConfig {
#[serde(default = "default_sourcing_projects_per_year")]
pub projects_per_year: u32,
#[serde(default = "default_renewal_horizon_months")]
pub renewal_horizon_months: u32,
#[serde(default = "default_project_duration_months")]
pub project_duration_months: u32,
}
impl Default for SourcingConfig {
fn default() -> Self {
Self {
projects_per_year: default_sourcing_projects_per_year(),
renewal_horizon_months: default_renewal_horizon_months(),
project_duration_months: default_project_duration_months(),
}
}
}
fn default_sourcing_projects_per_year() -> u32 {
10
}
fn default_renewal_horizon_months() -> u32 {
3
}
fn default_project_duration_months() -> u32 {
4
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualificationConfig {
#[serde(default = "default_qualification_pass_rate")]
pub pass_rate: f64,
#[serde(default = "default_qualification_validity_days")]
pub validity_days: u32,
#[serde(default = "default_financial_weight")]
pub financial_weight: f64,
#[serde(default = "default_quality_weight")]
pub quality_weight: f64,
#[serde(default = "default_delivery_weight")]
pub delivery_weight: f64,
#[serde(default = "default_compliance_weight")]
pub compliance_weight: f64,
}
impl Default for QualificationConfig {
fn default() -> Self {
Self {
pass_rate: default_qualification_pass_rate(),
validity_days: default_qualification_validity_days(),
financial_weight: default_financial_weight(),
quality_weight: default_quality_weight(),
delivery_weight: default_delivery_weight(),
compliance_weight: default_compliance_weight(),
}
}
}
fn default_qualification_pass_rate() -> f64 {
0.75
}
fn default_qualification_validity_days() -> u32 {
365
}
fn default_financial_weight() -> f64 {
0.25
}
fn default_quality_weight() -> f64 {
0.30
}
fn default_delivery_weight() -> f64 {
0.25
}
fn default_compliance_weight() -> f64 {
0.20
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RfxConfig {
#[serde(default = "default_rfi_threshold")]
pub rfi_threshold: f64,
#[serde(default = "default_min_invited_vendors")]
pub min_invited_vendors: u32,
#[serde(default = "default_max_invited_vendors")]
pub max_invited_vendors: u32,
#[serde(default = "default_response_rate")]
pub response_rate: f64,
#[serde(default = "default_price_weight")]
pub default_price_weight: f64,
#[serde(default = "default_rfx_quality_weight")]
pub default_quality_weight: f64,
#[serde(default = "default_rfx_delivery_weight")]
pub default_delivery_weight: f64,
}
impl Default for RfxConfig {
fn default() -> Self {
Self {
rfi_threshold: default_rfi_threshold(),
min_invited_vendors: default_min_invited_vendors(),
max_invited_vendors: default_max_invited_vendors(),
response_rate: default_response_rate(),
default_price_weight: default_price_weight(),
default_quality_weight: default_rfx_quality_weight(),
default_delivery_weight: default_rfx_delivery_weight(),
}
}
}
fn default_rfi_threshold() -> f64 {
100_000.0
}
fn default_min_invited_vendors() -> u32 {
3
}
fn default_max_invited_vendors() -> u32 {
8
}
fn default_response_rate() -> f64 {
0.70
}
fn default_price_weight() -> f64 {
0.40
}
fn default_rfx_quality_weight() -> f64 {
0.35
}
fn default_rfx_delivery_weight() -> f64 {
0.25
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractConfig {
#[serde(default = "default_min_contract_months")]
pub min_duration_months: u32,
#[serde(default = "default_max_contract_months")]
pub max_duration_months: u32,
#[serde(default = "default_auto_renewal_rate")]
pub auto_renewal_rate: f64,
#[serde(default = "default_amendment_rate")]
pub amendment_rate: f64,
#[serde(default)]
pub type_distribution: ContractTypeDistribution,
}
impl Default for ContractConfig {
fn default() -> Self {
Self {
min_duration_months: default_min_contract_months(),
max_duration_months: default_max_contract_months(),
auto_renewal_rate: default_auto_renewal_rate(),
amendment_rate: default_amendment_rate(),
type_distribution: ContractTypeDistribution::default(),
}
}
}
fn default_min_contract_months() -> u32 {
12
}
fn default_max_contract_months() -> u32 {
36
}
fn default_auto_renewal_rate() -> f64 {
0.40
}
fn default_amendment_rate() -> f64 {
0.20
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractTypeDistribution {
#[serde(default = "default_fixed_price_pct")]
pub fixed_price: f64,
#[serde(default = "default_blanket_pct")]
pub blanket: f64,
#[serde(default = "default_time_materials_pct")]
pub time_and_materials: f64,
#[serde(default = "default_service_agreement_pct")]
pub service_agreement: f64,
}
impl Default for ContractTypeDistribution {
fn default() -> Self {
Self {
fixed_price: default_fixed_price_pct(),
blanket: default_blanket_pct(),
time_and_materials: default_time_materials_pct(),
service_agreement: default_service_agreement_pct(),
}
}
}
fn default_fixed_price_pct() -> f64 {
0.40
}
fn default_blanket_pct() -> f64 {
0.30
}
fn default_time_materials_pct() -> f64 {
0.15
}
fn default_service_agreement_pct() -> f64 {
0.15
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CatalogConfig {
#[serde(default = "default_preferred_vendor_flag_rate")]
pub preferred_vendor_flag_rate: f64,
#[serde(default = "default_multi_source_rate")]
pub multi_source_rate: f64,
}
impl Default for CatalogConfig {
fn default() -> Self {
Self {
preferred_vendor_flag_rate: default_preferred_vendor_flag_rate(),
multi_source_rate: default_multi_source_rate(),
}
}
}
fn default_preferred_vendor_flag_rate() -> f64 {
0.70
}
fn default_multi_source_rate() -> f64 {
0.25
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScorecardConfig {
#[serde(default = "default_scorecard_frequency")]
pub frequency: String,
#[serde(default = "default_otd_weight")]
pub on_time_delivery_weight: f64,
#[serde(default = "default_quality_score_weight")]
pub quality_weight: f64,
#[serde(default = "default_price_score_weight")]
pub price_weight: f64,
#[serde(default = "default_responsiveness_weight")]
pub responsiveness_weight: f64,
#[serde(default = "default_grade_a_threshold")]
pub grade_a_threshold: f64,
#[serde(default = "default_grade_b_threshold")]
pub grade_b_threshold: f64,
#[serde(default = "default_grade_c_threshold")]
pub grade_c_threshold: f64,
}
impl Default for ScorecardConfig {
fn default() -> Self {
Self {
frequency: default_scorecard_frequency(),
on_time_delivery_weight: default_otd_weight(),
quality_weight: default_quality_score_weight(),
price_weight: default_price_score_weight(),
responsiveness_weight: default_responsiveness_weight(),
grade_a_threshold: default_grade_a_threshold(),
grade_b_threshold: default_grade_b_threshold(),
grade_c_threshold: default_grade_c_threshold(),
}
}
}
fn default_scorecard_frequency() -> String {
"quarterly".to_string()
}
fn default_otd_weight() -> f64 {
0.30
}
fn default_quality_score_weight() -> f64 {
0.30
}
fn default_price_score_weight() -> f64 {
0.25
}
fn default_responsiveness_weight() -> f64 {
0.15
}
fn default_grade_a_threshold() -> f64 {
90.0
}
fn default_grade_b_threshold() -> f64 {
75.0
}
fn default_grade_c_threshold() -> f64 {
60.0
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct P2PIntegrationConfig {
#[serde(default = "default_off_contract_rate")]
pub off_contract_rate: f64,
#[serde(default = "default_price_tolerance")]
pub price_tolerance: f64,
#[serde(default)]
pub catalog_enforcement: bool,
}
impl Default for P2PIntegrationConfig {
fn default() -> Self {
Self {
off_contract_rate: default_off_contract_rate(),
price_tolerance: default_price_tolerance(),
catalog_enforcement: false,
}
}
}
fn default_off_contract_rate() -> f64 {
0.15
}
fn default_price_tolerance() -> f64 {
0.02
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FinancialReportingConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_true")]
pub generate_balance_sheet: bool,
#[serde(default = "default_true")]
pub generate_income_statement: bool,
#[serde(default = "default_true")]
pub generate_cash_flow: bool,
#[serde(default = "default_true")]
pub generate_changes_in_equity: bool,
#[serde(default = "default_comparative_periods")]
pub comparative_periods: u32,
#[serde(default)]
pub management_kpis: ManagementKpisConfig,
#[serde(default)]
pub budgets: BudgetConfig,
}
impl Default for FinancialReportingConfig {
fn default() -> Self {
Self {
enabled: false,
generate_balance_sheet: true,
generate_income_statement: true,
generate_cash_flow: true,
generate_changes_in_equity: true,
comparative_periods: default_comparative_periods(),
management_kpis: ManagementKpisConfig::default(),
budgets: BudgetConfig::default(),
}
}
}
fn default_comparative_periods() -> u32 {
1
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ManagementKpisConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_kpi_frequency")]
pub frequency: String,
}
fn default_kpi_frequency() -> String {
"monthly".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BudgetConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_revenue_growth_rate")]
pub revenue_growth_rate: f64,
#[serde(default = "default_expense_inflation_rate")]
pub expense_inflation_rate: f64,
#[serde(default = "default_variance_noise")]
pub variance_noise: f64,
}
impl Default for BudgetConfig {
fn default() -> Self {
Self {
enabled: false,
revenue_growth_rate: default_revenue_growth_rate(),
expense_inflation_rate: default_expense_inflation_rate(),
variance_noise: default_variance_noise(),
}
}
}
fn default_revenue_growth_rate() -> f64 {
0.05
}
fn default_expense_inflation_rate() -> f64 {
0.03
}
fn default_variance_noise() -> f64 {
0.10
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct HrConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub payroll: PayrollConfig,
#[serde(default)]
pub time_attendance: TimeAttendanceConfig,
#[serde(default)]
pub expenses: ExpenseConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PayrollConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_pay_frequency")]
pub pay_frequency: String,
#[serde(default)]
pub salary_ranges: PayrollSalaryRanges,
#[serde(default)]
pub tax_rates: PayrollTaxRates,
#[serde(default = "default_benefits_enrollment_rate")]
pub benefits_enrollment_rate: f64,
#[serde(default = "default_retirement_participation_rate")]
pub retirement_participation_rate: f64,
}
impl Default for PayrollConfig {
fn default() -> Self {
Self {
enabled: true,
pay_frequency: default_pay_frequency(),
salary_ranges: PayrollSalaryRanges::default(),
tax_rates: PayrollTaxRates::default(),
benefits_enrollment_rate: default_benefits_enrollment_rate(),
retirement_participation_rate: default_retirement_participation_rate(),
}
}
}
fn default_pay_frequency() -> String {
"monthly".to_string()
}
fn default_benefits_enrollment_rate() -> f64 {
0.60
}
fn default_retirement_participation_rate() -> f64 {
0.45
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PayrollSalaryRanges {
#[serde(default = "default_staff_min")]
pub staff_min: f64,
#[serde(default = "default_staff_max")]
pub staff_max: f64,
#[serde(default = "default_manager_min")]
pub manager_min: f64,
#[serde(default = "default_manager_max")]
pub manager_max: f64,
#[serde(default = "default_director_min")]
pub director_min: f64,
#[serde(default = "default_director_max")]
pub director_max: f64,
#[serde(default = "default_executive_min")]
pub executive_min: f64,
#[serde(default = "default_executive_max")]
pub executive_max: f64,
}
impl Default for PayrollSalaryRanges {
fn default() -> Self {
Self {
staff_min: default_staff_min(),
staff_max: default_staff_max(),
manager_min: default_manager_min(),
manager_max: default_manager_max(),
director_min: default_director_min(),
director_max: default_director_max(),
executive_min: default_executive_min(),
executive_max: default_executive_max(),
}
}
}
fn default_staff_min() -> f64 {
50_000.0
}
fn default_staff_max() -> f64 {
70_000.0
}
fn default_manager_min() -> f64 {
80_000.0
}
fn default_manager_max() -> f64 {
120_000.0
}
fn default_director_min() -> f64 {
120_000.0
}
fn default_director_max() -> f64 {
180_000.0
}
fn default_executive_min() -> f64 {
180_000.0
}
fn default_executive_max() -> f64 {
350_000.0
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PayrollTaxRates {
#[serde(default = "default_federal_rate")]
pub federal_effective: f64,
#[serde(default = "default_state_rate")]
pub state_effective: f64,
#[serde(default = "default_fica_rate")]
pub fica: f64,
}
impl Default for PayrollTaxRates {
fn default() -> Self {
Self {
federal_effective: default_federal_rate(),
state_effective: default_state_rate(),
fica: default_fica_rate(),
}
}
}
fn default_federal_rate() -> f64 {
0.22
}
fn default_state_rate() -> f64 {
0.05
}
fn default_fica_rate() -> f64 {
0.0765
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeAttendanceConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_overtime_rate")]
pub overtime_rate: f64,
}
impl Default for TimeAttendanceConfig {
fn default() -> Self {
Self {
enabled: true,
overtime_rate: default_overtime_rate(),
}
}
}
fn default_overtime_rate() -> f64 {
0.10
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExpenseConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_expense_submission_rate")]
pub submission_rate: f64,
#[serde(default = "default_policy_violation_rate")]
pub policy_violation_rate: f64,
}
impl Default for ExpenseConfig {
fn default() -> Self {
Self {
enabled: true,
submission_rate: default_expense_submission_rate(),
policy_violation_rate: default_policy_violation_rate(),
}
}
}
fn default_expense_submission_rate() -> f64 {
0.30
}
fn default_policy_violation_rate() -> f64 {
0.08
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ManufacturingProcessConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub production_orders: ProductionOrderConfig,
#[serde(default)]
pub costing: ManufacturingCostingConfig,
#[serde(default)]
pub routing: RoutingConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProductionOrderConfig {
#[serde(default = "default_prod_orders_per_month")]
pub orders_per_month: u32,
#[serde(default = "default_prod_avg_batch_size")]
pub avg_batch_size: u32,
#[serde(default = "default_prod_yield_rate")]
pub yield_rate: f64,
#[serde(default = "default_prod_make_to_order_rate")]
pub make_to_order_rate: f64,
#[serde(default = "default_prod_rework_rate")]
pub rework_rate: f64,
}
impl Default for ProductionOrderConfig {
fn default() -> Self {
Self {
orders_per_month: default_prod_orders_per_month(),
avg_batch_size: default_prod_avg_batch_size(),
yield_rate: default_prod_yield_rate(),
make_to_order_rate: default_prod_make_to_order_rate(),
rework_rate: default_prod_rework_rate(),
}
}
}
fn default_prod_orders_per_month() -> u32 {
50
}
fn default_prod_avg_batch_size() -> u32 {
100
}
fn default_prod_yield_rate() -> f64 {
0.97
}
fn default_prod_make_to_order_rate() -> f64 {
0.20
}
fn default_prod_rework_rate() -> f64 {
0.03
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManufacturingCostingConfig {
#[serde(default = "default_labor_rate")]
pub labor_rate_per_hour: f64,
#[serde(default = "default_overhead_rate")]
pub overhead_rate: f64,
#[serde(default = "default_cost_update_frequency")]
pub standard_cost_update_frequency: String,
}
impl Default for ManufacturingCostingConfig {
fn default() -> Self {
Self {
labor_rate_per_hour: default_labor_rate(),
overhead_rate: default_overhead_rate(),
standard_cost_update_frequency: default_cost_update_frequency(),
}
}
}
fn default_labor_rate() -> f64 {
35.0
}
fn default_overhead_rate() -> f64 {
1.50
}
fn default_cost_update_frequency() -> String {
"quarterly".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoutingConfig {
#[serde(default = "default_avg_operations")]
pub avg_operations: u32,
#[serde(default = "default_setup_time")]
pub setup_time_hours: f64,
#[serde(default = "default_run_time_variation")]
pub run_time_variation: f64,
}
impl Default for RoutingConfig {
fn default() -> Self {
Self {
avg_operations: default_avg_operations(),
setup_time_hours: default_setup_time(),
run_time_variation: default_run_time_variation(),
}
}
}
fn default_avg_operations() -> u32 {
4
}
fn default_setup_time() -> f64 {
1.5
}
fn default_run_time_variation() -> f64 {
0.15
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SalesQuoteConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_quotes_per_month")]
pub quotes_per_month: u32,
#[serde(default = "default_quote_win_rate")]
pub win_rate: f64,
#[serde(default = "default_quote_validity_days")]
pub validity_days: u32,
}
impl Default for SalesQuoteConfig {
fn default() -> Self {
Self {
enabled: false,
quotes_per_month: default_quotes_per_month(),
win_rate: default_quote_win_rate(),
validity_days: default_quote_validity_days(),
}
}
}
fn default_quotes_per_month() -> u32 {
30
}
fn default_quote_win_rate() -> f64 {
0.35
}
fn default_quote_validity_days() -> u32 {
30
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaxConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub jurisdictions: TaxJurisdictionConfig,
#[serde(default)]
pub vat_gst: VatGstConfig,
#[serde(default)]
pub sales_tax: SalesTaxConfig,
#[serde(default)]
pub withholding: WithholdingTaxSchemaConfig,
#[serde(default)]
pub provisions: TaxProvisionSchemaConfig,
#[serde(default)]
pub payroll_tax: PayrollTaxSchemaConfig,
#[serde(default = "default_tax_anomaly_rate")]
pub anomaly_rate: f64,
}
fn default_tax_anomaly_rate() -> f64 {
0.03
}
impl Default for TaxConfig {
fn default() -> Self {
Self {
enabled: false,
jurisdictions: TaxJurisdictionConfig::default(),
vat_gst: VatGstConfig::default(),
sales_tax: SalesTaxConfig::default(),
withholding: WithholdingTaxSchemaConfig::default(),
provisions: TaxProvisionSchemaConfig::default(),
payroll_tax: PayrollTaxSchemaConfig::default(),
anomaly_rate: default_tax_anomaly_rate(),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TaxJurisdictionConfig {
#[serde(default)]
pub countries: Vec<String>,
#[serde(default)]
pub include_subnational: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VatGstConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub standard_rates: std::collections::HashMap<String, f64>,
#[serde(default)]
pub reduced_rates: std::collections::HashMap<String, f64>,
#[serde(default)]
pub exempt_categories: Vec<String>,
#[serde(default = "default_true")]
pub reverse_charge: bool,
}
impl Default for VatGstConfig {
fn default() -> Self {
Self {
enabled: false,
standard_rates: std::collections::HashMap::new(),
reduced_rates: std::collections::HashMap::new(),
exempt_categories: Vec::new(),
reverse_charge: true,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SalesTaxConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub nexus_states: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WithholdingTaxSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_true")]
pub treaty_network: bool,
#[serde(default = "default_withholding_rate")]
pub default_rate: f64,
#[serde(default = "default_treaty_reduced_rate")]
pub treaty_reduced_rate: f64,
}
fn default_withholding_rate() -> f64 {
0.30
}
fn default_treaty_reduced_rate() -> f64 {
0.15
}
impl Default for WithholdingTaxSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
treaty_network: true,
default_rate: default_withholding_rate(),
treaty_reduced_rate: default_treaty_reduced_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaxProvisionSchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_statutory_rate")]
pub statutory_rate: f64,
#[serde(default = "default_true")]
pub uncertain_positions: bool,
}
fn default_statutory_rate() -> f64 {
0.21
}
impl Default for TaxProvisionSchemaConfig {
fn default() -> Self {
Self {
enabled: true,
statutory_rate: default_statutory_rate(),
uncertain_positions: true,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PayrollTaxSchemaConfig {
#[serde(default)]
pub enabled: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TreasuryConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub cash_positioning: CashPositioningConfig,
#[serde(default)]
pub cash_forecasting: CashForecastingConfig,
#[serde(default)]
pub cash_pooling: CashPoolingConfig,
#[serde(default)]
pub hedging: HedgingSchemaConfig,
#[serde(default)]
pub debt: DebtSchemaConfig,
#[serde(default)]
pub netting: NettingSchemaConfig,
#[serde(default)]
pub bank_guarantees: BankGuaranteeSchemaConfig,
#[serde(default = "default_treasury_anomaly_rate")]
pub anomaly_rate: f64,
}
fn default_treasury_anomaly_rate() -> f64 {
0.02
}
impl Default for TreasuryConfig {
fn default() -> Self {
Self {
enabled: false,
cash_positioning: CashPositioningConfig::default(),
cash_forecasting: CashForecastingConfig::default(),
cash_pooling: CashPoolingConfig::default(),
hedging: HedgingSchemaConfig::default(),
debt: DebtSchemaConfig::default(),
netting: NettingSchemaConfig::default(),
bank_guarantees: BankGuaranteeSchemaConfig::default(),
anomaly_rate: default_treasury_anomaly_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CashPositioningConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_cash_frequency")]
pub frequency: String,
#[serde(default = "default_minimum_balance_policy")]
pub minimum_balance_policy: f64,
}
fn default_cash_frequency() -> String {
"daily".to_string()
}
fn default_minimum_balance_policy() -> f64 {
100_000.0
}
impl Default for CashPositioningConfig {
fn default() -> Self {
Self {
enabled: true,
frequency: default_cash_frequency(),
minimum_balance_policy: default_minimum_balance_policy(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CashForecastingConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_horizon_days")]
pub horizon_days: u32,
#[serde(default = "default_ar_probability_curve")]
pub ar_collection_probability_curve: String,
#[serde(default = "default_confidence_interval")]
pub confidence_interval: f64,
}
fn default_horizon_days() -> u32 {
90
}
fn default_ar_probability_curve() -> String {
"aging".to_string()
}
fn default_confidence_interval() -> f64 {
0.90
}
impl Default for CashForecastingConfig {
fn default() -> Self {
Self {
enabled: true,
horizon_days: default_horizon_days(),
ar_collection_probability_curve: default_ar_probability_curve(),
confidence_interval: default_confidence_interval(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CashPoolingConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_pool_type")]
pub pool_type: String,
#[serde(default = "default_sweep_time")]
pub sweep_time: String,
}
fn default_pool_type() -> String {
"zero_balancing".to_string()
}
fn default_sweep_time() -> String {
"16:00".to_string()
}
impl Default for CashPoolingConfig {
fn default() -> Self {
Self {
enabled: false,
pool_type: default_pool_type(),
sweep_time: default_sweep_time(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HedgingSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_hedge_ratio")]
pub hedge_ratio: f64,
#[serde(default = "default_hedge_instruments")]
pub instruments: Vec<String>,
#[serde(default = "default_true")]
pub hedge_accounting: bool,
#[serde(default = "default_effectiveness_method")]
pub effectiveness_method: String,
}
fn default_hedge_ratio() -> f64 {
0.75
}
fn default_hedge_instruments() -> Vec<String> {
vec!["fx_forward".to_string(), "interest_rate_swap".to_string()]
}
fn default_effectiveness_method() -> String {
"regression".to_string()
}
impl Default for HedgingSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
hedge_ratio: default_hedge_ratio(),
instruments: default_hedge_instruments(),
hedge_accounting: true,
effectiveness_method: default_effectiveness_method(),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct DebtSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub instruments: Vec<DebtInstrumentDef>,
#[serde(default)]
pub covenants: Vec<CovenantDef>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DebtInstrumentDef {
#[serde(rename = "type")]
pub instrument_type: String,
#[serde(default)]
pub principal: Option<f64>,
#[serde(default)]
pub rate: Option<f64>,
#[serde(default)]
pub maturity_months: Option<u32>,
#[serde(default)]
pub facility: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CovenantDef {
#[serde(rename = "type")]
pub covenant_type: String,
pub threshold: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NettingSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_netting_cycle")]
pub cycle: String,
}
fn default_netting_cycle() -> String {
"monthly".to_string()
}
impl Default for NettingSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
cycle: default_netting_cycle(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BankGuaranteeSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_guarantee_count")]
pub count: u32,
}
fn default_guarantee_count() -> u32 {
5
}
impl Default for BankGuaranteeSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
count: default_guarantee_count(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectAccountingConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_project_count")]
pub project_count: u32,
#[serde(default)]
pub project_types: ProjectTypeDistribution,
#[serde(default)]
pub wbs: WbsSchemaConfig,
#[serde(default)]
pub cost_allocation: CostAllocationConfig,
#[serde(default)]
pub revenue_recognition: ProjectRevenueRecognitionConfig,
#[serde(default)]
pub milestones: MilestoneSchemaConfig,
#[serde(default)]
pub change_orders: ChangeOrderSchemaConfig,
#[serde(default)]
pub retainage: RetainageSchemaConfig,
#[serde(default)]
pub earned_value: EarnedValueSchemaConfig,
#[serde(default = "default_project_anomaly_rate")]
pub anomaly_rate: f64,
}
fn default_project_count() -> u32 {
10
}
fn default_project_anomaly_rate() -> f64 {
0.03
}
impl Default for ProjectAccountingConfig {
fn default() -> Self {
Self {
enabled: false,
project_count: default_project_count(),
project_types: ProjectTypeDistribution::default(),
wbs: WbsSchemaConfig::default(),
cost_allocation: CostAllocationConfig::default(),
revenue_recognition: ProjectRevenueRecognitionConfig::default(),
milestones: MilestoneSchemaConfig::default(),
change_orders: ChangeOrderSchemaConfig::default(),
retainage: RetainageSchemaConfig::default(),
earned_value: EarnedValueSchemaConfig::default(),
anomaly_rate: default_project_anomaly_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectTypeDistribution {
#[serde(default = "default_capital_weight")]
pub capital: f64,
#[serde(default = "default_internal_weight")]
pub internal: f64,
#[serde(default = "default_customer_weight")]
pub customer: f64,
#[serde(default = "default_rnd_weight")]
pub r_and_d: f64,
#[serde(default = "default_maintenance_weight")]
pub maintenance: f64,
#[serde(default = "default_technology_weight")]
pub technology: f64,
}
fn default_capital_weight() -> f64 {
0.25
}
fn default_internal_weight() -> f64 {
0.20
}
fn default_customer_weight() -> f64 {
0.30
}
fn default_rnd_weight() -> f64 {
0.10
}
fn default_maintenance_weight() -> f64 {
0.10
}
fn default_technology_weight() -> f64 {
0.05
}
impl Default for ProjectTypeDistribution {
fn default() -> Self {
Self {
capital: default_capital_weight(),
internal: default_internal_weight(),
customer: default_customer_weight(),
r_and_d: default_rnd_weight(),
maintenance: default_maintenance_weight(),
technology: default_technology_weight(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WbsSchemaConfig {
#[serde(default = "default_wbs_max_depth")]
pub max_depth: u32,
#[serde(default = "default_wbs_min_elements")]
pub min_elements_per_level: u32,
#[serde(default = "default_wbs_max_elements")]
pub max_elements_per_level: u32,
}
fn default_wbs_max_depth() -> u32 {
3
}
fn default_wbs_min_elements() -> u32 {
2
}
fn default_wbs_max_elements() -> u32 {
6
}
impl Default for WbsSchemaConfig {
fn default() -> Self {
Self {
max_depth: default_wbs_max_depth(),
min_elements_per_level: default_wbs_min_elements(),
max_elements_per_level: default_wbs_max_elements(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CostAllocationConfig {
#[serde(default = "default_time_entry_rate")]
pub time_entry_project_rate: f64,
#[serde(default = "default_expense_rate")]
pub expense_project_rate: f64,
#[serde(default = "default_po_rate")]
pub purchase_order_project_rate: f64,
#[serde(default = "default_vi_rate")]
pub vendor_invoice_project_rate: f64,
}
fn default_time_entry_rate() -> f64 {
0.60
}
fn default_expense_rate() -> f64 {
0.30
}
fn default_po_rate() -> f64 {
0.40
}
fn default_vi_rate() -> f64 {
0.35
}
impl Default for CostAllocationConfig {
fn default() -> Self {
Self {
time_entry_project_rate: default_time_entry_rate(),
expense_project_rate: default_expense_rate(),
purchase_order_project_rate: default_po_rate(),
vendor_invoice_project_rate: default_vi_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectRevenueRecognitionConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_revenue_method")]
pub method: String,
#[serde(default = "default_completion_measure")]
pub completion_measure: String,
#[serde(default = "default_avg_contract_value")]
pub avg_contract_value: f64,
}
fn default_revenue_method() -> String {
"percentage_of_completion".to_string()
}
fn default_completion_measure() -> String {
"cost_to_cost".to_string()
}
fn default_avg_contract_value() -> f64 {
500_000.0
}
impl Default for ProjectRevenueRecognitionConfig {
fn default() -> Self {
Self {
enabled: true,
method: default_revenue_method(),
completion_measure: default_completion_measure(),
avg_contract_value: default_avg_contract_value(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MilestoneSchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_milestones_per_project")]
pub avg_per_project: u32,
#[serde(default = "default_payment_milestone_rate")]
pub payment_milestone_rate: f64,
}
fn default_milestones_per_project() -> u32 {
4
}
fn default_payment_milestone_rate() -> f64 {
0.50
}
impl Default for MilestoneSchemaConfig {
fn default() -> Self {
Self {
enabled: true,
avg_per_project: default_milestones_per_project(),
payment_milestone_rate: default_payment_milestone_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChangeOrderSchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_change_order_probability")]
pub probability: f64,
#[serde(default = "default_max_change_orders")]
pub max_per_project: u32,
#[serde(default = "default_change_order_approval_rate")]
pub approval_rate: f64,
}
fn default_change_order_probability() -> f64 {
0.40
}
fn default_max_change_orders() -> u32 {
3
}
fn default_change_order_approval_rate() -> f64 {
0.75
}
impl Default for ChangeOrderSchemaConfig {
fn default() -> Self {
Self {
enabled: true,
probability: default_change_order_probability(),
max_per_project: default_max_change_orders(),
approval_rate: default_change_order_approval_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetainageSchemaConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_retainage_pct")]
pub default_percentage: f64,
}
fn default_retainage_pct() -> f64 {
0.10
}
impl Default for RetainageSchemaConfig {
fn default() -> Self {
Self {
enabled: false,
default_percentage: default_retainage_pct(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EarnedValueSchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_evm_frequency")]
pub frequency: String,
}
fn default_evm_frequency() -> String {
"monthly".to_string()
}
impl Default for EarnedValueSchemaConfig {
fn default() -> Self {
Self {
enabled: true,
frequency: default_evm_frequency(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EsgConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub environmental: EnvironmentalConfig,
#[serde(default)]
pub social: SocialConfig,
#[serde(default)]
pub governance: GovernanceSchemaConfig,
#[serde(default)]
pub supply_chain_esg: SupplyChainEsgConfig,
#[serde(default)]
pub reporting: EsgReportingConfig,
#[serde(default)]
pub climate_scenarios: ClimateScenarioConfig,
#[serde(default = "default_esg_anomaly_rate")]
pub anomaly_rate: f64,
}
fn default_esg_anomaly_rate() -> f64 {
0.02
}
impl Default for EsgConfig {
fn default() -> Self {
Self {
enabled: false,
environmental: EnvironmentalConfig::default(),
social: SocialConfig::default(),
governance: GovernanceSchemaConfig::default(),
supply_chain_esg: SupplyChainEsgConfig::default(),
reporting: EsgReportingConfig::default(),
climate_scenarios: ClimateScenarioConfig::default(),
anomaly_rate: default_esg_anomaly_rate(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CountryPacksSchemaConfig {
#[serde(default)]
pub external_dir: Option<PathBuf>,
#[serde(default)]
pub overrides: std::collections::HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvironmentalConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default)]
pub scope1: EmissionScopeConfig,
#[serde(default)]
pub scope2: EmissionScopeConfig,
#[serde(default)]
pub scope3: Scope3Config,
#[serde(default)]
pub energy: EnergySchemaConfig,
#[serde(default)]
pub water: WaterSchemaConfig,
#[serde(default)]
pub waste: WasteSchemaConfig,
}
impl Default for EnvironmentalConfig {
fn default() -> Self {
Self {
enabled: true,
scope1: EmissionScopeConfig::default(),
scope2: EmissionScopeConfig::default(),
scope3: Scope3Config::default(),
energy: EnergySchemaConfig::default(),
water: WaterSchemaConfig::default(),
waste: WasteSchemaConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmissionScopeConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_emission_region")]
pub factor_region: String,
}
fn default_emission_region() -> String {
"US".to_string()
}
impl Default for EmissionScopeConfig {
fn default() -> Self {
Self {
enabled: true,
factor_region: default_emission_region(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Scope3Config {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_scope3_categories")]
pub categories: Vec<String>,
#[serde(default = "default_spend_intensity")]
pub default_spend_intensity_kg_per_usd: f64,
}
fn default_scope3_categories() -> Vec<String> {
vec![
"purchased_goods".to_string(),
"business_travel".to_string(),
"employee_commuting".to_string(),
]
}
fn default_spend_intensity() -> f64 {
0.5
}
impl Default for Scope3Config {
fn default() -> Self {
Self {
enabled: true,
categories: default_scope3_categories(),
default_spend_intensity_kg_per_usd: default_spend_intensity(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnergySchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_facility_count")]
pub facility_count: u32,
#[serde(default = "default_renewable_target")]
pub renewable_target: f64,
}
fn default_facility_count() -> u32 {
5
}
fn default_renewable_target() -> f64 {
0.30
}
impl Default for EnergySchemaConfig {
fn default() -> Self {
Self {
enabled: true,
facility_count: default_facility_count(),
renewable_target: default_renewable_target(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WaterSchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_water_facility_count")]
pub facility_count: u32,
}
fn default_water_facility_count() -> u32 {
3
}
impl Default for WaterSchemaConfig {
fn default() -> Self {
Self {
enabled: true,
facility_count: default_water_facility_count(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WasteSchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_diversion_target")]
pub diversion_target: f64,
}
fn default_diversion_target() -> f64 {
0.50
}
impl Default for WasteSchemaConfig {
fn default() -> Self {
Self {
enabled: true,
diversion_target: default_diversion_target(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SocialConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default)]
pub diversity: DiversitySchemaConfig,
#[serde(default)]
pub pay_equity: PayEquitySchemaConfig,
#[serde(default)]
pub safety: SafetySchemaConfig,
}
impl Default for SocialConfig {
fn default() -> Self {
Self {
enabled: true,
diversity: DiversitySchemaConfig::default(),
pay_equity: PayEquitySchemaConfig::default(),
safety: SafetySchemaConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiversitySchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_diversity_dimensions")]
pub dimensions: Vec<String>,
}
fn default_diversity_dimensions() -> Vec<String> {
vec![
"gender".to_string(),
"ethnicity".to_string(),
"age_group".to_string(),
]
}
impl Default for DiversitySchemaConfig {
fn default() -> Self {
Self {
enabled: true,
dimensions: default_diversity_dimensions(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PayEquitySchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_pay_gap_threshold")]
pub gap_threshold: f64,
}
fn default_pay_gap_threshold() -> f64 {
0.05
}
impl Default for PayEquitySchemaConfig {
fn default() -> Self {
Self {
enabled: true,
gap_threshold: default_pay_gap_threshold(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SafetySchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_trir_target")]
pub target_trir: f64,
#[serde(default = "default_incident_count")]
pub incident_count: u32,
}
fn default_trir_target() -> f64 {
2.5
}
fn default_incident_count() -> u32 {
20
}
impl Default for SafetySchemaConfig {
fn default() -> Self {
Self {
enabled: true,
target_trir: default_trir_target(),
incident_count: default_incident_count(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GovernanceSchemaConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_board_size")]
pub board_size: u32,
#[serde(default = "default_independence_target")]
pub independence_target: f64,
}
fn default_board_size() -> u32 {
11
}
fn default_independence_target() -> f64 {
0.67
}
impl Default for GovernanceSchemaConfig {
fn default() -> Self {
Self {
enabled: true,
board_size: default_board_size(),
independence_target: default_independence_target(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SupplyChainEsgConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_assessment_coverage")]
pub assessment_coverage: f64,
#[serde(default = "default_high_risk_countries")]
pub high_risk_countries: Vec<String>,
}
fn default_assessment_coverage() -> f64 {
0.80
}
fn default_high_risk_countries() -> Vec<String> {
vec!["CN".to_string(), "BD".to_string(), "MM".to_string()]
}
impl Default for SupplyChainEsgConfig {
fn default() -> Self {
Self {
enabled: true,
assessment_coverage: default_assessment_coverage(),
high_risk_countries: default_high_risk_countries(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EsgReportingConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_esg_frameworks")]
pub frameworks: Vec<String>,
#[serde(default = "default_true")]
pub materiality_assessment: bool,
#[serde(default = "default_materiality_threshold")]
pub impact_threshold: f64,
#[serde(default = "default_materiality_threshold")]
pub financial_threshold: f64,
}
fn default_esg_frameworks() -> Vec<String> {
vec!["GRI".to_string(), "ESRS".to_string()]
}
fn default_materiality_threshold() -> f64 {
0.6
}
impl Default for EsgReportingConfig {
fn default() -> Self {
Self {
enabled: true,
frameworks: default_esg_frameworks(),
materiality_assessment: true,
impact_threshold: default_materiality_threshold(),
financial_threshold: default_materiality_threshold(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClimateScenarioConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_climate_scenarios")]
pub scenarios: Vec<String>,
#[serde(default = "default_time_horizons")]
pub time_horizons: Vec<u32>,
}
fn default_climate_scenarios() -> Vec<String> {
vec![
"net_zero_2050".to_string(),
"stated_policies".to_string(),
"current_trajectory".to_string(),
]
}
fn default_time_horizons() -> Vec<u32> {
vec![5, 10, 30]
}
impl Default for ClimateScenarioConfig {
fn default() -> Self {
Self {
enabled: false,
scenarios: default_climate_scenarios(),
time_horizons: default_time_horizons(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ScenariosConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub scenarios: Vec<ScenarioSchemaConfig>,
#[serde(default)]
pub causal_model: CausalModelSchemaConfig,
#[serde(default)]
pub defaults: ScenarioDefaultsConfig,
#[serde(default)]
pub generate_counterfactuals: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScenarioSchemaConfig {
pub name: String,
#[serde(default)]
pub description: String,
#[serde(default)]
pub tags: Vec<String>,
pub base: Option<String>,
pub probability_weight: Option<f64>,
#[serde(default)]
pub interventions: Vec<InterventionSchemaConfig>,
#[serde(default)]
pub constraints: ScenarioConstraintsSchemaConfig,
#[serde(default)]
pub output: ScenarioOutputSchemaConfig,
#[serde(default)]
pub metadata: std::collections::HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InterventionSchemaConfig {
#[serde(flatten)]
pub intervention_type: serde_json::Value,
#[serde(default)]
pub timing: InterventionTimingSchemaConfig,
pub label: Option<String>,
#[serde(default)]
pub priority: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InterventionTimingSchemaConfig {
#[serde(default = "default_start_month")]
pub start_month: u32,
pub duration_months: Option<u32>,
#[serde(default = "default_onset")]
pub onset: String,
pub ramp_months: Option<u32>,
}
fn default_start_month() -> u32 {
1
}
fn default_onset() -> String {
"sudden".to_string()
}
impl Default for InterventionTimingSchemaConfig {
fn default() -> Self {
Self {
start_month: 1,
duration_months: None,
onset: "sudden".to_string(),
ramp_months: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScenarioConstraintsSchemaConfig {
#[serde(default = "default_true")]
pub preserve_accounting_identity: bool,
#[serde(default = "default_true")]
pub preserve_document_chains: bool,
#[serde(default = "default_true")]
pub preserve_period_close: bool,
#[serde(default = "default_true")]
pub preserve_balance_coherence: bool,
#[serde(default)]
pub custom: Vec<CustomConstraintSchemaConfig>,
}
impl Default for ScenarioConstraintsSchemaConfig {
fn default() -> Self {
Self {
preserve_accounting_identity: true,
preserve_document_chains: true,
preserve_period_close: true,
preserve_balance_coherence: true,
custom: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomConstraintSchemaConfig {
pub config_path: String,
pub min: Option<f64>,
pub max: Option<f64>,
#[serde(default)]
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScenarioOutputSchemaConfig {
#[serde(default = "default_true")]
pub paired: bool,
#[serde(default = "default_diff_formats_schema")]
pub diff_formats: Vec<String>,
#[serde(default)]
pub diff_scope: Vec<String>,
}
fn default_diff_formats_schema() -> Vec<String> {
vec!["summary".to_string(), "aggregate".to_string()]
}
impl Default for ScenarioOutputSchemaConfig {
fn default() -> Self {
Self {
paired: true,
diff_formats: default_diff_formats_schema(),
diff_scope: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CausalModelSchemaConfig {
#[serde(default = "default_causal_preset")]
pub preset: String,
#[serde(default)]
pub nodes: Vec<serde_json::Value>,
#[serde(default)]
pub edges: Vec<serde_json::Value>,
}
fn default_causal_preset() -> String {
"default".to_string()
}
impl Default for CausalModelSchemaConfig {
fn default() -> Self {
Self {
preset: "default".to_string(),
nodes: Vec::new(),
edges: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ScenarioDefaultsConfig {
#[serde(default)]
pub constraints: ScenarioConstraintsSchemaConfig,
#[serde(default)]
pub output: ScenarioOutputSchemaConfig,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ComplianceRegulationsConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub jurisdictions: Vec<String>,
#[serde(default)]
pub reference_date: Option<String>,
#[serde(default)]
pub standards_selection: StandardsSelectionConfig,
#[serde(default)]
pub audit_procedures: AuditProcedureGenConfig,
#[serde(default)]
pub findings: ComplianceFindingGenConfig,
#[serde(default)]
pub filings: ComplianceFilingGenConfig,
#[serde(default)]
pub graph: ComplianceGraphConfig,
#[serde(default)]
pub output: ComplianceOutputConfig,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StandardsSelectionConfig {
#[serde(default)]
pub categories: Vec<String>,
#[serde(default)]
pub include: Vec<String>,
#[serde(default)]
pub exclude: Vec<String>,
#[serde(default)]
pub include_superseded: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditProcedureGenConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_procedures_per_standard")]
pub procedures_per_standard: usize,
#[serde(default = "default_sampling_method")]
pub sampling_method: String,
#[serde(default = "default_confidence_level")]
pub confidence_level: f64,
#[serde(default = "default_tolerable_misstatement")]
pub tolerable_misstatement: f64,
}
fn default_procedures_per_standard() -> usize {
3
}
fn default_sampling_method() -> String {
"statistical".to_string()
}
fn default_confidence_level() -> f64 {
0.95
}
fn default_tolerable_misstatement() -> f64 {
0.05
}
impl Default for AuditProcedureGenConfig {
fn default() -> Self {
Self {
enabled: false,
procedures_per_standard: default_procedures_per_standard(),
sampling_method: default_sampling_method(),
confidence_level: default_confidence_level(),
tolerable_misstatement: default_tolerable_misstatement(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceFindingGenConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_finding_rate")]
pub finding_rate: f64,
#[serde(default = "default_cr_material_weakness_rate")]
pub material_weakness_rate: f64,
#[serde(default = "default_cr_significant_deficiency_rate")]
pub significant_deficiency_rate: f64,
#[serde(default = "default_true")]
pub generate_remediation: bool,
}
fn default_finding_rate() -> f64 {
0.05
}
fn default_cr_material_weakness_rate() -> f64 {
0.02
}
fn default_cr_significant_deficiency_rate() -> f64 {
0.08
}
impl Default for ComplianceFindingGenConfig {
fn default() -> Self {
Self {
enabled: false,
finding_rate: default_finding_rate(),
material_weakness_rate: default_cr_material_weakness_rate(),
significant_deficiency_rate: default_cr_significant_deficiency_rate(),
generate_remediation: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceFilingGenConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub filing_types: Vec<String>,
#[serde(default = "default_true")]
pub generate_status_progression: bool,
}
impl Default for ComplianceFilingGenConfig {
fn default() -> Self {
Self {
enabled: false,
filing_types: Vec::new(),
generate_status_progression: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceGraphConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_true")]
pub include_compliance_nodes: bool,
#[serde(default = "default_true")]
pub include_compliance_edges: bool,
#[serde(default = "default_true")]
pub include_cross_references: bool,
#[serde(default)]
pub include_supersession_edges: bool,
#[serde(default = "default_true")]
pub include_account_links: bool,
#[serde(default = "default_true")]
pub include_control_links: bool,
#[serde(default = "default_true")]
pub include_company_links: bool,
}
impl Default for ComplianceGraphConfig {
fn default() -> Self {
Self {
enabled: false,
include_compliance_nodes: true,
include_compliance_edges: true,
include_cross_references: true,
include_supersession_edges: false,
include_account_links: true,
include_control_links: true,
include_company_links: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceOutputConfig {
#[serde(default = "default_true")]
pub export_registry: bool,
#[serde(default = "default_true")]
pub export_jurisdictions: bool,
#[serde(default = "default_true")]
pub export_cross_references: bool,
#[serde(default)]
pub export_version_history: bool,
}
impl Default for ComplianceOutputConfig {
fn default() -> Self {
Self {
export_registry: true,
export_jurisdictions: true,
export_cross_references: true,
export_version_history: false,
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use crate::presets::demo_preset;
#[test]
fn test_config_yaml_roundtrip() {
let config = demo_preset();
let yaml = serde_yaml::to_string(&config).expect("Failed to serialize to YAML");
let deserialized: GeneratorConfig =
serde_yaml::from_str(&yaml).expect("Failed to deserialize from YAML");
assert_eq!(
config.global.period_months,
deserialized.global.period_months
);
assert_eq!(config.global.industry, deserialized.global.industry);
assert_eq!(config.companies.len(), deserialized.companies.len());
assert_eq!(config.companies[0].code, deserialized.companies[0].code);
}
#[test]
fn test_config_json_roundtrip() {
let mut config = demo_preset();
config.master_data.employees.approval_limits.executive = 1e12;
let json = serde_json::to_string(&config).expect("Failed to serialize to JSON");
let deserialized: GeneratorConfig =
serde_json::from_str(&json).expect("Failed to deserialize from JSON");
assert_eq!(
config.global.period_months,
deserialized.global.period_months
);
assert_eq!(config.global.industry, deserialized.global.industry);
assert_eq!(config.companies.len(), deserialized.companies.len());
}
#[test]
fn test_transaction_volume_serialization() {
let volumes = vec![
(TransactionVolume::TenK, "ten_k"),
(TransactionVolume::HundredK, "hundred_k"),
(TransactionVolume::OneM, "one_m"),
(TransactionVolume::TenM, "ten_m"),
(TransactionVolume::HundredM, "hundred_m"),
];
for (volume, expected_key) in volumes {
let json = serde_json::to_string(&volume).expect("Failed to serialize");
assert!(
json.contains(expected_key),
"Expected {} in JSON: {}",
expected_key,
json
);
}
}
#[test]
fn test_transaction_volume_custom_serialization() {
let volume = TransactionVolume::Custom(12345);
let json = serde_json::to_string(&volume).expect("Failed to serialize");
let deserialized: TransactionVolume =
serde_json::from_str(&json).expect("Failed to deserialize");
assert_eq!(deserialized.count(), 12345);
}
#[test]
fn test_output_mode_serialization() {
let modes = vec![
OutputMode::Streaming,
OutputMode::FlatFile,
OutputMode::Both,
];
for mode in modes {
let json = serde_json::to_string(&mode).expect("Failed to serialize");
let deserialized: OutputMode =
serde_json::from_str(&json).expect("Failed to deserialize");
assert!(format!("{:?}", mode) == format!("{:?}", deserialized));
}
}
#[test]
fn test_file_format_serialization() {
let formats = vec![
FileFormat::Csv,
FileFormat::Parquet,
FileFormat::Json,
FileFormat::JsonLines,
];
for format in formats {
let json = serde_json::to_string(&format).expect("Failed to serialize");
let deserialized: FileFormat =
serde_json::from_str(&json).expect("Failed to deserialize");
assert!(format!("{:?}", format) == format!("{:?}", deserialized));
}
}
#[test]
fn test_compression_algorithm_serialization() {
let algos = vec![
CompressionAlgorithm::Gzip,
CompressionAlgorithm::Zstd,
CompressionAlgorithm::Lz4,
CompressionAlgorithm::Snappy,
];
for algo in algos {
let json = serde_json::to_string(&algo).expect("Failed to serialize");
let deserialized: CompressionAlgorithm =
serde_json::from_str(&json).expect("Failed to deserialize");
assert!(format!("{:?}", algo) == format!("{:?}", deserialized));
}
}
#[test]
fn test_transfer_pricing_method_serialization() {
let methods = vec![
TransferPricingMethod::CostPlus,
TransferPricingMethod::ComparableUncontrolled,
TransferPricingMethod::ResalePrice,
TransferPricingMethod::TransactionalNetMargin,
TransferPricingMethod::ProfitSplit,
];
for method in methods {
let json = serde_json::to_string(&method).expect("Failed to serialize");
let deserialized: TransferPricingMethod =
serde_json::from_str(&json).expect("Failed to deserialize");
assert!(format!("{:?}", method) == format!("{:?}", deserialized));
}
}
#[test]
fn test_benford_exemption_serialization() {
let exemptions = vec![
BenfordExemption::Recurring,
BenfordExemption::Payroll,
BenfordExemption::FixedFees,
BenfordExemption::RoundAmounts,
];
for exemption in exemptions {
let json = serde_json::to_string(&exemption).expect("Failed to serialize");
let deserialized: BenfordExemption =
serde_json::from_str(&json).expect("Failed to deserialize");
assert!(format!("{:?}", exemption) == format!("{:?}", deserialized));
}
}
#[test]
fn test_global_config_defaults() {
let yaml = r#"
industry: manufacturing
start_date: "2024-01-01"
period_months: 6
"#;
let config: GlobalConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
assert_eq!(config.group_currency, "USD");
assert!(config.parallel);
assert_eq!(config.worker_threads, 0);
assert_eq!(config.memory_limit_mb, 0);
}
#[test]
fn test_fraud_config_defaults() {
let config = FraudConfig::default();
assert!(!config.enabled);
assert_eq!(config.fraud_rate, 0.005);
assert!(!config.clustering_enabled);
}
#[test]
fn test_internal_controls_config_defaults() {
let config = InternalControlsConfig::default();
assert!(!config.enabled);
assert_eq!(config.exception_rate, 0.02);
assert_eq!(config.sod_violation_rate, 0.01);
assert!(config.export_control_master_data);
assert_eq!(config.sox_materiality_threshold, 10000.0);
assert!(config.coso_enabled);
assert!(!config.include_entity_level_controls);
assert_eq!(config.target_maturity_level, "mixed");
}
#[test]
fn test_output_config_defaults() {
let config = OutputConfig::default();
assert!(matches!(config.mode, OutputMode::FlatFile));
assert_eq!(config.formats, vec![FileFormat::Parquet]);
assert!(config.compression.enabled);
assert!(matches!(
config.compression.algorithm,
CompressionAlgorithm::Zstd
));
assert!(config.include_acdoca);
assert!(!config.include_bseg);
assert!(config.partition_by_period);
assert!(!config.partition_by_company);
}
#[test]
fn test_approval_config_defaults() {
let config = ApprovalConfig::default();
assert!(!config.enabled);
assert_eq!(config.auto_approve_threshold, 1000.0);
assert_eq!(config.rejection_rate, 0.02);
assert_eq!(config.revision_rate, 0.05);
assert_eq!(config.average_approval_delay_hours, 4.0);
assert_eq!(config.thresholds.len(), 4);
}
#[test]
fn test_p2p_flow_config_defaults() {
let config = P2PFlowConfig::default();
assert!(config.enabled);
assert_eq!(config.three_way_match_rate, 0.95);
assert_eq!(config.partial_delivery_rate, 0.15);
assert_eq!(config.average_po_to_gr_days, 14);
}
#[test]
fn test_o2c_flow_config_defaults() {
let config = O2CFlowConfig::default();
assert!(config.enabled);
assert_eq!(config.credit_check_failure_rate, 0.02);
assert_eq!(config.return_rate, 0.03);
assert_eq!(config.bad_debt_rate, 0.01);
}
#[test]
fn test_balance_config_defaults() {
let config = BalanceConfig::default();
assert!(!config.generate_opening_balances);
assert!(config.generate_trial_balances);
assert_eq!(config.target_gross_margin, 0.35);
assert!(config.validate_balance_equation);
assert!(config.reconcile_subledgers);
}
#[test]
fn test_partial_config_with_defaults() {
let yaml = r#"
global:
industry: manufacturing
start_date: "2024-01-01"
period_months: 3
companies:
- code: "TEST"
name: "Test Company"
currency: "USD"
country: "US"
annual_transaction_volume: ten_k
chart_of_accounts:
complexity: small
output:
output_directory: "./output"
"#;
let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
assert_eq!(config.global.period_months, 3);
assert_eq!(config.companies.len(), 1);
assert!(!config.fraud.enabled); assert!(!config.internal_controls.enabled); }
#[test]
fn test_config_with_fraud_enabled() {
let yaml = r#"
global:
industry: retail
start_date: "2024-01-01"
period_months: 12
companies:
- code: "RETAIL"
name: "Retail Co"
currency: "USD"
country: "US"
annual_transaction_volume: hundred_k
chart_of_accounts:
complexity: medium
output:
output_directory: "./output"
fraud:
enabled: true
fraud_rate: 0.05
clustering_enabled: true
"#;
let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
assert!(config.fraud.enabled);
assert_eq!(config.fraud.fraud_rate, 0.05);
assert!(config.fraud.clustering_enabled);
}
#[test]
fn test_config_with_multiple_companies() {
let yaml = r#"
global:
industry: manufacturing
start_date: "2024-01-01"
period_months: 6
companies:
- code: "HQ"
name: "Headquarters"
currency: "USD"
country: "US"
annual_transaction_volume: hundred_k
volume_weight: 1.0
- code: "EU"
name: "European Subsidiary"
currency: "EUR"
country: "DE"
annual_transaction_volume: hundred_k
volume_weight: 0.5
- code: "APAC"
name: "Asia Pacific"
currency: "JPY"
country: "JP"
annual_transaction_volume: ten_k
volume_weight: 0.3
chart_of_accounts:
complexity: large
output:
output_directory: "./output"
"#;
let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
assert_eq!(config.companies.len(), 3);
assert_eq!(config.companies[0].code, "HQ");
assert_eq!(config.companies[1].currency, "EUR");
assert_eq!(config.companies[2].volume_weight, 0.3);
}
#[test]
fn test_intercompany_config() {
let yaml = r#"
enabled: true
ic_transaction_rate: 0.20
transfer_pricing_method: cost_plus
markup_percent: 0.08
generate_matched_pairs: true
generate_eliminations: true
"#;
let config: IntercompanyConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
assert!(config.enabled);
assert_eq!(config.ic_transaction_rate, 0.20);
assert!(matches!(
config.transfer_pricing_method,
TransferPricingMethod::CostPlus
));
assert_eq!(config.markup_percent, 0.08);
assert!(config.generate_eliminations);
}
#[test]
fn test_company_config_defaults() {
let yaml = r#"
code: "TEST"
name: "Test Company"
currency: "USD"
country: "US"
annual_transaction_volume: ten_k
"#;
let config: CompanyConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
assert_eq!(config.fiscal_year_variant, "K4"); assert_eq!(config.volume_weight, 1.0); }
#[test]
fn test_coa_config_defaults() {
let yaml = r#"
complexity: medium
"#;
let config: ChartOfAccountsConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
assert!(config.industry_specific); assert!(config.custom_accounts.is_none());
assert_eq!(config.min_hierarchy_depth, 2); assert_eq!(config.max_hierarchy_depth, 5); }
#[test]
fn test_accounting_standards_config_defaults() {
let config = AccountingStandardsConfig::default();
assert!(!config.enabled);
assert!(config.framework.is_none());
assert!(!config.revenue_recognition.enabled);
assert!(!config.leases.enabled);
assert!(!config.fair_value.enabled);
assert!(!config.impairment.enabled);
assert!(!config.generate_differences);
}
#[test]
fn test_accounting_standards_config_yaml() {
let yaml = r#"
enabled: true
framework: ifrs
revenue_recognition:
enabled: true
generate_contracts: true
avg_obligations_per_contract: 2.5
variable_consideration_rate: 0.20
over_time_recognition_rate: 0.35
contract_count: 150
leases:
enabled: true
lease_count: 75
finance_lease_percent: 0.25
avg_lease_term_months: 48
generate_differences: true
"#;
let config: AccountingStandardsConfig =
serde_yaml::from_str(yaml).expect("Failed to parse");
assert!(config.enabled);
assert!(matches!(
config.framework,
Some(AccountingFrameworkConfig::Ifrs)
));
assert!(config.revenue_recognition.enabled);
assert_eq!(config.revenue_recognition.contract_count, 150);
assert_eq!(config.revenue_recognition.avg_obligations_per_contract, 2.5);
assert!(config.leases.enabled);
assert_eq!(config.leases.lease_count, 75);
assert_eq!(config.leases.finance_lease_percent, 0.25);
assert!(config.generate_differences);
}
#[test]
fn test_accounting_framework_serialization() {
let frameworks = [
AccountingFrameworkConfig::UsGaap,
AccountingFrameworkConfig::Ifrs,
AccountingFrameworkConfig::DualReporting,
AccountingFrameworkConfig::FrenchGaap,
AccountingFrameworkConfig::GermanGaap,
];
for framework in frameworks {
let json = serde_json::to_string(&framework).expect("Failed to serialize");
let deserialized: AccountingFrameworkConfig =
serde_json::from_str(&json).expect("Failed to deserialize");
assert!(format!("{:?}", framework) == format!("{:?}", deserialized));
}
}
#[test]
fn test_revenue_recognition_config_defaults() {
let config = RevenueRecognitionConfig::default();
assert!(!config.enabled);
assert!(config.generate_contracts);
assert_eq!(config.avg_obligations_per_contract, 2.0);
assert_eq!(config.variable_consideration_rate, 0.15);
assert_eq!(config.over_time_recognition_rate, 0.30);
assert_eq!(config.contract_count, 100);
}
#[test]
fn test_lease_accounting_config_defaults() {
let config = LeaseAccountingConfig::default();
assert!(!config.enabled);
assert_eq!(config.lease_count, 50);
assert_eq!(config.finance_lease_percent, 0.30);
assert_eq!(config.avg_lease_term_months, 60);
assert!(config.generate_amortization);
assert_eq!(config.real_estate_percent, 0.40);
}
#[test]
fn test_fair_value_config_defaults() {
let config = FairValueConfig::default();
assert!(!config.enabled);
assert_eq!(config.measurement_count, 25);
assert_eq!(config.level1_percent, 0.40);
assert_eq!(config.level2_percent, 0.35);
assert_eq!(config.level3_percent, 0.25);
assert!(!config.include_sensitivity_analysis);
}
#[test]
fn test_impairment_config_defaults() {
let config = ImpairmentConfig::default();
assert!(!config.enabled);
assert_eq!(config.test_count, 15);
assert_eq!(config.impairment_rate, 0.10);
assert!(config.generate_projections);
assert!(!config.include_goodwill);
}
#[test]
fn test_audit_standards_config_defaults() {
let config = AuditStandardsConfig::default();
assert!(!config.enabled);
assert!(!config.isa_compliance.enabled);
assert!(!config.analytical_procedures.enabled);
assert!(!config.confirmations.enabled);
assert!(!config.opinion.enabled);
assert!(!config.generate_audit_trail);
assert!(!config.sox.enabled);
assert!(!config.pcaob.enabled);
}
#[test]
fn test_audit_standards_config_yaml() {
let yaml = r#"
enabled: true
isa_compliance:
enabled: true
compliance_level: comprehensive
generate_isa_mappings: true
include_pcaob: true
framework: dual
analytical_procedures:
enabled: true
procedures_per_account: 5
variance_probability: 0.25
confirmations:
enabled: true
confirmation_count: 75
positive_response_rate: 0.90
exception_rate: 0.08
opinion:
enabled: true
generate_kam: true
average_kam_count: 4
sox:
enabled: true
generate_302_certifications: true
generate_404_assessments: true
material_weakness_rate: 0.03
pcaob:
enabled: true
is_pcaob_audit: true
include_icfr_opinion: true
generate_audit_trail: true
"#;
let config: AuditStandardsConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
assert!(config.enabled);
assert!(config.isa_compliance.enabled);
assert_eq!(config.isa_compliance.compliance_level, "comprehensive");
assert!(config.isa_compliance.include_pcaob);
assert_eq!(config.isa_compliance.framework, "dual");
assert!(config.analytical_procedures.enabled);
assert_eq!(config.analytical_procedures.procedures_per_account, 5);
assert!(config.confirmations.enabled);
assert_eq!(config.confirmations.confirmation_count, 75);
assert!(config.opinion.enabled);
assert_eq!(config.opinion.average_kam_count, 4);
assert!(config.sox.enabled);
assert!(config.sox.generate_302_certifications);
assert_eq!(config.sox.material_weakness_rate, 0.03);
assert!(config.pcaob.enabled);
assert!(config.pcaob.is_pcaob_audit);
assert!(config.pcaob.include_icfr_opinion);
assert!(config.generate_audit_trail);
}
#[test]
fn test_isa_compliance_config_defaults() {
let config = IsaComplianceConfig::default();
assert!(!config.enabled);
assert_eq!(config.compliance_level, "standard");
assert!(config.generate_isa_mappings);
assert!(config.generate_coverage_summary);
assert!(!config.include_pcaob);
assert_eq!(config.framework, "isa");
}
#[test]
fn test_sox_compliance_config_defaults() {
let config = SoxComplianceConfig::default();
assert!(!config.enabled);
assert!(config.generate_302_certifications);
assert!(config.generate_404_assessments);
assert_eq!(config.materiality_threshold, 10000.0);
assert_eq!(config.material_weakness_rate, 0.02);
assert_eq!(config.significant_deficiency_rate, 0.08);
}
#[test]
fn test_pcaob_config_defaults() {
let config = PcaobConfig::default();
assert!(!config.enabled);
assert!(!config.is_pcaob_audit);
assert!(config.generate_cam);
assert!(!config.include_icfr_opinion);
assert!(!config.generate_standard_mappings);
}
#[test]
fn test_config_with_standards_enabled() {
let yaml = r#"
global:
industry: financial_services
start_date: "2024-01-01"
period_months: 12
companies:
- code: "BANK"
name: "Test Bank"
currency: "USD"
country: "US"
annual_transaction_volume: hundred_k
chart_of_accounts:
complexity: large
output:
output_directory: "./output"
accounting_standards:
enabled: true
framework: us_gaap
revenue_recognition:
enabled: true
leases:
enabled: true
audit_standards:
enabled: true
isa_compliance:
enabled: true
sox:
enabled: true
"#;
let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
assert!(config.accounting_standards.enabled);
assert!(matches!(
config.accounting_standards.framework,
Some(AccountingFrameworkConfig::UsGaap)
));
assert!(config.accounting_standards.revenue_recognition.enabled);
assert!(config.accounting_standards.leases.enabled);
assert!(config.audit_standards.enabled);
assert!(config.audit_standards.isa_compliance.enabled);
assert!(config.audit_standards.sox.enabled);
}
#[test]
fn test_industry_specific_config_defaults() {
let config = IndustrySpecificConfig::default();
assert!(!config.enabled);
assert!(!config.manufacturing.enabled);
assert!(!config.retail.enabled);
assert!(!config.healthcare.enabled);
assert!(!config.technology.enabled);
assert!(!config.financial_services.enabled);
assert!(!config.professional_services.enabled);
}
#[test]
fn test_manufacturing_config_defaults() {
let config = ManufacturingConfig::default();
assert!(!config.enabled);
assert_eq!(config.bom_depth, 4);
assert!(!config.just_in_time);
assert_eq!(config.supplier_tiers, 2);
assert_eq!(config.target_yield_rate, 0.97);
assert_eq!(config.scrap_alert_threshold, 0.03);
}
#[test]
fn test_retail_config_defaults() {
let config = RetailConfig::default();
assert!(!config.enabled);
assert_eq!(config.avg_daily_transactions, 500);
assert!(config.loss_prevention);
assert_eq!(config.shrinkage_rate, 0.015);
}
#[test]
fn test_healthcare_config_defaults() {
let config = HealthcareConfig::default();
assert!(!config.enabled);
assert_eq!(config.facility_type, "hospital");
assert_eq!(config.avg_daily_encounters, 150);
assert!(config.compliance.hipaa);
assert!(config.compliance.stark_law);
assert!(config.coding_systems.icd10);
assert!(config.coding_systems.cpt);
}
#[test]
fn test_technology_config_defaults() {
let config = TechnologyConfig::default();
assert!(!config.enabled);
assert_eq!(config.revenue_model, "saas");
assert_eq!(config.subscription_revenue_pct, 0.60);
assert!(config.rd_capitalization.enabled);
}
#[test]
fn test_config_with_industry_specific() {
let yaml = r#"
global:
industry: healthcare
start_date: "2024-01-01"
period_months: 12
companies:
- code: "HOSP"
name: "Test Hospital"
currency: "USD"
country: "US"
annual_transaction_volume: hundred_k
chart_of_accounts:
complexity: medium
output:
output_directory: "./output"
industry_specific:
enabled: true
healthcare:
enabled: true
facility_type: hospital
payer_mix:
medicare: 0.45
medicaid: 0.15
commercial: 0.35
self_pay: 0.05
coding_systems:
icd10: true
cpt: true
drg: true
compliance:
hipaa: true
stark_law: true
anomaly_rates:
upcoding: 0.03
unbundling: 0.02
"#;
let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
assert!(config.industry_specific.enabled);
assert!(config.industry_specific.healthcare.enabled);
assert_eq!(
config.industry_specific.healthcare.facility_type,
"hospital"
);
assert_eq!(config.industry_specific.healthcare.payer_mix.medicare, 0.45);
assert_eq!(config.industry_specific.healthcare.payer_mix.self_pay, 0.05);
assert!(config.industry_specific.healthcare.coding_systems.icd10);
assert!(config.industry_specific.healthcare.compliance.hipaa);
assert_eq!(
config.industry_specific.healthcare.anomaly_rates.upcoding,
0.03
);
}
#[test]
fn test_config_with_manufacturing_specific() {
let yaml = r#"
global:
industry: manufacturing
start_date: "2024-01-01"
period_months: 12
companies:
- code: "MFG"
name: "Test Manufacturing"
currency: "USD"
country: "US"
annual_transaction_volume: hundred_k
chart_of_accounts:
complexity: medium
output:
output_directory: "./output"
industry_specific:
enabled: true
manufacturing:
enabled: true
bom_depth: 5
just_in_time: true
supplier_tiers: 3
target_yield_rate: 0.98
anomaly_rates:
yield_manipulation: 0.02
phantom_production: 0.01
"#;
let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
assert!(config.industry_specific.enabled);
assert!(config.industry_specific.manufacturing.enabled);
assert_eq!(config.industry_specific.manufacturing.bom_depth, 5);
assert!(config.industry_specific.manufacturing.just_in_time);
assert_eq!(config.industry_specific.manufacturing.supplier_tiers, 3);
assert_eq!(
config.industry_specific.manufacturing.target_yield_rate,
0.98
);
assert_eq!(
config
.industry_specific
.manufacturing
.anomaly_rates
.yield_manipulation,
0.02
);
}
#[test]
fn test_tax_config_defaults() {
let tax = TaxConfig::default();
assert!(!tax.enabled);
assert!(tax.jurisdictions.countries.is_empty());
assert!(!tax.jurisdictions.include_subnational);
assert!(!tax.vat_gst.enabled);
assert!(tax.vat_gst.standard_rates.is_empty());
assert!(tax.vat_gst.reduced_rates.is_empty());
assert!(tax.vat_gst.exempt_categories.is_empty());
assert!(tax.vat_gst.reverse_charge);
assert!(!tax.sales_tax.enabled);
assert!(tax.sales_tax.nexus_states.is_empty());
assert!(!tax.withholding.enabled);
assert!(tax.withholding.treaty_network);
assert_eq!(tax.withholding.default_rate, 0.30);
assert_eq!(tax.withholding.treaty_reduced_rate, 0.15);
assert!(tax.provisions.enabled);
assert_eq!(tax.provisions.statutory_rate, 0.21);
assert!(tax.provisions.uncertain_positions);
assert!(!tax.payroll_tax.enabled);
assert_eq!(tax.anomaly_rate, 0.03);
}
#[test]
fn test_tax_config_from_yaml() {
let yaml = r#"
global:
seed: 42
start_date: "2024-01-01"
period_months: 12
industry: retail
companies:
- code: C001
name: Test Corp
currency: USD
country: US
annual_transaction_volume: ten_k
chart_of_accounts:
complexity: small
output:
output_directory: ./output
tax:
enabled: true
anomaly_rate: 0.05
jurisdictions:
countries: ["US", "DE", "GB"]
include_subnational: true
vat_gst:
enabled: true
standard_rates:
DE: 0.19
GB: 0.20
reduced_rates:
DE: 0.07
GB: 0.05
exempt_categories:
- financial_services
- healthcare
reverse_charge: false
sales_tax:
enabled: true
nexus_states: ["CA", "NY", "TX"]
withholding:
enabled: true
treaty_network: false
default_rate: 0.25
treaty_reduced_rate: 0.10
provisions:
enabled: false
statutory_rate: 0.28
uncertain_positions: false
payroll_tax:
enabled: true
"#;
let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
assert!(config.tax.enabled);
assert_eq!(config.tax.anomaly_rate, 0.05);
assert_eq!(config.tax.jurisdictions.countries.len(), 3);
assert!(config
.tax
.jurisdictions
.countries
.contains(&"DE".to_string()));
assert!(config.tax.jurisdictions.include_subnational);
assert!(config.tax.vat_gst.enabled);
assert_eq!(config.tax.vat_gst.standard_rates.get("DE"), Some(&0.19));
assert_eq!(config.tax.vat_gst.standard_rates.get("GB"), Some(&0.20));
assert_eq!(config.tax.vat_gst.reduced_rates.get("DE"), Some(&0.07));
assert_eq!(config.tax.vat_gst.exempt_categories.len(), 2);
assert!(!config.tax.vat_gst.reverse_charge);
assert!(config.tax.sales_tax.enabled);
assert_eq!(config.tax.sales_tax.nexus_states.len(), 3);
assert!(config
.tax
.sales_tax
.nexus_states
.contains(&"CA".to_string()));
assert!(config.tax.withholding.enabled);
assert!(!config.tax.withholding.treaty_network);
assert_eq!(config.tax.withholding.default_rate, 0.25);
assert_eq!(config.tax.withholding.treaty_reduced_rate, 0.10);
assert!(!config.tax.provisions.enabled);
assert_eq!(config.tax.provisions.statutory_rate, 0.28);
assert!(!config.tax.provisions.uncertain_positions);
assert!(config.tax.payroll_tax.enabled);
}
#[test]
fn test_generator_config_with_tax_default() {
let yaml = r#"
global:
seed: 42
start_date: "2024-01-01"
period_months: 12
industry: retail
companies:
- code: C001
name: Test Corp
currency: USD
country: US
annual_transaction_volume: ten_k
chart_of_accounts:
complexity: small
output:
output_directory: ./output
"#;
let config: GeneratorConfig =
serde_yaml::from_str(yaml).expect("Failed to parse config without tax section");
assert!(!config.tax.enabled);
assert!(config.tax.jurisdictions.countries.is_empty());
assert_eq!(config.tax.anomaly_rate, 0.03);
assert!(config.tax.provisions.enabled); assert_eq!(config.tax.provisions.statutory_rate, 0.21);
}
#[test]
fn test_session_config_default_disabled() {
let yaml = "{}";
let config: SessionSchemaConfig =
serde_yaml::from_str(yaml).expect("Failed to parse empty session config");
assert!(!config.enabled);
assert!(config.checkpoint_path.is_none());
assert!(config.per_period_output);
assert!(config.consolidated_output);
}
#[test]
fn test_config_backward_compatible_without_session() {
let yaml = r#"
global:
seed: 42
start_date: "2024-01-01"
period_months: 12
industry: retail
companies:
- code: C001
name: Test Corp
currency: USD
country: US
annual_transaction_volume: ten_k
chart_of_accounts:
complexity: small
output:
output_directory: ./output
"#;
let config: GeneratorConfig =
serde_yaml::from_str(yaml).expect("Failed to parse config without session");
assert!(!config.session.enabled);
assert!(config.session.per_period_output);
assert!(config.session.consolidated_output);
assert!(config.global.fiscal_year_months.is_none());
}
#[test]
fn test_fiscal_year_months_parsed() {
let yaml = r#"
global:
seed: 42
start_date: "2024-01-01"
period_months: 24
industry: retail
fiscal_year_months: 12
companies:
- code: C001
name: Test Corp
currency: USD
country: US
annual_transaction_volume: ten_k
chart_of_accounts:
complexity: small
output:
output_directory: ./output
session:
enabled: true
checkpoint_path: /tmp/checkpoints
per_period_output: true
consolidated_output: false
"#;
let config: GeneratorConfig =
serde_yaml::from_str(yaml).expect("Failed to parse config with fiscal_year_months");
assert_eq!(config.global.fiscal_year_months, Some(12));
assert!(config.session.enabled);
assert_eq!(
config.session.checkpoint_path,
Some("/tmp/checkpoints".to_string())
);
assert!(config.session.per_period_output);
assert!(!config.session.consolidated_output);
}
}