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