automapper_validation/generated/fv2504/aperak_conditions_fv2504.rs
1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2504/APERAK_AHB_2_4a_Fehlerkorrektur_20250331.xml
4// Generated: 2026-03-12T10:19:40Z
5// </auto-generated>
6
7#[allow(unused_imports)]
8use crate::eval::format_validators::*;
9use crate::eval::{ConditionEvaluator, ConditionResult, EvaluationContext};
10
11/// Generated condition evaluator for APERAK FV2504.
12pub struct AperakConditionEvaluatorFV2504 {
13 // External condition IDs that require runtime context.
14 external_conditions: std::collections::HashSet<u32>,
15}
16
17impl Default for AperakConditionEvaluatorFV2504 {
18 fn default() -> Self {
19 let mut external_conditions = std::collections::HashSet::new();
20 external_conditions.insert(3);
21 external_conditions.insert(6);
22 Self {
23 external_conditions,
24 }
25 }
26}
27
28impl ConditionEvaluator for AperakConditionEvaluatorFV2504 {
29 fn message_type(&self) -> &str {
30 "APERAK"
31 }
32
33 fn format_version(&self) -> &str {
34 "FV2504"
35 }
36
37 fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
38 match condition {
39 1 => self.evaluate_1(ctx),
40 2 => self.evaluate_2(ctx),
41 3 => self.evaluate_3(ctx),
42 4 => self.evaluate_4(ctx),
43 5 => self.evaluate_5(ctx),
44 6 => self.evaluate_6(ctx),
45 7 => self.evaluate_7(ctx),
46 8 => self.evaluate_8(ctx),
47 9 => self.evaluate_9(ctx),
48 10 => self.evaluate_10(ctx),
49 11 => self.evaluate_11(ctx),
50 12 => self.evaluate_12(ctx),
51 13 => self.evaluate_13(ctx),
52 14 => self.evaluate_14(ctx),
53 15 => self.evaluate_15(ctx),
54 16 => self.evaluate_16(ctx),
55 494 => self.evaluate_494(ctx),
56 500 => self.evaluate_500(ctx),
57 501 => self.evaluate_501(ctx),
58 502 => self.evaluate_502(ctx),
59 931 => self.evaluate_931(ctx),
60 939 => self.evaluate_939(ctx),
61 940 => self.evaluate_940(ctx),
62 _ => ConditionResult::Unknown,
63 }
64 }
65
66 fn is_external(&self, condition: u32) -> bool {
67 self.external_conditions.contains(&condition)
68 }
69 fn is_known(&self, condition: u32) -> bool {
70 matches!(
71 condition,
72 1 | 2
73 | 3
74 | 4
75 | 5
76 | 6
77 | 7
78 | 8
79 | 9
80 | 10
81 | 11
82 | 12
83 | 13
84 | 14
85 | 15
86 | 16
87 | 494
88 | 500
89 | 501
90 | 502
91 | 931
92 | 939
93 | 940
94 )
95 }
96}
97
98impl AperakConditionEvaluatorFV2504 {
99 /// [3] Wenn für weitere Fehlerangabe benötigt.
100 /// EXTERNAL: Requires context from outside the message.
101 fn evaluate_3(&self, ctx: &EvaluationContext) -> ConditionResult {
102 ctx.external.evaluate("additional_error_detail_needed")
103 }
104
105 /// [6] Wenn Fehler innerhalb der Vorgangsebene von IFTSTA, INSRPT, UTILMD oder UTILTS vorhanden.
106 /// EXTERNAL: Requires context from outside the message.
107 fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
108 ctx.external
109 .evaluate("transaction_level_error_in_referenced_message")
110 }
111
112 /// [1] Wenn SG3 CTA+IC vorhanden.
113 fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
114 ctx.has_qualifier("CTA", 0, "IC")
115 }
116
117 /// [2] Wenn fehlerhafter Inhalt vorhanden.
118 // REVIEW: In APERAK messages, the ERC (Error Reason Code) segment is present when erroneous content is being reported. Checking for ERC existence is the best proxy for 'fehlerhafter Inhalt vorhanden'. An APERAK with no ERC segments is a pure positive acknowledgment with no errors. (medium confidence)
119 fn evaluate_2(&self, ctx: &EvaluationContext) -> ConditionResult {
120 // Wenn fehlerhafter Inhalt vorhanden — ERC segment indicates presence of erroneous content
121 ConditionResult::from(ctx.has_segment("ERC"))
122 }
123
124 /// [4] Wenn in dieser SG4, RFF+TN nicht vorhanden.
125 // REVIEW: Checks whether RFF+TN is absent within the current SG4 group's child SG5 groups. Uses parent-child navigation: for each SG4 instance, inspects all SG5 children for RFF with qualifier TN. Falls back to message-wide lacks_qualifier when no navigator is available. Medium confidence because the SG4/SG5 nesting in APERAK may differ from other message types. (medium confidence)
126 fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
127 let nav = match ctx.navigator() {
128 Some(n) => n,
129 None => return ctx.lacks_qualifier("RFF", 0, "TN"),
130 };
131 let sg4_count = nav.group_instance_count(&["SG4"]);
132 for i in 0..sg4_count {
133 let sg5_count = nav.child_group_instance_count(&["SG4"], i, "SG5");
134 let has_tn = (0..sg5_count).any(|j| {
135 nav.find_segments_in_child_group("RFF", &["SG4"], i, "SG5", j)
136 .iter()
137 .any(|s| {
138 s.elements
139 .first()
140 .and_then(|e| e.first())
141 .is_some_and(|v| v == "TN")
142 })
143 });
144 if !has_tn {
145 return ConditionResult::True;
146 }
147 }
148 ConditionResult::False
149 }
150
151 /// [5] Wenn SG4 ERC+Z29 vorhanden.
152 fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
153 ctx.has_qualifier("ERC", 0, "Z29")
154 }
155
156 /// [7] Wenn SG4 ERC+Z21 vorhanden.
157 fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
158 ctx.has_qualifier("ERC", 0, "Z21")
159 }
160
161 /// [8] Wenn SG4 ERC+Z16 vorhanden.
162 fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
163 ctx.has_qualifier("ERC", 0, "Z16")
164 }
165
166 /// [9] Wenn SG4 ERC+Z35 vorhanden.
167 fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
168 ctx.has_qualifier("ERC", 0, "Z35")
169 }
170
171 /// [10] Wenn SG4 ERC+Z38 vorhanden.
172 fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
173 ctx.has_qualifier("ERC", 0, "Z38")
174 }
175
176 /// [11] Wenn SG4 ERC+Z39 vorhanden.
177 fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
178 ctx.has_qualifier("ERC", 0, "Z39")
179 }
180
181 /// [12] Wenn SG4 ERC+Z41 vorhanden.
182 fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
183 ctx.has_qualifier("ERC", 0, "Z41")
184 }
185
186 /// [13] Wenn SG4 ERC+Z40 vorhanden.
187 fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
188 ctx.has_qualifier("ERC", 0, "Z40")
189 }
190
191 /// [14] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
192 fn evaluate_14(&self, ctx: &EvaluationContext) -> ConditionResult {
193 let coms = ctx.find_segments("COM");
194 ConditionResult::from(coms.iter().any(|com| {
195 com.elements
196 .first()
197 .and_then(|e| e.get(1))
198 .is_some_and(|v| v == "EM")
199 }))
200 }
201
202 /// [15] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
203 fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
204 let coms = ctx.find_segments("COM");
205 ConditionResult::from(coms.iter().any(|com| {
206 com.elements
207 .first()
208 .and_then(|e| e.get(1))
209 .map(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
210 .unwrap_or(false)
211 }))
212 }
213
214 /// [16] Wenn der referenzierte Nachrichtentyp IFTSTA, INSRPT, UTILMD oder UTILTS ist.
215 // REVIEW: APERAK references another message type. BGM and RFF segments in APERAK carry the referenced message type identifier. Checking all elements of BGM and RFF for the four message type names (IFTSTA, INSRPT, UTILMD, UTILTS) covers the likely encodings. (medium confidence)
216 fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
217 let bgm_segs = ctx.find_segments("BGM");
218 let in_bgm = bgm_segs.iter().any(|s| {
219 s.elements
220 .iter()
221 .flatten()
222 .any(|v| matches!(v.as_str(), "IFTSTA" | "INSRPT" | "UTILMD" | "UTILTS"))
223 });
224 if in_bgm {
225 return ConditionResult::True;
226 }
227 let rff_segs = ctx.find_segments("RFF");
228 ConditionResult::from(rff_segs.iter().any(|s| {
229 s.elements
230 .iter()
231 .flatten()
232 .any(|v| matches!(v.as_str(), "IFTSTA" | "INSRPT" | "UTILMD" | "UTILTS"))
233 }))
234 }
235
236 /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
237 // REVIEW: This condition is a declarative constraint: the date in this field must be the document creation time or earlier. It is not a conditional predicate that selects when a field is used, but rather a validation rule that always applies. Without knowing the specific DTM qualifier pair to compare (which field this annotates vs. DTM+137), a full comparison is not implementable. Returning True reflects that the constraint is always in force. (medium confidence)
238 fn evaluate_494(&self, _ctx: &EvaluationContext) -> ConditionResult {
239 // Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde,
240 // oder ein Zeitpunkt, der davor liegt.
241 // This is a declarative validation constraint on the field value itself — it always
242 // applies when the field is present. The AHB uses this condition to annotate the
243 // allowed date range; the constraint is unconditionally in effect.
244 ConditionResult::True
245 }
246
247 /// [500] Hinweis: Für Folgeprozesse.
248 fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
249 // Hinweis: Für Folgeprozesse — informational note, always applies
250 ConditionResult::True
251 }
252
253 /// [501] Hinweis: Für Initialprozesse.
254 fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
255 // Hinweis: Für Initialprozesse — informational note, always applies
256 ConditionResult::True
257 }
258
259 /// [502] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
260 fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
261 // Hinweis: Es darf nur eine Information im DE3148 übermittelt werden — informational note, always applies
262 ConditionResult::True
263 }
264
265 /// [931] Format: ZZZ = +00
266 // REVIEW: Timezone format ZZZ = +00 means the datetime value string in DTM element 0 component 1 must end with '+00'. Checking all DTM segments for this suffix covers the format requirement. (medium confidence)
267 fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
268 let dtm_segs = ctx.find_segments("DTM");
269 ConditionResult::from(dtm_segs.iter().any(|s| {
270 s.elements
271 .first()
272 .and_then(|e| e.get(1))
273 .map(|v| v.ends_with("+00"))
274 .unwrap_or(false)
275 }))
276 }
277
278 /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
279 // REVIEW: Email address format check: the communication address in COM element 0 component 0 must contain both '@' and '.' characters. Checking all COM segments covers the relevant occurrences. (medium confidence)
280 fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
281 let com_segs = ctx.find_segments("COM");
282 ConditionResult::from(com_segs.iter().any(|s| {
283 s.elements
284 .first()
285 .and_then(|e| e.first())
286 .map(|v| v.contains('@') && v.contains('.'))
287 .unwrap_or(false)
288 }))
289 }
290
291 /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
292 // REVIEW: Phone number format check: the string must start with '+' followed by only digits. Checking COM element 0 component 0 across all COM segments covers international phone number format validation. (medium confidence)
293 fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
294 let com_segs = ctx.find_segments("COM");
295 ConditionResult::from(com_segs.iter().any(|s| {
296 s.elements
297 .first()
298 .and_then(|e| e.first())
299 .map(|v| {
300 let mut chars = v.chars();
301 chars.next() == Some('+') && chars.all(|c| c.is_ascii_digit())
302 })
303 .unwrap_or(false)
304 }))
305 }
306}