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