Skip to main content

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}