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