datasynth_eval/coherence/
document_chain.rs1use crate::error::EvalResult;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct DocumentChainEvaluation {
12 pub p2p_total: usize,
14 pub p2p_complete: usize,
16 pub p2p_completion_rate: f64,
18 pub p2p_po_created: usize,
20 pub p2p_gr_created: usize,
22 pub p2p_invoice_created: usize,
24 pub p2p_payment_created: usize,
26 pub p2p_three_way_match_passed: usize,
28 pub p2p_three_way_match_rate: f64,
30 pub o2c_total: usize,
32 pub o2c_complete: usize,
34 pub o2c_completion_rate: f64,
36 pub o2c_so_created: usize,
38 pub o2c_delivery_created: usize,
40 pub o2c_invoice_created: usize,
42 pub o2c_receipt_created: usize,
44 pub o2c_credit_check_passed: usize,
46 pub o2c_credit_check_rate: f64,
48 pub orphan_documents: usize,
50 pub broken_references: usize,
52 pub reference_integrity_score: f64,
54}
55
56#[derive(Debug, Clone)]
58pub struct P2PChainData {
59 pub is_complete: bool,
61 pub has_po: bool,
63 pub has_gr: bool,
65 pub has_invoice: bool,
67 pub has_payment: bool,
69 pub three_way_match_passed: bool,
71}
72
73#[derive(Debug, Clone)]
75pub struct O2CChainData {
76 pub is_complete: bool,
78 pub has_so: bool,
80 pub has_delivery: bool,
82 pub has_invoice: bool,
84 pub has_receipt: bool,
86 pub credit_check_passed: bool,
88}
89
90#[derive(Debug, Clone)]
92pub struct DocumentReferenceData {
93 pub total_references: usize,
95 pub valid_references: usize,
97 pub orphan_count: usize,
99}
100
101pub struct DocumentChainEvaluator;
103
104impl DocumentChainEvaluator {
105 pub fn new() -> Self {
107 Self
108 }
109
110 pub fn evaluate(
112 &self,
113 p2p_chains: &[P2PChainData],
114 o2c_chains: &[O2CChainData],
115 references: &DocumentReferenceData,
116 ) -> EvalResult<DocumentChainEvaluation> {
117 let p2p_total = p2p_chains.len();
119 let p2p_complete = p2p_chains.iter().filter(|c| c.is_complete).count();
120 let p2p_completion_rate = if p2p_total > 0 {
121 p2p_complete as f64 / p2p_total as f64
122 } else {
123 1.0
124 };
125
126 let p2p_po_created = p2p_chains.iter().filter(|c| c.has_po).count();
127 let p2p_gr_created = p2p_chains.iter().filter(|c| c.has_gr).count();
128 let p2p_invoice_created = p2p_chains.iter().filter(|c| c.has_invoice).count();
129 let p2p_payment_created = p2p_chains.iter().filter(|c| c.has_payment).count();
130 let p2p_three_way_match_passed = p2p_chains
131 .iter()
132 .filter(|c| c.three_way_match_passed)
133 .count();
134 let p2p_three_way_match_rate = if p2p_total > 0 {
135 p2p_three_way_match_passed as f64 / p2p_total as f64
136 } else {
137 1.0
138 };
139
140 let o2c_total = o2c_chains.len();
142 let o2c_complete = o2c_chains.iter().filter(|c| c.is_complete).count();
143 let o2c_completion_rate = if o2c_total > 0 {
144 o2c_complete as f64 / o2c_total as f64
145 } else {
146 1.0
147 };
148
149 let o2c_so_created = o2c_chains.iter().filter(|c| c.has_so).count();
150 let o2c_delivery_created = o2c_chains.iter().filter(|c| c.has_delivery).count();
151 let o2c_invoice_created = o2c_chains.iter().filter(|c| c.has_invoice).count();
152 let o2c_receipt_created = o2c_chains.iter().filter(|c| c.has_receipt).count();
153 let o2c_credit_check_passed = o2c_chains.iter().filter(|c| c.credit_check_passed).count();
154 let o2c_credit_check_rate = if o2c_total > 0 {
155 o2c_credit_check_passed as f64 / o2c_total as f64
156 } else {
157 1.0
158 };
159
160 let broken_references = references.total_references - references.valid_references;
162 let reference_integrity_score = if references.total_references > 0 {
163 references.valid_references as f64 / references.total_references as f64
164 } else {
165 1.0
166 };
167
168 Ok(DocumentChainEvaluation {
169 p2p_total,
170 p2p_complete,
171 p2p_completion_rate,
172 p2p_po_created,
173 p2p_gr_created,
174 p2p_invoice_created,
175 p2p_payment_created,
176 p2p_three_way_match_passed,
177 p2p_three_way_match_rate,
178 o2c_total,
179 o2c_complete,
180 o2c_completion_rate,
181 o2c_so_created,
182 o2c_delivery_created,
183 o2c_invoice_created,
184 o2c_receipt_created,
185 o2c_credit_check_passed,
186 o2c_credit_check_rate,
187 orphan_documents: references.orphan_count,
188 broken_references,
189 reference_integrity_score,
190 })
191 }
192}
193
194impl Default for DocumentChainEvaluator {
195 fn default() -> Self {
196 Self::new()
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn test_complete_p2p_chains() {
206 let p2p_chains = vec![
207 P2PChainData {
208 is_complete: true,
209 has_po: true,
210 has_gr: true,
211 has_invoice: true,
212 has_payment: true,
213 three_way_match_passed: true,
214 },
215 P2PChainData {
216 is_complete: true,
217 has_po: true,
218 has_gr: true,
219 has_invoice: true,
220 has_payment: true,
221 three_way_match_passed: true,
222 },
223 ];
224
225 let o2c_chains = vec![];
226 let references = DocumentReferenceData {
227 total_references: 10,
228 valid_references: 10,
229 orphan_count: 0,
230 };
231
232 let evaluator = DocumentChainEvaluator::new();
233 let result = evaluator
234 .evaluate(&p2p_chains, &o2c_chains, &references)
235 .unwrap();
236
237 assert_eq!(result.p2p_total, 2);
238 assert_eq!(result.p2p_complete, 2);
239 assert_eq!(result.p2p_completion_rate, 1.0);
240 assert_eq!(result.p2p_three_way_match_rate, 1.0);
241 }
242
243 #[test]
244 fn test_incomplete_chains() {
245 let p2p_chains = vec![P2PChainData {
246 is_complete: false,
247 has_po: true,
248 has_gr: true,
249 has_invoice: false,
250 has_payment: false,
251 three_way_match_passed: false,
252 }];
253
254 let o2c_chains = vec![];
255 let references = DocumentReferenceData {
256 total_references: 5,
257 valid_references: 4,
258 orphan_count: 1,
259 };
260
261 let evaluator = DocumentChainEvaluator::new();
262 let result = evaluator
263 .evaluate(&p2p_chains, &o2c_chains, &references)
264 .unwrap();
265
266 assert_eq!(result.p2p_completion_rate, 0.0);
267 assert_eq!(result.broken_references, 1);
268 assert_eq!(result.reference_integrity_score, 0.8);
269 }
270}