datasynth_eval/coherence/
mod.rs1mod balance;
7mod document_chain;
8mod intercompany;
9mod referential;
10mod subledger;
11
12pub use balance::{BalanceSheetEvaluation, BalanceSheetEvaluator};
13pub use document_chain::{DocumentChainEvaluation, DocumentChainEvaluator};
14pub use intercompany::{ICMatchingEvaluation, ICMatchingEvaluator};
15pub use referential::{ReferentialIntegrityEvaluation, ReferentialIntegrityEvaluator};
16pub use subledger::{SubledgerEvaluator, SubledgerReconciliationEvaluation};
17
18use serde::{Deserialize, Serialize};
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct CoherenceEvaluation {
23 pub balance: Option<BalanceSheetEvaluation>,
25 pub subledger: Option<SubledgerReconciliationEvaluation>,
27 pub document_chain: Option<DocumentChainEvaluation>,
29 pub intercompany: Option<ICMatchingEvaluation>,
31 pub referential: Option<ReferentialIntegrityEvaluation>,
33 pub passes: bool,
35 pub failures: Vec<String>,
37}
38
39impl CoherenceEvaluation {
40 pub fn new() -> Self {
42 Self {
43 balance: None,
44 subledger: None,
45 document_chain: None,
46 intercompany: None,
47 referential: None,
48 passes: true,
49 failures: Vec::new(),
50 }
51 }
52
53 pub fn check_thresholds(&mut self, thresholds: &crate::config::EvaluationThresholds) {
55 self.failures.clear();
56
57 if let Some(ref balance) = self.balance {
58 if !balance.equation_balanced {
59 self.failures.push(format!(
60 "Balance sheet equation not balanced (max imbalance: {})",
61 balance.max_imbalance
62 ));
63 }
64 }
65
66 if let Some(ref subledger) = self.subledger {
67 if subledger.completeness_score < thresholds.subledger_reconciliation_rate_min {
68 self.failures.push(format!(
69 "Subledger reconciliation {} < {} (threshold)",
70 subledger.completeness_score, thresholds.subledger_reconciliation_rate_min
71 ));
72 }
73 }
74
75 if let Some(ref doc_chain) = self.document_chain {
76 let min_rate = thresholds.document_chain_completion_min;
77 if doc_chain.p2p_completion_rate < min_rate {
78 self.failures.push(format!(
79 "P2P chain completion {} < {} (threshold)",
80 doc_chain.p2p_completion_rate, min_rate
81 ));
82 }
83 if doc_chain.o2c_completion_rate < min_rate {
84 self.failures.push(format!(
85 "O2C chain completion {} < {} (threshold)",
86 doc_chain.o2c_completion_rate, min_rate
87 ));
88 }
89 }
90
91 if let Some(ref ic) = self.intercompany {
92 if ic.match_rate < thresholds.ic_match_rate_min {
93 self.failures.push(format!(
94 "IC match rate {} < {} (threshold)",
95 ic.match_rate, thresholds.ic_match_rate_min
96 ));
97 }
98 }
99
100 if let Some(ref referential) = self.referential {
101 if referential.overall_integrity_score < thresholds.referential_integrity_min {
102 self.failures.push(format!(
103 "Referential integrity {} < {} (threshold)",
104 referential.overall_integrity_score, thresholds.referential_integrity_min
105 ));
106 }
107 }
108
109 self.passes = self.failures.is_empty();
110 }
111}
112
113impl Default for CoherenceEvaluation {
114 fn default() -> Self {
115 Self::new()
116 }
117}