Skip to main content

datasynth_eval/coherence/
mod.rs

1//! Semantic coherence evaluation module.
2//!
3//! Validates that generated data maintains accounting coherence including
4//! balance sheet equations, subledger reconciliation, and document chain integrity.
5
6mod audit;
7mod balance;
8mod bank_reconciliation;
9mod cross_process;
10mod document_chain;
11mod financial_reporting;
12mod hr_payroll;
13mod intercompany;
14mod manufacturing;
15mod multi_table;
16mod network;
17mod referential;
18mod sourcing;
19mod standards;
20mod subledger;
21
22pub use audit::{
23    AuditEvaluation, AuditEvaluator, AuditFindingData, AuditRiskData, AuditThresholds,
24    MaterialityData, WorkpaperData,
25};
26pub use balance::{AccountType, BalanceSheetEvaluation, BalanceSheetEvaluator, BalanceSnapshot};
27pub use bank_reconciliation::{
28    BankReconciliationEvaluation, BankReconciliationEvaluator, BankReconciliationThresholds,
29    ReconciliationData,
30};
31pub use cross_process::{
32    CrossProcessEvaluation, CrossProcessEvaluator, CrossProcessLinkData, CrossProcessThresholds,
33};
34pub use document_chain::{
35    DocumentChainEvaluation, DocumentChainEvaluator, DocumentReferenceData, O2CChainData,
36    P2PChainData,
37};
38pub use financial_reporting::{
39    BudgetVarianceData, FinancialReportingEvaluation, FinancialReportingEvaluator,
40    FinancialReportingThresholds, FinancialStatementData, KpiData,
41};
42pub use hr_payroll::{
43    ExpenseReportData, HrPayrollEvaluation, HrPayrollEvaluator, HrPayrollThresholds,
44    PayrollHoursData, PayrollLineItemData, PayrollRunData, TimeEntryData,
45};
46pub use intercompany::{ICMatchingData, ICMatchingEvaluation, ICMatchingEvaluator};
47pub use manufacturing::{
48    CycleCountData, ManufacturingEvaluation, ManufacturingEvaluator, ManufacturingThresholds,
49    ProductionOrderData, QualityInspectionData, RoutingOperationData,
50};
51pub use multi_table::{
52    get_o2c_flow_relationships, get_p2p_flow_relationships, AnomalyRecord, CascadeAnomalyAnalysis,
53    CascadePath, ConsistencyViolation, MultiTableConsistencyEvaluator, MultiTableData,
54    MultiTableEvaluation, TableConsistencyResult, TableRecord, TableRelationship,
55    TableRelationshipDef, ViolationType,
56};
57pub use network::{
58    ConcentrationMetrics, NetworkEdge, NetworkEvaluation, NetworkEvaluator, NetworkNode,
59    NetworkThresholds, StrengthStats,
60};
61pub use referential::{
62    EntityReferenceData, ReferentialData, ReferentialIntegrityEvaluation,
63    ReferentialIntegrityEvaluator,
64};
65pub use sourcing::{
66    BidEvaluationData, ScorecardCoverageData, SourcingEvaluation, SourcingEvaluator,
67    SourcingProjectData, SourcingThresholds, SpendAnalysisData,
68};
69pub use standards::{
70    AuditTrailEvaluation, AuditTrailGap, FairValueEvaluation, FrameworkViolation,
71    ImpairmentEvaluation, IsaComplianceEvaluation, LeaseAccountingEvaluation,
72    LeaseAccountingEvaluator, LeaseEvaluation, PcaobComplianceEvaluation, PerformanceObligation,
73    RevenueContract, RevenueRecognitionEvaluation, RevenueRecognitionEvaluator,
74    SoxComplianceEvaluation, StandardsComplianceEvaluation, StandardsThresholds,
75    VariableConsideration, ViolationSeverity,
76};
77pub use subledger::{SubledgerEvaluator, SubledgerReconciliationEvaluation};
78
79use serde::{Deserialize, Serialize};
80
81/// Combined coherence evaluation results.
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct CoherenceEvaluation {
84    /// Balance sheet validation results.
85    pub balance: Option<BalanceSheetEvaluation>,
86    /// Subledger reconciliation results.
87    pub subledger: Option<SubledgerReconciliationEvaluation>,
88    /// Document chain completeness results.
89    pub document_chain: Option<DocumentChainEvaluation>,
90    /// Intercompany matching results.
91    pub intercompany: Option<ICMatchingEvaluation>,
92    /// Referential integrity results.
93    pub referential: Option<ReferentialIntegrityEvaluation>,
94    /// Multi-table consistency results.
95    pub multi_table: Option<MultiTableEvaluation>,
96    /// Accounting and audit standards compliance results.
97    pub standards: Option<StandardsComplianceEvaluation>,
98    /// Network/interconnectivity evaluation results.
99    pub network: Option<NetworkEvaluation>,
100    /// Financial reporting evaluation results.
101    #[serde(default, skip_serializing_if = "Option::is_none")]
102    pub financial_reporting: Option<FinancialReportingEvaluation>,
103    /// HR/payroll evaluation results.
104    #[serde(default, skip_serializing_if = "Option::is_none")]
105    pub hr_payroll: Option<HrPayrollEvaluation>,
106    /// Manufacturing evaluation results.
107    #[serde(default, skip_serializing_if = "Option::is_none")]
108    pub manufacturing: Option<ManufacturingEvaluation>,
109    /// Bank reconciliation evaluation results.
110    #[serde(default, skip_serializing_if = "Option::is_none")]
111    pub bank_reconciliation: Option<BankReconciliationEvaluation>,
112    /// Source-to-contract evaluation results.
113    #[serde(default, skip_serializing_if = "Option::is_none")]
114    pub sourcing: Option<SourcingEvaluation>,
115    /// Cross-process link evaluation results.
116    #[serde(default, skip_serializing_if = "Option::is_none")]
117    pub cross_process: Option<CrossProcessEvaluation>,
118    /// Audit evaluation results.
119    #[serde(default, skip_serializing_if = "Option::is_none")]
120    pub audit: Option<AuditEvaluation>,
121    /// Overall pass/fail status.
122    pub passes: bool,
123    /// Summary of failed checks.
124    pub failures: Vec<String>,
125}
126
127impl CoherenceEvaluation {
128    /// Create a new empty evaluation.
129    pub fn new() -> Self {
130        Self {
131            balance: None,
132            subledger: None,
133            document_chain: None,
134            intercompany: None,
135            referential: None,
136            multi_table: None,
137            standards: None,
138            network: None,
139            financial_reporting: None,
140            hr_payroll: None,
141            manufacturing: None,
142            bank_reconciliation: None,
143            sourcing: None,
144            cross_process: None,
145            audit: None,
146            passes: true,
147            failures: Vec::new(),
148        }
149    }
150
151    /// Check all results against thresholds and update pass status.
152    pub fn check_thresholds(&mut self, thresholds: &crate::config::EvaluationThresholds) {
153        self.failures.clear();
154
155        if let Some(ref balance) = self.balance {
156            if !balance.equation_balanced {
157                self.failures.push(format!(
158                    "Balance sheet equation not balanced (max imbalance: {})",
159                    balance.max_imbalance
160                ));
161            }
162        }
163
164        if let Some(ref subledger) = self.subledger {
165            if subledger.completeness_score < thresholds.subledger_reconciliation_rate_min {
166                self.failures.push(format!(
167                    "Subledger reconciliation {} < {} (threshold)",
168                    subledger.completeness_score, thresholds.subledger_reconciliation_rate_min
169                ));
170            }
171        }
172
173        if let Some(ref doc_chain) = self.document_chain {
174            let min_rate = thresholds.document_chain_completion_min;
175            if doc_chain.p2p_completion_rate < min_rate {
176                self.failures.push(format!(
177                    "P2P chain completion {} < {} (threshold)",
178                    doc_chain.p2p_completion_rate, min_rate
179                ));
180            }
181            if doc_chain.o2c_completion_rate < min_rate {
182                self.failures.push(format!(
183                    "O2C chain completion {} < {} (threshold)",
184                    doc_chain.o2c_completion_rate, min_rate
185                ));
186            }
187        }
188
189        if let Some(ref ic) = self.intercompany {
190            if ic.match_rate < thresholds.ic_match_rate_min {
191                self.failures.push(format!(
192                    "IC match rate {} < {} (threshold)",
193                    ic.match_rate, thresholds.ic_match_rate_min
194                ));
195            }
196        }
197
198        if let Some(ref referential) = self.referential {
199            if referential.overall_integrity_score < thresholds.referential_integrity_min {
200                self.failures.push(format!(
201                    "Referential integrity {} < {} (threshold)",
202                    referential.overall_integrity_score, thresholds.referential_integrity_min
203                ));
204            }
205        }
206
207        if let Some(ref multi_table) = self.multi_table {
208            if multi_table.overall_consistency_score < thresholds.referential_integrity_min {
209                self.failures.push(format!(
210                    "Multi-table consistency {} < {} (threshold)",
211                    multi_table.overall_consistency_score, thresholds.referential_integrity_min
212                ));
213            }
214            self.failures.extend(multi_table.issues.clone());
215        }
216
217        if let Some(ref mut standards_eval) = self.standards.clone() {
218            let standards_thresholds = StandardsThresholds::default();
219            standards_eval.check_thresholds(&standards_thresholds);
220            self.failures.extend(standards_eval.failures.clone());
221        }
222
223        if let Some(ref network_eval) = self.network {
224            if !network_eval.passes {
225                self.failures.extend(network_eval.issues.clone());
226            }
227        }
228
229        // New evaluators: propagate issues
230        if let Some(ref eval) = self.financial_reporting {
231            if !eval.passes {
232                self.failures.extend(eval.issues.clone());
233            }
234        }
235        if let Some(ref eval) = self.hr_payroll {
236            if !eval.passes {
237                self.failures.extend(eval.issues.clone());
238            }
239        }
240        if let Some(ref eval) = self.manufacturing {
241            if !eval.passes {
242                self.failures.extend(eval.issues.clone());
243            }
244        }
245        if let Some(ref eval) = self.bank_reconciliation {
246            if !eval.passes {
247                self.failures.extend(eval.issues.clone());
248            }
249        }
250        if let Some(ref eval) = self.sourcing {
251            if !eval.passes {
252                self.failures.extend(eval.issues.clone());
253            }
254        }
255        if let Some(ref eval) = self.cross_process {
256            if !eval.passes {
257                self.failures.extend(eval.issues.clone());
258            }
259        }
260        if let Some(ref eval) = self.audit {
261            if !eval.passes {
262                self.failures.extend(eval.issues.clone());
263            }
264        }
265
266        self.passes = self.failures.is_empty();
267    }
268}
269
270impl Default for CoherenceEvaluation {
271    fn default() -> Self {
272        Self::new()
273    }
274}