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
6pub mod je_risk_scoring;
7pub mod ratio_analysis;
8pub mod sampling_validation;
9pub mod trend_analysis;
10
11mod audit;
12mod balance;
13mod bank_reconciliation;
14mod country_packs;
15mod cross_process;
16mod document_chain;
17mod esg;
18mod financial_reporting;
19mod fraud_packs;
20mod hr_payroll;
21mod intercompany;
22mod manufacturing;
23mod multi_period;
24mod multi_table;
25mod network;
26mod project_accounting;
27mod referential;
28mod sales_quotes;
29mod sourcing;
30mod standards;
31mod subledger;
32mod tax;
33mod treasury;
34
35pub use audit::{
36    AuditEvaluation, AuditEvaluator, AuditFindingData, AuditRiskData, AuditThresholds,
37    MaterialityData, WorkpaperData,
38};
39pub use balance::{AccountType, BalanceSheetEvaluation, BalanceSheetEvaluator, BalanceSnapshot};
40pub use bank_reconciliation::{
41    BankReconciliationEvaluation, BankReconciliationEvaluator, BankReconciliationThresholds,
42    ReconciliationData,
43};
44pub use country_packs::{
45    ApprovalLevelData, CountryPackData, CountryPackEvaluation, CountryPackEvaluator,
46    CountryPackThresholds, HolidayData, TaxRateData,
47};
48pub use cross_process::{
49    CrossProcessEvaluation, CrossProcessEvaluator, CrossProcessLinkData, CrossProcessThresholds,
50};
51pub use document_chain::{
52    DocumentChainEvaluation, DocumentChainEvaluator, DocumentReferenceData, O2CChainData,
53    P2PChainData,
54};
55pub use esg::{
56    EsgEvaluation, EsgEvaluator, EsgThresholds, GovernanceData, SafetyMetricData, SupplierEsgData,
57    WaterUsageData,
58};
59pub use financial_reporting::{
60    BudgetVarianceData, FinancialReportingEvaluation, FinancialReportingEvaluator,
61    FinancialReportingThresholds, FinancialStatementData, KpiData,
62};
63pub use fraud_packs::{FraudPackAnalysis, FraudPackAnalyzer, FraudPackData, FraudPackThresholds};
64pub use hr_payroll::{
65    ExpenseReportData, HrPayrollEvaluation, HrPayrollEvaluator, HrPayrollThresholds,
66    PayrollHoursData, PayrollLineItemData, PayrollRunData, TimeEntryData,
67};
68pub use intercompany::{
69    ICMatchingData, ICMatchingEvaluation, ICMatchingEvaluator, UnmatchedICItem,
70};
71pub use je_risk_scoring::{JeRiskScoringResult, RiskAttributeStats, RiskDistribution};
72pub use manufacturing::{
73    CycleCountData, ManufacturingEvaluation, ManufacturingEvaluator, ManufacturingThresholds,
74    ProductionOrderData, QualityInspectionData, RoutingOperationData,
75};
76pub use multi_period::{
77    MultiPeriodAnalysis, MultiPeriodAnalyzer, MultiPeriodThresholds, PeriodData,
78};
79pub use multi_table::{
80    get_o2c_flow_relationships, get_p2p_flow_relationships, AnomalyRecord, CascadeAnomalyAnalysis,
81    CascadePath, ConsistencyViolation, MultiTableConsistencyEvaluator, MultiTableData,
82    MultiTableEvaluation, TableConsistencyResult, TableRecord, TableRelationship,
83    TableRelationshipDef, ViolationType,
84};
85pub use network::{
86    ConcentrationMetrics, NetworkEdge, NetworkEvaluation, NetworkEvaluator, NetworkNode,
87    NetworkThresholds, StrengthStats,
88};
89pub use project_accounting::{
90    EarnedValueData, ProjectAccountingEvaluation, ProjectAccountingEvaluator,
91    ProjectAccountingThresholds, ProjectRevenueData, RetainageData,
92};
93pub use ratio_analysis::{FinancialRatios, RatioAnalysisResult, RatioCheck};
94pub use referential::{
95    EntityReferenceData, ReferentialData, ReferentialIntegrityEvaluation,
96    ReferentialIntegrityEvaluator,
97};
98pub use sales_quotes::{
99    QuoteLineData, SalesQuoteData, SalesQuoteEvaluation, SalesQuoteEvaluator, SalesQuoteThresholds,
100};
101pub use sampling_validation::{
102    validate_sampling, SamplingValidationResult, Stratum, StratumResult,
103};
104pub use sourcing::{
105    BidEvaluationData, ScorecardCoverageData, SourcingEvaluation, SourcingEvaluator,
106    SourcingProjectData, SourcingThresholds, SpendAnalysisData,
107};
108pub use standards::{
109    AuditTrailEvaluation, AuditTrailGap, FairValueEvaluation, FrameworkViolation,
110    ImpairmentEvaluation, IsaComplianceEvaluation, LeaseAccountingEvaluation,
111    LeaseAccountingEvaluator, LeaseEvaluation, PcaobComplianceEvaluation, PerformanceObligation,
112    RevenueContract, RevenueRecognitionEvaluation, RevenueRecognitionEvaluator,
113    SoxComplianceEvaluation, StandardsComplianceEvaluation, StandardsThresholds,
114    VariableConsideration, ViolationSeverity,
115};
116pub use subledger::{SubledgerEvaluator, SubledgerReconciliationEvaluation};
117pub use tax::{
118    TaxEvaluation, TaxEvaluator, TaxLineData, TaxReturnData, TaxThresholds, WithholdingData,
119};
120pub use treasury::{
121    CashPositionData, CovenantData, HedgeEffectivenessData, NettingData, TreasuryEvaluation,
122    TreasuryEvaluator, TreasuryThresholds,
123};
124pub use trend_analysis::{analyze_trends, TrendConsistencyCheck, TrendPlausibilityResult};
125
126use serde::{Deserialize, Serialize};
127
128/// Combined coherence evaluation results.
129#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct CoherenceEvaluation {
131    /// Balance sheet validation results.
132    pub balance: Option<BalanceSheetEvaluation>,
133    /// Subledger reconciliation results.
134    pub subledger: Option<SubledgerReconciliationEvaluation>,
135    /// Document chain completeness results.
136    pub document_chain: Option<DocumentChainEvaluation>,
137    /// Intercompany matching results.
138    pub intercompany: Option<ICMatchingEvaluation>,
139    /// Referential integrity results.
140    pub referential: Option<ReferentialIntegrityEvaluation>,
141    /// Multi-table consistency results.
142    pub multi_table: Option<MultiTableEvaluation>,
143    /// Accounting and audit standards compliance results.
144    pub standards: Option<StandardsComplianceEvaluation>,
145    /// Network/interconnectivity evaluation results.
146    pub network: Option<NetworkEvaluation>,
147    /// Financial reporting evaluation results.
148    #[serde(default, skip_serializing_if = "Option::is_none")]
149    pub financial_reporting: Option<FinancialReportingEvaluation>,
150    /// HR/payroll evaluation results.
151    #[serde(default, skip_serializing_if = "Option::is_none")]
152    pub hr_payroll: Option<HrPayrollEvaluation>,
153    /// Manufacturing evaluation results.
154    #[serde(default, skip_serializing_if = "Option::is_none")]
155    pub manufacturing: Option<ManufacturingEvaluation>,
156    /// Bank reconciliation evaluation results.
157    #[serde(default, skip_serializing_if = "Option::is_none")]
158    pub bank_reconciliation: Option<BankReconciliationEvaluation>,
159    /// Source-to-contract evaluation results.
160    #[serde(default, skip_serializing_if = "Option::is_none")]
161    pub sourcing: Option<SourcingEvaluation>,
162    /// Cross-process link evaluation results.
163    #[serde(default, skip_serializing_if = "Option::is_none")]
164    pub cross_process: Option<CrossProcessEvaluation>,
165    /// Audit evaluation results.
166    #[serde(default, skip_serializing_if = "Option::is_none")]
167    pub audit: Option<AuditEvaluation>,
168    /// Tax evaluation results.
169    #[serde(default, skip_serializing_if = "Option::is_none")]
170    pub tax: Option<TaxEvaluation>,
171    /// Treasury evaluation results.
172    #[serde(default, skip_serializing_if = "Option::is_none")]
173    pub treasury: Option<TreasuryEvaluation>,
174    /// Project accounting evaluation results.
175    #[serde(default, skip_serializing_if = "Option::is_none")]
176    pub project_accounting: Option<ProjectAccountingEvaluation>,
177    /// ESG evaluation results.
178    #[serde(default, skip_serializing_if = "Option::is_none")]
179    pub esg: Option<EsgEvaluation>,
180    /// Sales quote evaluation results.
181    #[serde(default, skip_serializing_if = "Option::is_none")]
182    pub sales_quotes: Option<SalesQuoteEvaluation>,
183    /// Country pack evaluation results.
184    #[serde(default, skip_serializing_if = "Option::is_none")]
185    pub country_packs: Option<CountryPackEvaluation>,
186    /// Multi-period coherence evaluation results.
187    #[serde(default, skip_serializing_if = "Option::is_none")]
188    pub multi_period: Option<MultiPeriodAnalysis>,
189    /// Overall pass/fail status.
190    pub passes: bool,
191    /// Summary of failed checks.
192    pub failures: Vec<String>,
193}
194
195impl CoherenceEvaluation {
196    /// Create a new empty evaluation.
197    pub fn new() -> Self {
198        Self {
199            balance: None,
200            subledger: None,
201            document_chain: None,
202            intercompany: None,
203            referential: None,
204            multi_table: None,
205            standards: None,
206            network: None,
207            financial_reporting: None,
208            hr_payroll: None,
209            manufacturing: None,
210            bank_reconciliation: None,
211            sourcing: None,
212            cross_process: None,
213            audit: None,
214            tax: None,
215            treasury: None,
216            project_accounting: None,
217            esg: None,
218            sales_quotes: None,
219            country_packs: None,
220            multi_period: None,
221            passes: true,
222            failures: Vec::new(),
223        }
224    }
225
226    /// Check all results against thresholds and update pass status.
227    pub fn check_thresholds(&mut self, thresholds: &crate::config::EvaluationThresholds) {
228        self.failures.clear();
229
230        if let Some(ref balance) = self.balance {
231            if !balance.equation_balanced {
232                self.failures.push(format!(
233                    "Balance sheet equation not balanced (max imbalance: {})",
234                    balance.max_imbalance
235                ));
236            }
237        }
238
239        if let Some(ref subledger) = self.subledger {
240            if subledger.completeness_score < thresholds.subledger_reconciliation_rate_min {
241                self.failures.push(format!(
242                    "Subledger reconciliation {} < {} (threshold)",
243                    subledger.completeness_score, thresholds.subledger_reconciliation_rate_min
244                ));
245            }
246        }
247
248        if let Some(ref doc_chain) = self.document_chain {
249            let min_rate = thresholds.document_chain_completion_min;
250            if doc_chain.p2p_completion_rate < min_rate {
251                self.failures.push(format!(
252                    "P2P chain completion {} < {} (threshold)",
253                    doc_chain.p2p_completion_rate, min_rate
254                ));
255            }
256            if doc_chain.o2c_completion_rate < min_rate {
257                self.failures.push(format!(
258                    "O2C chain completion {} < {} (threshold)",
259                    doc_chain.o2c_completion_rate, min_rate
260                ));
261            }
262        }
263
264        if let Some(ref ic) = self.intercompany {
265            if ic.match_rate < thresholds.ic_match_rate_min {
266                self.failures.push(format!(
267                    "IC match rate {} < {} (threshold)",
268                    ic.match_rate, thresholds.ic_match_rate_min
269                ));
270            }
271        }
272
273        if let Some(ref referential) = self.referential {
274            if referential.overall_integrity_score < thresholds.referential_integrity_min {
275                self.failures.push(format!(
276                    "Referential integrity {} < {} (threshold)",
277                    referential.overall_integrity_score, thresholds.referential_integrity_min
278                ));
279            }
280        }
281
282        if let Some(ref multi_table) = self.multi_table {
283            if multi_table.overall_consistency_score < thresholds.referential_integrity_min {
284                self.failures.push(format!(
285                    "Multi-table consistency {} < {} (threshold)",
286                    multi_table.overall_consistency_score, thresholds.referential_integrity_min
287                ));
288            }
289            self.failures.extend(multi_table.issues.clone());
290        }
291
292        if let Some(ref mut standards_eval) = self.standards.clone() {
293            let standards_thresholds = StandardsThresholds::default();
294            standards_eval.check_thresholds(&standards_thresholds);
295            self.failures.extend(standards_eval.failures.clone());
296        }
297
298        if let Some(ref network_eval) = self.network {
299            if !network_eval.passes {
300                self.failures.extend(network_eval.issues.clone());
301            }
302        }
303
304        // New evaluators: propagate issues
305        if let Some(ref eval) = self.financial_reporting {
306            if !eval.passes {
307                self.failures.extend(eval.issues.clone());
308            }
309        }
310        if let Some(ref eval) = self.hr_payroll {
311            if !eval.passes {
312                self.failures.extend(eval.issues.clone());
313            }
314        }
315        if let Some(ref eval) = self.manufacturing {
316            if !eval.passes {
317                self.failures.extend(eval.issues.clone());
318            }
319        }
320        if let Some(ref eval) = self.bank_reconciliation {
321            if !eval.passes {
322                self.failures.extend(eval.issues.clone());
323            }
324        }
325        if let Some(ref eval) = self.sourcing {
326            if !eval.passes {
327                self.failures.extend(eval.issues.clone());
328            }
329        }
330        if let Some(ref eval) = self.cross_process {
331            if !eval.passes {
332                self.failures.extend(eval.issues.clone());
333            }
334        }
335        if let Some(ref eval) = self.audit {
336            if !eval.passes {
337                self.failures.extend(eval.issues.clone());
338            }
339        }
340        if let Some(ref eval) = self.tax {
341            if !eval.passes {
342                self.failures.extend(eval.issues.clone());
343            }
344        }
345        if let Some(ref eval) = self.treasury {
346            if !eval.passes {
347                self.failures.extend(eval.issues.clone());
348            }
349        }
350        if let Some(ref eval) = self.project_accounting {
351            if !eval.passes {
352                self.failures.extend(eval.issues.clone());
353            }
354        }
355        if let Some(ref eval) = self.esg {
356            if !eval.passes {
357                self.failures.extend(eval.issues.clone());
358            }
359        }
360        if let Some(ref eval) = self.sales_quotes {
361            if !eval.passes {
362                self.failures.extend(eval.issues.clone());
363            }
364        }
365        if let Some(ref eval) = self.country_packs {
366            if !eval.passes {
367                self.failures.extend(eval.issues.clone());
368            }
369        }
370        if let Some(ref eval) = self.multi_period {
371            if !eval.passes {
372                self.failures.extend(eval.issues.clone());
373            }
374        }
375
376        self.passes = self.failures.is_empty();
377    }
378}
379
380impl Default for CoherenceEvaluation {
381    fn default() -> Self {
382        Self::new()
383    }
384}