Skip to main content

automapper_validation/generated/fv2504/
reqote_conditions_fv2504.rs

1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2504/REQOTE_AHB_1_0a_Fehlerkorrektur_20250225.xml
4// Generated: 2026-03-12T09:51:49Z
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 REQOTE FV2504.
17pub struct ReqoteConditionEvaluatorFV2504 {
18    // External condition IDs that require runtime context.
19    external_conditions: std::collections::HashSet<u32>,
20}
21
22impl Default for ReqoteConditionEvaluatorFV2504 {
23    fn default() -> Self {
24        let mut external_conditions = std::collections::HashSet::new();
25        external_conditions.insert(10);
26        external_conditions.insert(24);
27        external_conditions.insert(28);
28        external_conditions.insert(29);
29        external_conditions.insert(30);
30        external_conditions.insert(31);
31        external_conditions.insert(32);
32        external_conditions.insert(33);
33        external_conditions.insert(35);
34        external_conditions.insert(36);
35        external_conditions.insert(37);
36        external_conditions.insert(38);
37        external_conditions.insert(41);
38        external_conditions.insert(42);
39        external_conditions.insert(43);
40        external_conditions.insert(492);
41        external_conditions.insert(493);
42        external_conditions.insert(2065);
43        external_conditions.insert(2066);
44        Self {
45            external_conditions,
46        }
47    }
48}
49
50impl ConditionEvaluator for ReqoteConditionEvaluatorFV2504 {
51    fn message_type(&self) -> &str {
52        "REQOTE"
53    }
54
55    fn format_version(&self) -> &str {
56        "FV2504"
57    }
58
59    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
60        match condition {
61            1 => self.evaluate_1(ctx),
62            2 => self.evaluate_2(ctx),
63            10 => self.evaluate_10(ctx),
64            15 => self.evaluate_15(ctx),
65            16 => self.evaluate_16(ctx),
66            17 => self.evaluate_17(ctx),
67            18 => self.evaluate_18(ctx),
68            19 => self.evaluate_19(ctx),
69            20 => self.evaluate_20(ctx),
70            21 => self.evaluate_21(ctx),
71            22 => self.evaluate_22(ctx),
72            23 => self.evaluate_23(ctx),
73            24 => self.evaluate_24(ctx),
74            25 => self.evaluate_25(ctx),
75            26 => self.evaluate_26(ctx),
76            27 => self.evaluate_27(ctx),
77            28 => self.evaluate_28(ctx),
78            29 => self.evaluate_29(ctx),
79            30 => self.evaluate_30(ctx),
80            31 => self.evaluate_31(ctx),
81            32 => self.evaluate_32(ctx),
82            33 => self.evaluate_33(ctx),
83            35 => self.evaluate_35(ctx),
84            36 => self.evaluate_36(ctx),
85            37 => self.evaluate_37(ctx),
86            38 => self.evaluate_38(ctx),
87            39 => self.evaluate_39(ctx),
88            40 => self.evaluate_40(ctx),
89            41 => self.evaluate_41(ctx),
90            42 => self.evaluate_42(ctx),
91            43 => self.evaluate_43(ctx),
92            490 => self.evaluate_490(ctx),
93            491 => self.evaluate_491(ctx),
94            492 => self.evaluate_492(ctx),
95            493 => self.evaluate_493(ctx),
96            494 => self.evaluate_494(ctx),
97            500 => self.evaluate_500(ctx),
98            501 => self.evaluate_501(ctx),
99            502 => self.evaluate_502(ctx),
100            503 => self.evaluate_503(ctx),
101            504 => self.evaluate_504(ctx),
102            507 => self.evaluate_507(ctx),
103            508 => self.evaluate_508(ctx),
104            509 => self.evaluate_509(ctx),
105            510 => self.evaluate_510(ctx),
106            511 => self.evaluate_511(ctx),
107            512 => self.evaluate_512(ctx),
108            514 => self.evaluate_514(ctx),
109            515 => self.evaluate_515(ctx),
110            516 => self.evaluate_516(ctx),
111            903 => self.evaluate_903(ctx),
112            906 => self.evaluate_906(ctx),
113            931 => self.evaluate_931(ctx),
114            932 => self.evaluate_932(ctx),
115            933 => self.evaluate_933(ctx),
116            934 => self.evaluate_934(ctx),
117            935 => self.evaluate_935(ctx),
118            939 => self.evaluate_939(ctx),
119            940 => self.evaluate_940(ctx),
120            950 => self.evaluate_950(ctx),
121            951 => self.evaluate_951(ctx),
122            960 => self.evaluate_960(ctx),
123            961 => self.evaluate_961(ctx),
124            962 => self.evaluate_962(ctx),
125            967 => self.evaluate_967(ctx),
126            2005 => self.evaluate_2005(ctx),
127            2060 => self.evaluate_2060(ctx),
128            2061 => self.evaluate_2061(ctx),
129            2062 => self.evaluate_2062(ctx),
130            2063 => self.evaluate_2063(ctx),
131            2064 => self.evaluate_2064(ctx),
132            2065 => self.evaluate_2065(ctx),
133            2066 => self.evaluate_2066(ctx),
134            _ => ConditionResult::Unknown,
135        }
136    }
137
138    fn is_external(&self, condition: u32) -> bool {
139        self.external_conditions.contains(&condition)
140    }
141    fn is_known(&self, condition: u32) -> bool {
142        matches!(
143            condition,
144            1 | 2
145                | 10
146                | 15
147                | 16
148                | 17
149                | 18
150                | 19
151                | 20
152                | 21
153                | 22
154                | 23
155                | 24
156                | 25
157                | 26
158                | 27
159                | 28
160                | 29
161                | 30
162                | 31
163                | 32
164                | 33
165                | 35
166                | 36
167                | 37
168                | 38
169                | 39
170                | 40
171                | 41
172                | 42
173                | 43
174                | 490
175                | 491
176                | 492
177                | 493
178                | 494
179                | 500
180                | 501
181                | 502
182                | 503
183                | 504
184                | 507
185                | 508
186                | 509
187                | 510
188                | 511
189                | 512
190                | 514
191                | 515
192                | 516
193                | 903
194                | 906
195                | 931
196                | 932
197                | 933
198                | 934
199                | 935
200                | 939
201                | 940
202                | 950
203                | 951
204                | 960
205                | 961
206                | 962
207                | 967
208                | 2005
209                | 2060
210                | 2061
211                | 2062
212                | 2063
213                | 2064
214                | 2065
215                | 2066
216        )
217    }
218}
219
220impl ReqoteConditionEvaluatorFV2504 {
221    /// [33] Es sind nur die Messprodukte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.4 „Messprodukte mit Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW“ enthalten sind.
222    /// EXTERNAL: Requires context from outside the message.
223    fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
224        ctx.external
225            .evaluate("product_in_konfigurationserlaubnis_typ2_smgw_list")
226    }
227
228    /// [37] Es sind nur die Messprodukt-Position-Codes erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.7 „Art der Werte für Messprodukte nach Typ 2“ enthalten sind.
229    /// EXTERNAL: Requires context from outside the message.
230    fn evaluate_37(&self, ctx: &EvaluationContext) -> ConditionResult {
231        ctx.external
232            .evaluate("product_position_code_in_art_der_werte_typ2_list")
233    }
234
235    /// [38] Wenn innerhalb derselben SG27 LIN im PIA+5 DE7140 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) ein Produkt angegeben ist, das in der Codeliste der Konfigurationen...
236    /// EXTERNAL: Requires context from outside the message.
237    fn evaluate_38(&self, ctx: &EvaluationContext) -> ConditionResult {
238        ctx.external
239            .evaluate("product_is_schwellwert_triggered_in_konfigurationserlaubnis_list")
240    }
241
242    /// [41] Es sind nur die Messprodukte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.6.1 „Werte nach Typ 2 aus Backend“ enthalten sind.
243    /// EXTERNAL: Requires context from outside the message.
244    fn evaluate_41(&self, ctx: &EvaluationContext) -> ConditionResult {
245        ctx.external.evaluate("product_in_werte_typ2_backend_list")
246    }
247
248    /// [42] Es sind nur die Messprodukte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.6.2 „Werte nach Typ 2 aus SMGW“ enthalten sind.
249    /// EXTERNAL: Requires context from outside the message.
250    fn evaluate_42(&self, ctx: &EvaluationContext) -> ConditionResult {
251        ctx.external.evaluate("product_in_werte_typ2_smgw_list")
252    }
253
254    /// [43] Wenn innerhalb derselben SG27 LIN im PIA+5 DE7140 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) ein Produkt angegeben ist, das in der Codeliste der Konfigurationen...
255    /// EXTERNAL: Requires context from outside the message.
256    fn evaluate_43(&self, ctx: &EvaluationContext) -> ConditionResult {
257        ctx.external
258            .evaluate("product_is_schwellwert_ausloeser_typ2_smgw")
259    }
260
261    /// [490] wenn Wert in diesem DE, an der Stelle CCYYMMDDHHMM ein Zeitpunkt aus dem angegeben Zeitraum der Tabelle Kapitel 3.5 „Übersicht gesetzliche deutsche Sommerzeit (MESZ)“ der Spalten: „Sommerzei...
262    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
263        match ctx.resolved_value {
264            Some(val) => is_mesz_utc(val),
265            None => ConditionResult::False,
266        }
267    }
268
269    /// [491] wenn Wert in diesem DE, an der Stelle CCYYMMDDHHMM ein Zeitpunkt aus dem angegeben Zeitraum der Tabelle Kapitel 3.6 „Übersicht gesetzliche deutsche Zeit (MEZ)“ der Spalten: „Winterzeit (MEZ)...
270    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
271        match ctx.resolved_value {
272            Some(val) => is_mez_utc(val),
273            None => ConditionResult::False,
274        }
275    }
276
277    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt.
278    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
279        // The date in this DE must be ≤ DTM+137 (Nachrichtendatum).
280        // Prefer the tree-resolved value; fall back to scanning for a
281        // non-137 DTM when unavailable.
282        let creation = match ctx
283            .find_segments_with_qualifier("DTM", 0, "137")
284            .first()
285            .and_then(|s| s.elements.first())
286            .and_then(|e| e.get(1))
287        {
288            Some(v) if !v.is_empty() => v.clone(),
289            _ => return ConditionResult::Unknown,
290        };
291        if let Some(val) = ctx.resolved_value {
292            if !val.is_empty() {
293                return ConditionResult::from(val <= creation.as_str());
294            }
295        }
296        // Fallback for non-tree callers: first non-137 DTM value.
297        for dtm in ctx.find_segments("DTM") {
298            let qual = dtm
299                .elements
300                .first()
301                .and_then(|e| e.first())
302                .map(|s| s.as_str());
303            if qual != Some("137") {
304                if let Some(val) = dtm.elements.first().and_then(|e| e.get(1)) {
305                    if !val.is_empty() {
306                        return ConditionResult::from(val.as_str() <= creation.as_str());
307                    }
308                }
309            }
310        }
311        ConditionResult::Unknown
312    }
313
314    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
315    fn evaluate_967(&self, ctx: &EvaluationContext) -> ConditionResult {
316        // Structural check on the certificate body (base64 charset + plausible
317        // length). Cryptographic validation (signature, CA, BSI profile) is
318        // out of scope for static AHB validation.
319        match ctx.resolved_value {
320            Some(val) => validate_x509_cert_body(val),
321            None => ConditionResult::Unknown,
322        }
323    }
324
325    /// [2065] Diese SG28 ist so oft zu wiederholen, wie zu den unterschiedlichen Messprodukt-Position-Codes zu dem innerhalb derselben SG27 LIN im PIA+5 DE7140 (Erforderliches Produkt Konfigurationserlaubnis fü...
326    /// EXTERNAL: Requires context from outside the message.
327    fn evaluate_2065(&self, ctx: &EvaluationContext) -> ConditionResult {
328        ctx.external
329            .evaluate("sg28_repeat_count_schwellwert_smgw_config")
330    }
331
332    /// [2066] Diese SG28 ist so oft zu wiederholen, wie zu den unterschiedlichen Messprodukt-Position-Codes zu dem innerhalb derselben SG27 LIN im PIA+5 DE7140 (Erforderliches Produkt Konfigurationserlaubnis fü...
333    /// EXTERNAL: Requires context from outside the message.
334    fn evaluate_2066(&self, ctx: &EvaluationContext) -> ConditionResult {
335        ctx.external
336            .evaluate("sg28_repeat_count_schwellwert_smgw_werte_typ2")
337    }
338
339    /// [1] Wenn DTM+203 (Ausführungsdatum) nicht vorhanden
340    fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
341        ctx.lacks_qualifier("DTM", 0, "203")
342    }
343
344    /// [2] Wenn DTM+469 (Beginn zum (nächstmöglichen Termin)) nicht vorhanden
345    fn evaluate_2(&self, ctx: &EvaluationContext) -> ConditionResult {
346        ctx.lacks_qualifier("DTM", 0, "469")
347    }
348
349    /// [10] MP-ID nur aus Sparte Strom
350    /// EXTERNAL: Requires context from outside the message.
351    fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
352        ctx.external.evaluate("mp_id_strom_only")
353    }
354
355    /// [15] Wenn DTM+76 (Datum zum geplanten Leistungsbeginn) nicht vorhanden
356    fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
357        ctx.lacks_qualifier("DTM", 0, "76")
358    }
359
360    /// [16] Wenn SG1 RFF+Z41 (Referenznummer des Vorgangs der Anmeldung nach WiM) nicht vorhanden
361    fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
362        ctx.lacks_qualifier("RFF", 0, "Z41")
363    }
364
365    /// [17] Wenn SG1 RFF+Z41 (Referenznummer des Vorgangs der Anmeldung nach WiM) vorhanden
366    fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
367        ctx.has_qualifier("RFF", 0, "Z41")
368    }
369
370    /// [18] Wenn IMD++Z55 (Änderung Konfiguration) vorhanden
371    fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
372        ctx.has_qualifier("IMD", 1, "Z55")
373    }
374
375    /// [19] Wenn SG27 LIN++Z64 (Erforderliches Produkt Schaltzeitdefinitionen) vorhanden
376    fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
377        ctx.has_qualifier("LIN", 1, "Z64")
378    }
379
380    /// [20] Wenn SG27 LIN++Z65 (Erforderliches Produkt Leistungskurvendefinitionen) vorhanden
381    fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
382        ctx.has_qualifier("LIN", 1, "Z65")
383    }
384
385    /// [21] Wenn SG27 LIN++Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) vorhanden
386    fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
387        ctx.has_qualifier("LIN", 1, "Z66")
388    }
389
390    /// [22] Wenn SG27 LIN++Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) vorhanden
391    fn evaluate_22(&self, ctx: &EvaluationContext) -> ConditionResult {
392        ctx.has_qualifier("LIN", 1, "Z67")
393    }
394
395    /// [23] Wenn SG27 LIN++Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) vorhanden
396    fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
397        ctx.has_qualifier("LIN", 1, "Z68")
398    }
399
400    /// [24] Wenn Produkt bestellt werden soll
401    /// EXTERNAL: Requires context from outside the message.
402    // REVIEW: Whether a product should be ordered is a business-intent condition. In a REQOTE context this could theoretically be signalled by presence of PIA+5, but the AHB phrasing 'soll bestellt werden' indicates a business decision or workflow state that cannot be reliably inferred from message structure alone. (medium confidence)
403    fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
404        ctx.external.evaluate("product_to_be_ordered")
405    }
406
407    /// [25] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Marktlokation angegeben ist.
408    fn evaluate_25(&self, ctx: &EvaluationContext) -> ConditionResult {
409        // Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Marktlokation angegeben ist.
410        // LOC elements[0][0] == "172" (qualifier), elements[1][0] == DE3225 (Identifikator)
411        // Marktlokation ID: 11 digits with Luhn check digit
412        let segs = ctx.find_segments_with_qualifier("LOC", 0, "172");
413        match segs
414            .first()
415            .and_then(|s| s.elements.get(1))
416            .and_then(|e| e.first())
417        {
418            Some(val) if !val.is_empty() => validate_malo_id(val),
419            _ => ConditionResult::Unknown,
420        }
421    }
422
423    /// [26] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Messlokation angegeben ist.
424    fn evaluate_26(&self, ctx: &EvaluationContext) -> ConditionResult {
425        // Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Messlokation angegeben ist.
426        // LOC elements[0][0] == "172" (qualifier), elements[1][0] == DE3225 (Identifikator)
427        // Messlokation ID: 33 alphanumeric characters (Zählpunkt format)
428        let segs = ctx.find_segments_with_qualifier("LOC", 0, "172");
429        match segs
430            .first()
431            .and_then(|s| s.elements.get(1))
432            .and_then(|e| e.first())
433        {
434            Some(val) if !val.is_empty() => validate_zahlpunkt(val),
435            _ => ConditionResult::Unknown,
436        }
437    }
438
439    /// [27] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Netzlokation angegeben ist.
440    // REVIEW: Checks LOC+172 DE3225 value as a Netzlokation ID by exclusion: it is present but does not match Marktlokation format (11-digit Luhn) nor Messlokation format (33 alphanumeric). Medium confidence because there is no dedicated Netzlokation validator — the logic relies on the three location types being mutually exclusive and exhaustive for this qualifier. (medium confidence)
441    fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
442        // Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Netzlokation angegeben ist.
443        // LOC elements[0][0] == "172" (qualifier), elements[1][0] == DE3225 (Identifikator)
444        // Netzlokation ID: neither MaLo (11 digits Luhn) nor MeLo (33 alphanumeric)
445        let segs = ctx.find_segments_with_qualifier("LOC", 0, "172");
446        match segs
447            .first()
448            .and_then(|s| s.elements.get(1))
449            .and_then(|e| e.first())
450        {
451            Some(val) if !val.is_empty() => {
452                let is_malo = matches!(validate_malo_id(val), ConditionResult::True);
453                let is_melo = matches!(validate_zahlpunkt(val), ConditionResult::True);
454                ConditionResult::from(!is_malo && !is_melo)
455            }
456            _ => ConditionResult::Unknown,
457        }
458    }
459
460    /// [28] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Steuerbaren Ressource angegeben ist.
461    /// EXTERNAL: Requires context from outside the message.
462    fn evaluate_28(&self, ctx: &EvaluationContext) -> ConditionResult {
463        ctx.external.evaluate("loc_172_is_steuerbare_ressource")
464    }
465
466    /// [29] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.1 „Konfigurationsprodukte Schaltzeitdefinition“ enthalten sind.
467    /// EXTERNAL: Requires context from outside the message.
468    fn evaluate_29(&self, ctx: &EvaluationContext) -> ConditionResult {
469        ctx.external
470            .evaluate("product_in_konfiguration_schaltzeitdefinition")
471    }
472
473    /// [30] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.2 „Konfigurationsprodukte Leistungskurvendefinition“ enthalten sind.
474    /// EXTERNAL: Requires context from outside the message.
475    fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
476        ctx.external
477            .evaluate("product_in_konfiguration_leistungskurvendefinition")
478    }
479
480    /// [31] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.3 „Konfigurationsprodukte Ad-Hoc-Steuerkanal“ enthalten sind.
481    /// EXTERNAL: Requires context from outside the message.
482    fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
483        ctx.external
484            .evaluate("product_in_konfiguration_adhoc_steuerkanal")
485    }
486
487    /// [32] Es sind nur die Messprodukte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.5 „Messprodukte für Werte nach Typ 2 aus Backend für LF und NB“ enthalten sind.
488    /// EXTERNAL: Requires context from outside the message.
489    fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
490        ctx.external.evaluate("product_in_messprodukte_typ2_lf_nb")
491    }
492
493    /// [35] Wenn MP-ID in SG11 NAD+MS mit Rolle LF vorhanden
494    /// EXTERNAL: Requires context from outside the message.
495    // REVIEW: NAD+MS identifies the sender by MP-ID (elements[0]=MS), but the market role 'LF' (Lieferant) cannot be determined from the NAD segment alone — it requires an external participant role registry lookup. The segment only carries the ID, not the role. (medium confidence)
496    fn evaluate_35(&self, ctx: &EvaluationContext) -> ConditionResult {
497        ctx.external.evaluate("sender_is_lf")
498    }
499
500    /// [36] Wenn MP-ID in SG11 NAD+MS mit Rolle NB vorhanden
501    /// EXTERNAL: Requires context from outside the message.
502    // REVIEW: Same pattern as condition 35: NAD+MS carries the sender MP-ID but the market role 'NB' (Netzbetreiber) cannot be inferred from the EDIFACT segment alone — requires external role registry. (medium confidence)
503    fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
504        ctx.external.evaluate("sender_is_nb")
505    }
506
507    /// [39] wenn im DE3155 in demselben COM der Code EM vorhanden ist
508    fn evaluate_39(&self, ctx: &EvaluationContext) -> ConditionResult {
509        // COM segment: C076 composite — elements[0][0]=DE3148 (address), elements[0][1]=DE3155 (qualifier)
510        let coms = ctx.find_segments("COM");
511        if coms.is_empty() {
512            return ConditionResult::False;
513        }
514        ConditionResult::from(coms.iter().any(|s| {
515            s.elements
516                .first()
517                .and_then(|e| e.get(1))
518                .is_some_and(|v| v == "EM")
519        }))
520    }
521
522    /// [40] wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
523    fn evaluate_40(&self, ctx: &EvaluationContext) -> ConditionResult {
524        // COM segment: C076 composite — elements[0][0]=DE3148 (address), elements[0][1]=DE3155 (qualifier)
525        // TE=Telephone, FX=Fax, AJ=Unknown, AL=Unknown (telephony-related codes)
526        let coms = ctx.find_segments("COM");
527        if coms.is_empty() {
528            return ConditionResult::False;
529        }
530        ConditionResult::from(coms.iter().any(|s| {
531            s.elements
532                .first()
533                .and_then(|e| e.get(1))
534                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
535        }))
536    }
537
538    /// [492] wenn MP-ID in NAD+MR aus Sparte Strom
539    /// EXTERNAL: Requires context from outside the message.
540    // REVIEW: Checks whether the MP-ID in NAD+MR belongs to the electricity sector (Sparte Strom). The sector of a market participant cannot be derived from the EDIFACT message structure — it requires external market participant registry lookup. Delegated to external provider. (medium confidence)
541    fn evaluate_492(&self, ctx: &EvaluationContext) -> ConditionResult {
542        ctx.external.evaluate("recipient_is_electricity_sector")
543    }
544
545    /// [493] wenn MP-ID in NAD+MR aus Sparte Gas
546    /// EXTERNAL: Requires context from outside the message.
547    // REVIEW: Checks whether the MP-ID in NAD+MR belongs to the gas sector (Sparte Gas). Same reasoning as condition 492 — sector membership requires external registry lookup. (medium confidence)
548    fn evaluate_493(&self, ctx: &EvaluationContext) -> ConditionResult {
549        ctx.external.evaluate("recipient_is_gas_sector")
550    }
551
552    /// [500] Hinweis: Angabe eines technischen Ansprechpartners für die Geräteübernahme
553    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
554        // Hinweis: Angabe eines technischen Ansprechpartners für die Geräteübernahme
555        // Informational note — always applies
556        ConditionResult::True
557    }
558
559    /// [501] Hinweis: Angabe eines Ansprechpartners für die Rechnungsabwicklung
560    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
561        // Hinweis: Angabe eines Ansprechpartners für die Rechnungsabwicklung
562        // Informational note — always applies
563        ConditionResult::True
564    }
565
566    /// [502] Hinweis: Verwendung der ID der Marktlokation
567    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
568        // Hinweis: Verwendung der ID der Marktlokation
569        // Informational note — always applies
570        ConditionResult::True
571    }
572
573    /// [503] Hinweis: Verwendung der ID der Messlokation
574    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
575        // Hinweis: Verwendung der ID der Messlokation
576        // Informational note — always applies
577        ConditionResult::True
578    }
579
580    /// [504] Hinweis: Verwendung der ID der Tranche
581    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
582        // Hinweis: Verwendung der ID der Tranche — informational note, always applies
583        ConditionResult::True
584    }
585
586    /// [507] Hinweis: Vorgangsnummer aus SG4 IDE+24 DE7402 der UTILMD mit BGM+E01 mit der die Anmeldung des MSB-Wechsels erfolgt ist.
587    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
588        // Hinweis: Vorgangsnummer aus SG4 IDE+24 DE7402 der UTILMD — informational note, always applies
589        ConditionResult::True
590    }
591
592    /// [508] Hinweis: Wert aus BGM+Z73 DE1004 der IFTSTA mit der die Antwort auf die Bestellung der Konfiguration übermittelt wurde
593    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
594        // Hinweis: Wert aus BGM+Z73 DE1004 der IFTSTA — informational note, always applies
595        ConditionResult::True
596    }
597
598    /// [509] Hinweis: Vorgangsnummer aus CNI DE1490 der IFTSTA mit BGM+Z73 mit der die Antwort auf die Bestellung der Konfiguration übermittelt wurde
599    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
600        // Hinweis: Vorgangsnummer aus CNI DE1490 der IFTSTA — informational note, always applies
601        ConditionResult::True
602    }
603
604    /// [510] Hinweis: Verwendung der ID der Netzlokation
605    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
606        // Hinweis: Verwendung der ID der Netzlokation — informational note, always applies
607        ConditionResult::True
608    }
609
610    /// [511] Hinweis: Verwendung der ID der Steuerbaren Ressource
611    fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
612        ConditionResult::True
613    }
614
615    /// [512] Hinweis: Für den Empfang der Werte nach Typ 2 aus dem SMGW.
616    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
617        ConditionResult::True
618    }
619
620    /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
621    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
622        ConditionResult::True
623    }
624
625    /// [515] Hinweis: Es ist eine URI IPv4 für die Bereitstellung der Werte anzugeben.
626    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
627        ConditionResult::True
628    }
629
630    /// [516] Hinweis: Es ist eine URI IPv6 für die Bereitstellung der Werte anzugeben.
631    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
632        ConditionResult::True
633    }
634
635    /// [903] Format: Möglicher Wert: 1
636    fn evaluate_903(&self, _ctx: &EvaluationContext) -> ConditionResult {
637        ConditionResult::True
638    }
639
640    /// [906] Format: max. 3 Nachkommastellen
641    fn evaluate_906(&self, _ctx: &EvaluationContext) -> ConditionResult {
642        ConditionResult::True
643    }
644
645    /// [931] Format: ZZZ = +00
646    fn evaluate_931(&self, _ctx: &EvaluationContext) -> ConditionResult {
647        ConditionResult::True
648    }
649
650    /// [932] Format: HHMM = 2200
651    fn evaluate_932(&self, _ctx: &EvaluationContext) -> ConditionResult {
652        ConditionResult::True
653    }
654
655    /// [933] Format: HHMM = 2300
656    fn evaluate_933(&self, _ctx: &EvaluationContext) -> ConditionResult {
657        ConditionResult::True
658    }
659
660    /// [934] Format: HHMM = 0400
661    fn evaluate_934(&self, _ctx: &EvaluationContext) -> ConditionResult {
662        ConditionResult::True
663    }
664
665    /// [935] Format: HHMM = 0500
666    fn evaluate_935(&self, _ctx: &EvaluationContext) -> ConditionResult {
667        ConditionResult::True
668    }
669
670    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
671    fn evaluate_939(&self, _ctx: &EvaluationContext) -> ConditionResult {
672        ConditionResult::True
673    }
674
675    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
676    fn evaluate_940(&self, _ctx: &EvaluationContext) -> ConditionResult {
677        ConditionResult::True
678    }
679
680    /// [950] Format: Marktlokations-ID
681    fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
682        ConditionResult::True
683    }
684
685    /// [951] Format: Zählpunktbezeichnung
686    fn evaluate_951(&self, ctx: &EvaluationContext) -> ConditionResult {
687        ctx.format_check_qualified("LOC", 0, "Z17", 1, 0, validate_zahlpunkt)
688    }
689
690    /// [960] Format: Netzlokations-ID
691    // HAND-EDITED: a Netzlokations-ID is "E" + ten alphanumerics (`E1688117482`),
692    // not MaLo-shaped; `validate_malo_id` rejected every real one (#173).
693    fn evaluate_960(&self, ctx: &EvaluationContext) -> ConditionResult {
694        ctx.format_check_qualified("LOC", 0, "Z18", 1, 0, validate_nelo_id)
695    }
696
697    /// [961] Format: SR-ID
698    // REVIEW: SR-ID (SteuerbareRessource-ID) uses the same 11-digit Luhn check digit format as MaLo-ID. LOC+Z19 is the standard qualifier for SteuerbareRessource. (medium confidence)
699    fn evaluate_961(&self, ctx: &EvaluationContext) -> ConditionResult {
700        ctx.format_check_qualified("LOC", 0, "Z19", 1, 0, validate_malo_id)
701    }
702
703    /// [962] Format: max. 6 Vorkommastellen
704    fn evaluate_962(&self, ctx: &EvaluationContext) -> ConditionResult {
705        ctx.format_check("QTY", 0, 1, |val| validate_max_integer_digits(val, 6))
706    }
707
708    /// [2005] Pro Nachricht ist die SG27 genau einmal anzugeben
709    // REVIEW: SG27 must appear exactly once per message. LIN is the entry segment for SG27. Counting all LIN segments gives the SG27 instance count. Returns True when exactly one LIN exists. (medium confidence)
710    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
711        let lin_count = ctx.find_segments("LIN").len();
712        ConditionResult::from(lin_count == 1)
713    }
714
715    /// [2060] Pro Nachricht ist die SG27 LIN+Z64 (Erforderliches Produkt Schaltzeitdefinitionen) maximal einmal anzugeben
716    // REVIEW: LIN with DE1229=Z64 (Erforderliches Produkt Schaltzeitdefinitionen) must appear at most once. Per segment structure reference, DE1229 (Handlung, Code) is at elements[1][0]. Returns True when 0 or 1 such LIN segments exist. (medium confidence)
717    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
718        let lins = ctx.find_segments("LIN");
719        let count = lins
720            .iter()
721            .filter(|s| {
722                s.elements
723                    .get(1)
724                    .and_then(|e| e.first())
725                    .is_some_and(|v| v == "Z64")
726            })
727            .count();
728        ConditionResult::from(count <= 1)
729    }
730
731    /// [2061] Pro Nachricht ist die SG27 LIN++Z65 (Erforderliches Produkt Leistungskurvendefinitionen) maximal einmal anzugeben
732    // REVIEW: LIN with DE1229=Z65 (Erforderliches Produkt Leistungskurvendefinitionen) must appear at most once. Per segment structure reference, DE1229 is at elements[1][0]. Returns True when 0 or 1 such LIN segments exist. (medium confidence)
733    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
734        let lins = ctx.find_segments("LIN");
735        let count = lins
736            .iter()
737            .filter(|s| {
738                s.elements
739                    .get(1)
740                    .and_then(|e| e.first())
741                    .is_some_and(|v| v == "Z65")
742            })
743            .count();
744        ConditionResult::from(count <= 1)
745    }
746
747    /// [2062] Pro Nachricht ist die SG27 LIN++Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) maximal einmal anzugeben
748    // REVIEW: LIN with DE1229=Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) must appear at most once. Per segment structure reference, DE1229 is at elements[1][0]. Returns True when 0 or 1 such LIN segments exist. (medium confidence)
749    fn evaluate_2062(&self, ctx: &EvaluationContext) -> ConditionResult {
750        let lins = ctx.find_segments("LIN");
751        let count = lins
752            .iter()
753            .filter(|s| {
754                s.elements
755                    .get(1)
756                    .and_then(|e| e.first())
757                    .is_some_and(|v| v == "Z66")
758            })
759            .count();
760        ConditionResult::from(count <= 1)
761    }
762
763    /// [2063] Pro Nachricht ist die SG27 LIN++Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) maximal einmal anzugeben
764    // REVIEW: LIN with DE1229=Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) must appear at most once. Per segment structure reference, DE1229 is at elements[1][0]. Returns True when 0 or 1 such LIN segments exist. (medium confidence)
765    fn evaluate_2063(&self, ctx: &EvaluationContext) -> ConditionResult {
766        let lins = ctx.find_segments("LIN");
767        let count = lins
768            .iter()
769            .filter(|s| {
770                s.elements
771                    .get(1)
772                    .and_then(|e| e.first())
773                    .is_some_and(|v| v == "Z67")
774            })
775            .count();
776        ConditionResult::from(count <= 1)
777    }
778
779    /// [2064] Pro Nachricht ist die SG27 LIN++Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) maximal einmal anzugeben
780    // REVIEW: LIN with DE1229=Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) must appear at most once. Per segment structure reference, DE1229 is at elements[1][0]. Returns True when 0 or 1 such LIN segments exist. (medium confidence)
781    fn evaluate_2064(&self, ctx: &EvaluationContext) -> ConditionResult {
782        let lins = ctx.find_segments("LIN");
783        let count = lins
784            .iter()
785            .filter(|s| {
786                s.elements
787                    .get(1)
788                    .and_then(|e| e.first())
789                    .is_some_and(|v| v == "Z68")
790            })
791            .count();
792        ConditionResult::from(count <= 1)
793    }
794}