1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
// <auto-generated>
// Generated by automapper-generator generate-conditions
// AHB: xml-migs-and-ahbs/FV2504/APERAK_AHB_2_4a_Fehlerkorrektur_20250331.xml
// Generated: 2026-03-12T10:19:40Z
// </auto-generated>
// LLM-produced bodies aren't idiomatic Rust — silence stylistic lints at
// file scope so `cargo clippy -D warnings` stays green. Correctness,
// suspicious, and perf categories stay on so real bugs still surface.
#![allow(clippy::style, clippy::complexity)]
#[allow(unused_imports)]
use crate::eval::format_validators::*;
use crate::eval::{ConditionEvaluator, ConditionResult, EvaluationContext};
/// Generated condition evaluator for APERAK FV2504.
pub struct AperakConditionEvaluatorFV2504 {
// External condition IDs that require runtime context.
external_conditions: std::collections::HashSet<u32>,
}
impl Default for AperakConditionEvaluatorFV2504 {
fn default() -> Self {
let mut external_conditions = std::collections::HashSet::new();
external_conditions.insert(3);
external_conditions.insert(6);
Self {
external_conditions,
}
}
}
impl ConditionEvaluator for AperakConditionEvaluatorFV2504 {
fn message_type(&self) -> &str {
"APERAK"
}
fn format_version(&self) -> &str {
"FV2504"
}
fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
match condition {
1 => self.evaluate_1(ctx),
2 => self.evaluate_2(ctx),
3 => self.evaluate_3(ctx),
4 => self.evaluate_4(ctx),
5 => self.evaluate_5(ctx),
6 => self.evaluate_6(ctx),
7 => self.evaluate_7(ctx),
8 => self.evaluate_8(ctx),
9 => self.evaluate_9(ctx),
10 => self.evaluate_10(ctx),
11 => self.evaluate_11(ctx),
12 => self.evaluate_12(ctx),
13 => self.evaluate_13(ctx),
14 => self.evaluate_14(ctx),
15 => self.evaluate_15(ctx),
16 => self.evaluate_16(ctx),
494 => self.evaluate_494(ctx),
500 => self.evaluate_500(ctx),
501 => self.evaluate_501(ctx),
502 => self.evaluate_502(ctx),
931 => self.evaluate_931(ctx),
939 => self.evaluate_939(ctx),
940 => self.evaluate_940(ctx),
_ => ConditionResult::Unknown,
}
}
fn is_external(&self, condition: u32) -> bool {
self.external_conditions.contains(&condition)
}
fn is_known(&self, condition: u32) -> bool {
matches!(
condition,
1 | 2
| 3
| 4
| 5
| 6
| 7
| 8
| 9
| 10
| 11
| 12
| 13
| 14
| 15
| 16
| 494
| 500
| 501
| 502
| 931
| 939
| 940
)
}
}
impl AperakConditionEvaluatorFV2504 {
/// [3] Wenn für weitere Fehlerangabe benötigt.
/// EXTERNAL: Requires context from outside the message.
fn evaluate_3(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("additional_error_detail_needed")
}
/// [6] Wenn Fehler innerhalb der Vorgangsebene von IFTSTA, INSRPT, UTILMD oder UTILTS vorhanden.
/// EXTERNAL: Requires context from outside the message.
fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external
.evaluate("transaction_level_error_in_referenced_message")
}
/// [1] Wenn SG3 CTA+IC vorhanden.
fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("CTA", 0, "IC")
}
/// [2] Wenn fehlerhafter Inhalt vorhanden.
// 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)
fn evaluate_2(&self, ctx: &EvaluationContext) -> ConditionResult {
// Wenn fehlerhafter Inhalt vorhanden — ERC segment indicates presence of erroneous content
ConditionResult::from(ctx.has_segment("ERC"))
}
/// [4] Wenn in dieser SG4, RFF+TN nicht vorhanden.
// 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)
fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
let nav = match ctx.navigator() {
Some(n) => n,
None => return ctx.lacks_qualifier("RFF", 0, "TN"),
};
let sg4_count = nav.group_instance_count(&["SG4"]);
for i in 0..sg4_count {
let sg5_count = nav.child_group_instance_count(&["SG4"], i, "SG5");
let has_tn = (0..sg5_count).any(|j| {
nav.find_segments_in_child_group("RFF", &["SG4"], i, "SG5", j)
.iter()
.any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "TN")
})
});
if !has_tn {
return ConditionResult::True;
}
}
ConditionResult::False
}
/// [5] Wenn SG4 ERC+Z29 vorhanden.
fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("ERC", 0, "Z29")
}
/// [7] Wenn SG4 ERC+Z21 vorhanden.
fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("ERC", 0, "Z21")
}
/// [8] Wenn SG4 ERC+Z16 vorhanden.
fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("ERC", 0, "Z16")
}
/// [9] Wenn SG4 ERC+Z35 vorhanden.
fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("ERC", 0, "Z35")
}
/// [10] Wenn SG4 ERC+Z38 vorhanden.
fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("ERC", 0, "Z38")
}
/// [11] Wenn SG4 ERC+Z39 vorhanden.
fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("ERC", 0, "Z39")
}
/// [12] Wenn SG4 ERC+Z41 vorhanden.
fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("ERC", 0, "Z41")
}
/// [13] Wenn SG4 ERC+Z40 vorhanden.
fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("ERC", 0, "Z40")
}
/// [14] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
fn evaluate_14(&self, ctx: &EvaluationContext) -> ConditionResult {
let coms = ctx.find_segments("COM");
ConditionResult::from(coms.iter().any(|com| {
com.elements
.first()
.and_then(|e| e.get(1))
.is_some_and(|v| v == "EM")
}))
}
/// [15] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
let coms = ctx.find_segments("COM");
ConditionResult::from(coms.iter().any(|com| {
com.elements
.first()
.and_then(|e| e.get(1))
.map(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
.unwrap_or(false)
}))
}
/// [16] Wenn der referenzierte Nachrichtentyp IFTSTA, INSRPT, UTILMD oder UTILTS ist.
// 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)
fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
let bgm_segs = ctx.find_segments("BGM");
let in_bgm = bgm_segs.iter().any(|s| {
s.elements
.iter()
.flatten()
.any(|v| matches!(v.as_str(), "IFTSTA" | "INSRPT" | "UTILMD" | "UTILTS"))
});
if in_bgm {
return ConditionResult::True;
}
let rff_segs = ctx.find_segments("RFF");
ConditionResult::from(rff_segs.iter().any(|s| {
s.elements
.iter()
.flatten()
.any(|v| matches!(v.as_str(), "IFTSTA" | "INSRPT" | "UTILMD" | "UTILTS"))
}))
}
/// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
// 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)
fn evaluate_494(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde,
// oder ein Zeitpunkt, der davor liegt.
// This is a declarative validation constraint on the field value itself — it always
// applies when the field is present. The AHB uses this condition to annotate the
// allowed date range; the constraint is unconditionally in effect.
ConditionResult::True
}
/// [500] Hinweis: Für Folgeprozesse.
fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Für Folgeprozesse — informational note, always applies
ConditionResult::True
}
/// [501] Hinweis: Für Initialprozesse.
fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Für Initialprozesse — informational note, always applies
ConditionResult::True
}
/// [502] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Es darf nur eine Information im DE3148 übermittelt werden — informational note, always applies
ConditionResult::True
}
/// [931] Format: ZZZ = +00
fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("DTM", 0, 1, validate_timezone_utc)
}
/// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("COM", 0, 0, validate_email)
}
/// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("COM", 0, 0, validate_phone)
}
}