1pub 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#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct CoherenceEvaluation {
131 pub balance: Option<BalanceSheetEvaluation>,
133 pub subledger: Option<SubledgerReconciliationEvaluation>,
135 pub document_chain: Option<DocumentChainEvaluation>,
137 pub intercompany: Option<ICMatchingEvaluation>,
139 pub referential: Option<ReferentialIntegrityEvaluation>,
141 pub multi_table: Option<MultiTableEvaluation>,
143 pub standards: Option<StandardsComplianceEvaluation>,
145 pub network: Option<NetworkEvaluation>,
147 #[serde(default, skip_serializing_if = "Option::is_none")]
149 pub financial_reporting: Option<FinancialReportingEvaluation>,
150 #[serde(default, skip_serializing_if = "Option::is_none")]
152 pub hr_payroll: Option<HrPayrollEvaluation>,
153 #[serde(default, skip_serializing_if = "Option::is_none")]
155 pub manufacturing: Option<ManufacturingEvaluation>,
156 #[serde(default, skip_serializing_if = "Option::is_none")]
158 pub bank_reconciliation: Option<BankReconciliationEvaluation>,
159 #[serde(default, skip_serializing_if = "Option::is_none")]
161 pub sourcing: Option<SourcingEvaluation>,
162 #[serde(default, skip_serializing_if = "Option::is_none")]
164 pub cross_process: Option<CrossProcessEvaluation>,
165 #[serde(default, skip_serializing_if = "Option::is_none")]
167 pub audit: Option<AuditEvaluation>,
168 #[serde(default, skip_serializing_if = "Option::is_none")]
170 pub tax: Option<TaxEvaluation>,
171 #[serde(default, skip_serializing_if = "Option::is_none")]
173 pub treasury: Option<TreasuryEvaluation>,
174 #[serde(default, skip_serializing_if = "Option::is_none")]
176 pub project_accounting: Option<ProjectAccountingEvaluation>,
177 #[serde(default, skip_serializing_if = "Option::is_none")]
179 pub esg: Option<EsgEvaluation>,
180 #[serde(default, skip_serializing_if = "Option::is_none")]
182 pub sales_quotes: Option<SalesQuoteEvaluation>,
183 #[serde(default, skip_serializing_if = "Option::is_none")]
185 pub country_packs: Option<CountryPackEvaluation>,
186 #[serde(default, skip_serializing_if = "Option::is_none")]
188 pub multi_period: Option<MultiPeriodAnalysis>,
189 pub passes: bool,
191 pub failures: Vec<String>,
193}
194
195impl CoherenceEvaluation {
196 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 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 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}