Skip to main content

automapper_validation/generated/fv2510/
invoic_conditions_fv2510.rs

1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2510/INVOIC_AHB_1_0_Fehlerkorrektur_20250623.xml
4// Generated: 2026-03-12T11:09:04Z
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 INVOIC FV2510.
17pub struct InvoicConditionEvaluatorFV2510 {
18    // External condition IDs that require runtime context.
19    external_conditions: std::collections::HashSet<u32>,
20}
21
22impl Default for InvoicConditionEvaluatorFV2510 {
23    fn default() -> Self {
24        let mut external_conditions = std::collections::HashSet::new();
25        external_conditions.insert(1);
26        external_conditions.insert(2);
27        external_conditions.insert(3);
28        external_conditions.insert(7);
29        external_conditions.insert(8);
30        external_conditions.insert(11);
31        external_conditions.insert(13);
32        external_conditions.insert(15);
33        external_conditions.insert(16);
34        external_conditions.insert(24);
35        external_conditions.insert(25);
36        external_conditions.insert(28);
37        external_conditions.insert(30);
38        external_conditions.insert(31);
39        external_conditions.insert(34);
40        external_conditions.insert(37);
41        external_conditions.insert(40);
42        external_conditions.insert(45);
43        external_conditions.insert(67);
44        external_conditions.insert(68);
45        external_conditions.insert(69);
46        external_conditions.insert(72);
47        external_conditions.insert(76);
48        external_conditions.insert(492);
49        external_conditions.insert(493);
50        Self {
51            external_conditions,
52        }
53    }
54}
55
56impl ConditionEvaluator for InvoicConditionEvaluatorFV2510 {
57    fn message_type(&self) -> &str {
58        "INVOIC"
59    }
60
61    fn format_version(&self) -> &str {
62        "FV2510"
63    }
64
65    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
66        match condition {
67            1 => self.evaluate_1(ctx),
68            2 => self.evaluate_2(ctx),
69            3 => self.evaluate_3(ctx),
70            4 => self.evaluate_4(ctx),
71            5 => self.evaluate_5(ctx),
72            6 => self.evaluate_6(ctx),
73            7 => self.evaluate_7(ctx),
74            8 => self.evaluate_8(ctx),
75            9 => self.evaluate_9(ctx),
76            10 => self.evaluate_10(ctx),
77            11 => self.evaluate_11(ctx),
78            12 => self.evaluate_12(ctx),
79            13 => self.evaluate_13(ctx),
80            14 => self.evaluate_14(ctx),
81            15 => self.evaluate_15(ctx),
82            16 => self.evaluate_16(ctx),
83            17 => self.evaluate_17(ctx),
84            18 => self.evaluate_18(ctx),
85            19 => self.evaluate_19(ctx),
86            20 => self.evaluate_20(ctx),
87            21 => self.evaluate_21(ctx),
88            22 => self.evaluate_22(ctx),
89            23 => self.evaluate_23(ctx),
90            24 => self.evaluate_24(ctx),
91            25 => self.evaluate_25(ctx),
92            26 => self.evaluate_26(ctx),
93            27 => self.evaluate_27(ctx),
94            28 => self.evaluate_28(ctx),
95            29 => self.evaluate_29(ctx),
96            30 => self.evaluate_30(ctx),
97            31 => self.evaluate_31(ctx),
98            32 => self.evaluate_32(ctx),
99            34 => self.evaluate_34(ctx),
100            36 => self.evaluate_36(ctx),
101            37 => self.evaluate_37(ctx),
102            40 => self.evaluate_40(ctx),
103            41 => self.evaluate_41(ctx),
104            42 => self.evaluate_42(ctx),
105            44 => self.evaluate_44(ctx),
106            45 => self.evaluate_45(ctx),
107            47 => self.evaluate_47(ctx),
108            48 => self.evaluate_48(ctx),
109            49 => self.evaluate_49(ctx),
110            50 => self.evaluate_50(ctx),
111            51 => self.evaluate_51(ctx),
112            53 => self.evaluate_53(ctx),
113            54 => self.evaluate_54(ctx),
114            55 => self.evaluate_55(ctx),
115            56 => self.evaluate_56(ctx),
116            57 => self.evaluate_57(ctx),
117            58 => self.evaluate_58(ctx),
118            59 => self.evaluate_59(ctx),
119            60 => self.evaluate_60(ctx),
120            61 => self.evaluate_61(ctx),
121            64 => self.evaluate_64(ctx),
122            65 => self.evaluate_65(ctx),
123            66 => self.evaluate_66(ctx),
124            67 => self.evaluate_67(ctx),
125            68 => self.evaluate_68(ctx),
126            69 => self.evaluate_69(ctx),
127            70 => self.evaluate_70(ctx),
128            71 => self.evaluate_71(ctx),
129            72 => self.evaluate_72(ctx),
130            73 => self.evaluate_73(ctx),
131            74 => self.evaluate_74(ctx),
132            75 => self.evaluate_75(ctx),
133            76 => self.evaluate_76(ctx),
134            77 => self.evaluate_77(ctx),
135            78 => self.evaluate_78(ctx),
136            79 => self.evaluate_79(ctx),
137            80 => self.evaluate_80(ctx),
138            81 => self.evaluate_81(ctx),
139            82 => self.evaluate_82(ctx),
140            83 => self.evaluate_83(ctx),
141            84 => self.evaluate_84(ctx),
142            85 => self.evaluate_85(ctx),
143            86 => self.evaluate_86(ctx),
144            87 => self.evaluate_87(ctx),
145            88 => self.evaluate_88(ctx),
146            490 => self.evaluate_490(ctx),
147            491 => self.evaluate_491(ctx),
148            492 => self.evaluate_492(ctx),
149            493 => self.evaluate_493(ctx),
150            501 => self.evaluate_501(ctx),
151            502 => self.evaluate_502(ctx),
152            503 => self.evaluate_503(ctx),
153            504 => self.evaluate_504(ctx),
154            507 => self.evaluate_507(ctx),
155            508 => self.evaluate_508(ctx),
156            509 => self.evaluate_509(ctx),
157            510 => self.evaluate_510(ctx),
158            512 => self.evaluate_512(ctx),
159            513 => self.evaluate_513(ctx),
160            514 => self.evaluate_514(ctx),
161            515 => self.evaluate_515(ctx),
162            516 => self.evaluate_516(ctx),
163            517 => self.evaluate_517(ctx),
164            518 => self.evaluate_518(ctx),
165            519 => self.evaluate_519(ctx),
166            520 => self.evaluate_520(ctx),
167            521 => self.evaluate_521(ctx),
168            522 => self.evaluate_522(ctx),
169            523 => self.evaluate_523(ctx),
170            524 => self.evaluate_524(ctx),
171            525 => self.evaluate_525(ctx),
172            526 => self.evaluate_526(ctx),
173            902 => self.evaluate_902(ctx),
174            906 => self.evaluate_906(ctx),
175            908 => self.evaluate_908(ctx),
176            910 => self.evaluate_910(ctx),
177            911 => self.evaluate_911(ctx),
178            912 => self.evaluate_912(ctx),
179            914 => self.evaluate_914(ctx),
180            927 => self.evaluate_927(ctx),
181            930 => self.evaluate_930(ctx),
182            931 => self.evaluate_931(ctx),
183            932 => self.evaluate_932(ctx),
184            933 => self.evaluate_933(ctx),
185            934 => self.evaluate_934(ctx),
186            935 => self.evaluate_935(ctx),
187            937 => self.evaluate_937(ctx),
188            939 => self.evaluate_939(ctx),
189            940 => self.evaluate_940(ctx),
190            946 => self.evaluate_946(ctx),
191            950 => self.evaluate_950(ctx),
192            951 => self.evaluate_951(ctx),
193            960 => self.evaluate_960(ctx),
194            961 => self.evaluate_961(ctx),
195            _ => ConditionResult::Unknown,
196        }
197    }
198
199    fn is_external(&self, condition: u32) -> bool {
200        self.external_conditions.contains(&condition)
201    }
202    fn is_known(&self, condition: u32) -> bool {
203        matches!(
204            condition,
205            1 | 2
206                | 3
207                | 4
208                | 5
209                | 6
210                | 7
211                | 8
212                | 9
213                | 10
214                | 11
215                | 12
216                | 13
217                | 14
218                | 15
219                | 16
220                | 17
221                | 18
222                | 19
223                | 20
224                | 21
225                | 22
226                | 23
227                | 24
228                | 25
229                | 26
230                | 27
231                | 28
232                | 29
233                | 30
234                | 31
235                | 32
236                | 34
237                | 36
238                | 37
239                | 40
240                | 41
241                | 42
242                | 44
243                | 45
244                | 47
245                | 48
246                | 49
247                | 50
248                | 51
249                | 53
250                | 54
251                | 55
252                | 56
253                | 57
254                | 58
255                | 59
256                | 60
257                | 61
258                | 64
259                | 65
260                | 66
261                | 67
262                | 68
263                | 69
264                | 70
265                | 71
266                | 72
267                | 73
268                | 74
269                | 75
270                | 76
271                | 77
272                | 78
273                | 79
274                | 80
275                | 81
276                | 82
277                | 83
278                | 84
279                | 85
280                | 86
281                | 87
282                | 88
283                | 490
284                | 491
285                | 492
286                | 493
287                | 501
288                | 502
289                | 503
290                | 504
291                | 507
292                | 508
293                | 509
294                | 510
295                | 512
296                | 513
297                | 514
298                | 515
299                | 516
300                | 517
301                | 518
302                | 519
303                | 520
304                | 521
305                | 522
306                | 523
307                | 524
308                | 525
309                | 526
310                | 902
311                | 906
312                | 908
313                | 910
314                | 911
315                | 912
316                | 914
317                | 927
318                | 930
319                | 931
320                | 932
321                | 933
322                | 934
323                | 935
324                | 937
325                | 939
326                | 940
327                | 946
328                | 950
329                | 951
330                | 960
331                | 961
332        )
333    }
334}
335
336impl InvoicConditionEvaluatorFV2510 {
337    /// [1] Wenn in zu stornierender Rechnung gefüllt
338    /// EXTERNAL: Requires context from outside the message.
339    fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
340        ctx.external.evaluate("original_invoice_field_filled")
341    }
342
343    /// [2] Wenn es sich um eine Nutzungsüberlassung (Pacht) eines Gerätes handelt
344    /// EXTERNAL: Requires context from outside the message.
345    fn evaluate_2(&self, ctx: &EvaluationContext) -> ConditionResult {
346        ctx.external.evaluate("device_is_leased")
347    }
348
349    /// [3] Wenn es sich um einen Kauf eines Gerätes handelt
350    /// EXTERNAL: Requires context from outside the message.
351    fn evaluate_3(&self, ctx: &EvaluationContext) -> ConditionResult {
352        ctx.external.evaluate("device_is_purchased")
353    }
354
355    /// [7] Sofern keine Großkundenpostleitzahl verwendet wird
356    /// EXTERNAL: Requires context from outside the message.
357    fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
358        ctx.external.evaluate("no_grosskundenpzl_used")
359    }
360
361    /// [8] Bei zeitabhängigen Preisen
362    /// EXTERNAL: Requires context from outside the message.
363    fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
364        ctx.external.evaluate("time_dependent_prices")
365    }
366
367    /// [24] Wert muss mindestens 10 WT nach Wert aus DTM+137 DE2380 liegen
368    /// EXTERNAL: Requires context from outside the message.
369    fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
370        ctx.external
371            .evaluate("value_at_least_10_werktage_after_nachrichtendatum")
372    }
373
374    /// [25] Wert darf maximal 10 WT nach Wert aus DTM+137 DE2380 liegen
375    /// EXTERNAL: Requires context from outside the message.
376    fn evaluate_25(&self, ctx: &EvaluationContext) -> ConditionResult {
377        ctx.external
378            .evaluate("value_at_most_10_werktage_after_nachrichtendatum")
379    }
380
381    /// [67] Es sind nur die Artikelnummern erlaubt, die im Kapitel 2 „Codeliste der Artikelnummer“ in der Codeliste der Artikelnummern und Artikel-ID ein „X“ für den entsprechenden Prüfidentifikator ...
382    /// EXTERNAL: Requires context from outside the message.
383    fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
384        ctx.external
385            .evaluate("invoic_artikelnummer_allowed_for_pid")
386    }
387
388    /// [68] Es sind nur die Artikel-ID erlaubt, die im Kapitel 3 „Codeliste der Gruppenartikel-ID und Artikel-ID“  in der Codeliste der Artikelnummern und Artikel-ID ein „X“ in der Spalte „INVOIC Cod...
389    /// EXTERNAL: Requires context from outside the message.
390    fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
391        ctx.external.evaluate("invoic_artikel_id_x_codeverwendung")
392    }
393
394    /// [69] Es sind nur die Artikel-ID erlaubt, die im Kapitel 3 „Codeliste der Gruppenartikel-ID und Artikel-ID“  in der Codeliste der Artikelnummern und Artikel-ID ein „SOR“ in der Spalte „INVOIC C...
395    /// EXTERNAL: Requires context from outside the message.
396    fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
397        ctx.external
398            .evaluate("invoic_artikel_id_sor_codeverwendung")
399    }
400
401    /// [83] Wenn GEI+Z10 nicht vorhanden
402    fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
403        ctx.lacks_qualifier("GEI", 0, "Z10")
404    }
405
406    /// [87] Wenn IMD++TEC vorhanden
407    fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
408        ctx.has_qualifier("IMD", 1, "TEC")
409    }
410
411    /// [4] Wenn Steuerschuldnerschaft des Leistungsempfängers vorliegt
412    // REVIEW: "Wenn Steuerschuldnerschaft des Leistungsempfängers vorliegt" is the reverse charge mechanism. In UN/EDIFACT INVOIC, TAX category code "AE" (VAT Reverse Charge) signals this. The TAX segment composite C241 component 3 holds the tax category code. Medium confidence because element indices are derived from standard UN/EDIFACT TAX structure without a specific MIG segment structure reference to confirm the exact index for this INVOIC variant. (medium confidence)
413    fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
414        // Steuerschuldnerschaft des Leistungsempfängers = reverse charge (VAT category AE)
415        // In EDIFACT INVOIC the TAX segment carries duty/tax type category code.
416        // UN/EDIFACT code "AE" = VAT Reverse Charge (Steuerschuldnerschaft des Leistungsempfängers).
417        // TAX+7+VAT+++:::AE — elements[4][3] holds the tax category code.
418        let tax_segs = ctx.find_segments("TAX");
419        for seg in &tax_segs {
420            // elements[4] is the duty/tax account detail composite (C241 in UN/EDIFACT TAX)
421            // component 3 (index 3) is the duty/tax rate category code
422            if let Some(code) = seg.elements.get(4).and_then(|e| e.get(3)) {
423                if code == "AE" {
424                    return ConditionResult::True;
425                }
426            }
427        }
428        ConditionResult::False
429    }
430
431    /// [5] Wenn NAD+MR DE3207 &lt;&gt; „DE“
432    fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
433        let nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
434        match nads.first() {
435            Some(nad) => {
436                match nad
437                    .elements
438                    .get(8)
439                    .and_then(|e| e.first())
440                    .map(|s| s.as_str())
441                {
442                    Some(country) => ConditionResult::from(country != "DE"),
443                    None => ConditionResult::False, // segment absent → condition not applicable
444                }
445            }
446            None => ConditionResult::False, // segment absent → condition not applicable
447        }
448    }
449
450    /// [6] Wenn NAD+MR DE3207 = „DE“
451    fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
452        let nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
453        match nads.first() {
454            Some(nad) => {
455                match nad
456                    .elements
457                    .get(8)
458                    .and_then(|e| e.first())
459                    .map(|s| s.as_str())
460                {
461                    Some(country) => ConditionResult::from(country == "DE"),
462                    None => ConditionResult::False, // segment absent → condition not applicable
463                }
464            }
465            None => ConditionResult::False, // segment absent → condition not applicable
466        }
467    }
468
469    /// [9] Wenn SG26 DTM+203 nicht vorhanden
470    // REVIEW: Checks whether any SG26 instance lacks DTM+203 (Ausführungsdatum). Uses group navigator when available, falling back to message-wide absence check. DTM qualifier at elements[0][0]. (medium confidence)
471    fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
472        // DTM+203 (Ausführungsdatum) absence in the current SG26 context.
473        // Falls back to message-wide check when no group navigator available.
474        let nav = match ctx.navigator() {
475            Some(n) => n,
476            None => return ctx.lacks_qualifier("DTM", 0, "203"),
477        };
478        let sg26_count = nav.group_instance_count(&["SG26"]);
479        for i in 0..sg26_count {
480            let dtms = nav.find_segments_in_group("DTM", &["SG26"], i);
481            let has_203 = dtms.iter().any(|s| {
482                s.elements
483                    .first()
484                    .and_then(|e| e.first())
485                    .is_some_and(|v| v == "203")
486            });
487            if !has_203 {
488                return ConditionResult::True;
489            }
490        }
491        if sg26_count == 0 {
492            return ctx.lacks_qualifier("DTM", 0, "203");
493        }
494        ConditionResult::False
495    }
496
497    /// [10] Wenn SG26 DTM+155/156 nicht vorhanden
498    // REVIEW: Both DTM+155 (Abrechnungszeitraum Beginn) and DTM+156 (Ende) must be absent in the SG26. Uses group navigator; both must be absent simultaneously for the condition to be True. (medium confidence)
499    fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
500        // DTM+155 (Abrechnungszeitraum Beginn) and DTM+156 (Ende) both absent in SG26.
501        // Falls back to message-wide check when no group navigator available.
502        let nav = match ctx.navigator() {
503            Some(n) => n,
504            None => {
505                let no_155 = ctx.lacks_qualifier("DTM", 0, "155");
506                let no_156 = ctx.lacks_qualifier("DTM", 0, "156");
507                return match (no_155, no_156) {
508                    (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
509                    (ConditionResult::False, _) | (_, ConditionResult::False) => {
510                        ConditionResult::False
511                    }
512                    _ => ConditionResult::Unknown,
513                };
514            }
515        };
516        let sg26_count = nav.group_instance_count(&["SG26"]);
517        for i in 0..sg26_count {
518            let dtms = nav.find_segments_in_group("DTM", &["SG26"], i);
519            let has_155 = dtms.iter().any(|s| {
520                s.elements
521                    .first()
522                    .and_then(|e| e.first())
523                    .is_some_and(|v| v == "155")
524            });
525            let has_156 = dtms.iter().any(|s| {
526                s.elements
527                    .first()
528                    .and_then(|e| e.first())
529                    .is_some_and(|v| v == "156")
530            });
531            if !has_155 && !has_156 {
532                return ConditionResult::True;
533            }
534        }
535        if sg26_count == 0 {
536            let no_155 = ctx.lacks_qualifier("DTM", 0, "155");
537            let no_156 = ctx.lacks_qualifier("DTM", 0, "156");
538            return match (no_155, no_156) {
539                (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
540                (ConditionResult::False, _) | (_, ConditionResult::False) => ConditionResult::False,
541                _ => ConditionResult::Unknown,
542            };
543        }
544        ConditionResult::False
545    }
546
547    /// [11] Wenn Abschlag anfällt
548    /// EXTERNAL: Requires context from outside the message.
549    // REVIEW: Whether a deposit/advance payment (Abschlag) applies is business context that cannot be determined from the EDIFACT message structure alone — it depends on the contract type and billing arrangement. (medium confidence)
550    fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
551        ctx.external.evaluate("advance_payment_applies")
552    }
553
554    /// [12] Wenn SG26 QTY+136 vorhanden
555    fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
556        ctx.has_qualifier("QTY", 0, "136")
557    }
558
559    /// [13] Wenn vorausbezahlter Betrag vorliegt
560    /// EXTERNAL: Requires context from outside the message.
561    // REVIEW: Whether a prepaid amount (vorausbezahlter Betrag) is present is billing business context that cannot be derived solely from the EDIFACT segments in the message. (medium confidence)
562    fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
563        ctx.external.evaluate("prepaid_amount_present")
564    }
565
566    /// [14] Wenn in selben SG26 LIN DE7140 = "9990001000748" (Mehrmenge)
567    fn evaluate_14(&self, ctx: &EvaluationContext) -> ConditionResult {
568        // Check if any SG26 LIN has DE7140 (Produkt-/Leistungsnummer) = "9990001000748" (Mehrmenge).
569        // LIN is the entry segment of SG26; elements[2][0] = DE7140.
570        let lins = ctx.find_segments("LIN");
571        ConditionResult::from(lins.iter().any(|s| {
572            s.elements
573                .get(2)
574                .and_then(|e| e.first())
575                .is_some_and(|v| v == "9990001000748")
576        }))
577    }
578
579    /// [15] Wenn eine Bilanzierung erfolgt ist
580    /// EXTERNAL: Requires context from outside the message.
581    // REVIEW: Whether a balancing (Bilanzierung) has occurred is operational/business context external to the EDIFACT message itself. (medium confidence)
582    fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
583        ctx.external.evaluate("balancing_performed")
584    }
585
586    /// [16] Wenn eine Netznutzung erfolgt ist
587    /// EXTERNAL: Requires context from outside the message.
588    // REVIEW: Whether network usage (Netznutzung) has occurred is business context that cannot be determined solely from the EDIFACT message content. (medium confidence)
589    fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
590        ctx.external.evaluate("network_usage_performed")
591    }
592
593    /// [17] Wenn DTM+Z11 vorhanden
594    fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
595        ctx.has_qualifier("DTM", 0, "Z11")
596    }
597
598    /// [18] Wenn IMD++WIM nicht vorhanden
599    fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
600        ctx.lacks_qualifier("IMD", 1, "WIM")
601    }
602
603    /// [19] Wenn IMD++WIM vorhanden
604    fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
605        ctx.has_qualifier("IMD", 1, "WIM")
606    }
607
608    /// [20] Wenn fälliger Betrag (SG50 MOA+9) ≥ 0
609    // REVIEW: MOA+9 is fälliger Betrag (due amount): elements[0][0]='9', elements[0][1]=amount. Parses the numeric value and checks >= 0. (medium confidence)
610    fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
611        let moas = ctx.find_segments_with_qualifier("MOA", 0, "9");
612        match moas.first() {
613            Some(moa) => match moa.elements.first().and_then(|e| e.get(1)) {
614                Some(val) => match val.parse::<f64>() {
615                    Ok(n) => ConditionResult::from(n >= 0.0),
616                    Err(_) => ConditionResult::Unknown,
617                },
618                None => ConditionResult::False, // segment absent → condition not applicable
619            },
620            None => ConditionResult::False, // segment absent → condition not applicable
621        }
622    }
623
624    /// [21] Wenn fälliger Betrag (SG50 MOA+9) &lt; 0
625    // REVIEW: MOA+9 is fälliger Betrag. Parses numeric value and checks < 0. Inverse of condition 20. (medium confidence)
626    fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
627        let moas = ctx.find_segments_with_qualifier("MOA", 0, "9");
628        match moas.first() {
629            Some(moa) => match moa.elements.first().and_then(|e| e.get(1)) {
630                Some(val) => match val.parse::<f64>() {
631                    Ok(n) => ConditionResult::from(n < 0.0),
632                    Err(_) => ConditionResult::Unknown,
633                },
634                None => ConditionResult::False, // segment absent → condition not applicable
635            },
636            None => ConditionResult::False, // segment absent → condition not applicable
637        }
638    }
639
640    /// [22] Wenn vorhanden
641    fn evaluate_22(&self, _ctx: &EvaluationContext) -> ConditionResult {
642        // Wenn vorhanden — self-referential presence condition, always True when evaluated
643        ConditionResult::True
644    }
645
646    /// [23] Wenn im selben NAD DE3124 nicht vorhanden
647    // REVIEW: DE3124 resides in C058 at elements[2][0] for all NAD variants. 'Im selben NAD' means within the same NAD instance; evaluated message-wide as: any NAD where this component is absent or empty. (medium confidence)
648    fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
649        let nads = ctx.find_segments("NAD");
650        if nads.is_empty() {
651            return ConditionResult::Unknown;
652        }
653        // True if any NAD has DE3124 (elements[2][0] from C058) absent or empty
654        ConditionResult::from(nads.iter().any(|nad| {
655            nad.elements
656                .get(2)
657                .and_then(|e| e.first())
658                .map_or(true, |v| v.is_empty())
659        }))
660    }
661
662    /// [26] Wenn SG39 ALC+A+:Z04 vorhanden
663    fn evaluate_26(&self, ctx: &EvaluationContext) -> ConditionResult {
664        let alcs = ctx.find_segments_with_qualifier("ALC", 0, "A");
665        ConditionResult::from(alcs.iter().any(|alc| {
666            alc.elements
667                .get(1)
668                .and_then(|e| e.get(1))
669                .map_or(false, |v| v == "Z04")
670        }))
671    }
672
673    /// [27] Wenn SG39 ALC+C vorhanden
674    fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
675        ctx.has_qualifier("ALC", 0, "C")
676    }
677
678    /// [28] Wenn Zuschlag anfällt
679    /// EXTERNAL: Requires context from outside the message.
680    // REVIEW: 'Wenn Zuschlag anfällt' (if a surcharge applies) is a business-context determination that cannot be derived from EDIFACT segment content alone. It depends on contract terms or tariff configuration held outside the message. (medium confidence)
681    fn evaluate_28(&self, ctx: &EvaluationContext) -> ConditionResult {
682        ctx.external.evaluate("surcharge_applies")
683    }
684
685    /// [29] [Wenn DTM+155 (Abrechnungszeitraum Beginn) nicht größer 31.12.2015
686    // REVIEW: DTM+155 is Abrechnungszeitraum Beginn with format 303 (starts YYYYMMDD). 'nicht größer 31.12.2015' means <= 31.12.2015. Lexicographic comparison of 8-char ISO prefix is correct for this date format. (medium confidence)
687    fn evaluate_29(&self, ctx: &EvaluationContext) -> ConditionResult {
688        let dtms = ctx.find_segments_with_qualifier("DTM", 0, "155");
689        match dtms.first() {
690            Some(dtm) => {
691                match dtm.elements.first().and_then(|e| e.get(1)) {
692                    Some(val) if val.len() >= 8 => {
693                        // Format 303: CCYYMMDDHHMMSSZZZ — compare YYYYMMDD prefix
694                        let date_prefix = &val[..8];
695                        ConditionResult::from(date_prefix <= "20151231")
696                    }
697                    _ => ConditionResult::Unknown,
698                }
699            }
700            None => ConditionResult::False, // segment absent → condition not applicable
701        }
702    }
703
704    /// [30] Wenn MP-ID in NAD+MR nicht in der Rolle MGV oder KN
705    /// EXTERNAL: Requires context from outside the message.
706    // REVIEW: 'Wenn MP-ID in NAD+MR nicht in der Rolle MGV oder KN' requires knowledge of which market-participant IDs correspond to the MGV (Messstellenbetreiber Gasvertrieb) or KN (Kunde/Netz) roles. Role assignment is maintained in an external registry and cannot be determined from the NAD segment alone. (medium confidence)
707    fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
708        ctx.external.evaluate("recipient_not_mgv_or_kn")
709    }
710
711    /// [31] Wenn MP-ID in NAD+MR in der Rolle MGV
712    /// EXTERNAL: Requires context from outside the message.
713    fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
714        ctx.external.evaluate("recipient_is_mgv")
715    }
716
717    /// [32] Wenn SG39 ALC+A+:Z01 (Gemeinderabatt) vorhanden
718    fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
719        ctx.has_qualified_value("ALC", 0, "A", 1, 1, &["Z01"])
720    }
721
722    /// [34] Wenn in Ursprungsrechnung vorhanden
723    /// EXTERNAL: Requires context from outside the message.
724    // REVIEW: "Wenn in Ursprungsrechnung vorhanden" means the condition is met when the corresponding value was present in the referenced original invoice. That original document is not available inside the current EDIFACT message, so this is an external business-context check. (medium confidence)
725    fn evaluate_34(&self, ctx: &EvaluationContext) -> ConditionResult {
726        ctx.external.evaluate("in_original_invoice")
727    }
728
729    /// [36] Wenn DTM+156 (Abrechnungszeitraum Ende) ≥ 01.12.2019
730    // REVIEW: DTM+156 (Abrechnungszeitraum Ende) uses format 303 (CCYYMMDDHHMM), so lexicographic string comparison is correct. German date 01.12.2019 = December 1, 2019 = "201912010000". (medium confidence)
731    fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
732        let dtm_segs = ctx.find_segments_with_qualifier("DTM", 0, "156");
733        match dtm_segs.first() {
734            Some(dtm) => {
735                match dtm
736                    .elements
737                    .first()
738                    .and_then(|e| e.get(1))
739                    .map(|s| s.as_str())
740                {
741                    Some(value) if !value.is_empty() => {
742                        // Format 303 = CCYYMMDDHHMM; lexicographic comparison works for zero-padded timestamps.
743                        // 01.12.2019 (DD.MM.YYYY) = December 1, 2019 = "201912010000"
744                        ConditionResult::from(value >= "201912010000")
745                    }
746                    _ => ConditionResult::Unknown,
747                }
748            }
749            None => ConditionResult::False, // segment absent → condition not applicable
750        }
751    }
752
753    /// [37] Wenn Lieferschein zuvor ausgetauscht wurde
754    /// EXTERNAL: Requires context from outside the message.
755    // REVIEW: "Wenn Lieferschein zuvor ausgetauscht wurde" depends on whether a delivery note (Lieferschein) was exchanged in a prior business transaction. This cannot be determined from the current EDIFACT message alone; it requires external business context. (medium confidence)
756    fn evaluate_37(&self, ctx: &EvaluationContext) -> ConditionResult {
757        ctx.external.evaluate("delivery_note_previously_exchanged")
758    }
759
760    /// [40] Es sind nur die Artikelnummern erlaubt, die in der Codeliste der Artikelnummern und Artikel-ID mit dem entsprechenden Prüfidentifikator versehen sind oder Artikel-ID aus der Codeliste der Artikeln...
761    /// EXTERNAL: Requires context from outside the message.
762    // REVIEW: Condition requires validating that the article number/ID appears in the external "Codeliste der Artikelnummern und Artikel-ID" for the given Prüfidentifikator. Code-list membership cannot be verified from the EDIFACT message structure alone; it is an external reference-data check. (medium confidence)
763    fn evaluate_40(&self, ctx: &EvaluationContext) -> ConditionResult {
764        ctx.external.evaluate("artikel_id_in_codeliste")
765    }
766
767    /// [41] Wenn IMD++SOR vorhanden
768    fn evaluate_41(&self, ctx: &EvaluationContext) -> ConditionResult {
769        let imds = ctx.find_segments("IMD");
770        ConditionResult::from(imds.iter().any(|s| {
771            s.elements
772                .get(1)
773                .and_then(|e| e.first())
774                .is_some_and(|v| v == "SOR")
775        }))
776    }
777
778    /// [42] Wenn DTM+155 (Abrechnungszeitraum Beginn) ≥ 1.1.2023 0:00 gesetzlicher deutscher Zeit
779    // REVIEW: DTM+155 (Abrechnungszeitraum Beginn) uses format 303 (CCYYMMDDHHMM). January 1, 2023 at 00:00 = "202301010000". Lexicographic comparison is correct for zero-padded CCYYMMDDHHMM. (medium confidence)
780    fn evaluate_42(&self, ctx: &EvaluationContext) -> ConditionResult {
781        let dtm_segs = ctx.find_segments_with_qualifier("DTM", 0, "155");
782        match dtm_segs.first() {
783            Some(dtm) => {
784                match dtm
785                    .elements
786                    .first()
787                    .and_then(|e| e.get(1))
788                    .map(|s| s.as_str())
789                {
790                    Some(value) if !value.is_empty() => {
791                        // Format 303 = CCYYMMDDHHMM; 1.1.2023 0:00 gesetzlicher Zeit = "202301010000"
792                        ConditionResult::from(value >= "202301010000")
793                    }
794                    _ => ConditionResult::Unknown,
795                }
796            }
797            None => ConditionResult::False, // segment absent → condition not applicable
798        }
799    }
800
801    /// [44] Wenn IMD++ABS nicht vorhanden
802    fn evaluate_44(&self, ctx: &EvaluationContext) -> ConditionResult {
803        let imds = ctx.find_segments("IMD");
804        ConditionResult::from(!imds.iter().any(|s| {
805            s.elements
806                .get(1)
807                .and_then(|e| e.first())
808                .is_some_and(|v| v == "ABS")
809        }))
810    }
811
812    /// [45] Nur MP-ID aus Sparte Strom
813    /// EXTERNAL: Requires context from outside the message.
814    // REVIEW: "Nur MP-ID aus Sparte Strom" means only market-participant IDs that belong to the electricity sector are allowed. Determining which IDs are registered for Sparte Strom requires an external code list or market master data lookup and cannot be inferred from the EDIFACT message content. (medium confidence)
815    fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
816        ctx.external.evaluate("mp_id_sparte_strom")
817    }
818
819    /// [47] Wenn IMD++Z43 vorhanden
820    fn evaluate_47(&self, ctx: &EvaluationContext) -> ConditionResult {
821        let imds = ctx.find_segments("IMD");
822        ConditionResult::from(imds.iter().any(|s| {
823            s.elements
824                .get(1)
825                .and_then(|e| e.first())
826                .is_some_and(|v| v == "Z43")
827        }))
828    }
829
830    /// [48] Wenn IMD++Z44 vorhanden
831    fn evaluate_48(&self, ctx: &EvaluationContext) -> ConditionResult {
832        let imds = ctx.find_segments("IMD");
833        ConditionResult::from(imds.iter().any(|s| {
834            s.elements
835                .get(1)
836                .and_then(|e| e.first())
837                .is_some_and(|v| v == "Z44")
838        }))
839    }
840
841    /// [49] Wenn IMD++Z43 und IMD+Z44 nicht vorhanden
842    fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
843        let imds = ctx.find_segments("IMD");
844        let has_z43 = imds.iter().any(|s| {
845            s.elements
846                .get(1)
847                .and_then(|e| e.first())
848                .is_some_and(|v| v == "Z43")
849        });
850        let has_z44 = imds.iter().any(|s| {
851            s.elements
852                .get(1)
853                .and_then(|e| e.first())
854                .is_some_and(|v| v == "Z44")
855        });
856        ConditionResult::from(!has_z43 && !has_z44)
857    }
858
859    /// [50] Wenn IMD++SOR nicht vorhanden
860    fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
861        let imds = ctx.find_segments("IMD");
862        ConditionResult::from(!imds.iter().any(|s| {
863            s.elements
864                .get(1)
865                .and_then(|e| e.first())
866                .is_some_and(|v| v == "SOR")
867        }))
868    }
869
870    /// [51] Wenn SG26 DTM+156 (Positionsbezogener Abrechnungszeitraum Ende) ≤ 1.1.2023 0:00 gesetzlicher deutscher Zeit
871    // REVIEW: DTM+156 in SG26 (Positionsbezogener Abrechnungszeitraum Ende) uses format 303 (CCYYMMDDHHMM). January 1, 2023 at 00:00 = "202301010000". Without SG26-scoped group navigation, message-wide search is used; qualifier 156 in the SG26 context is distinct from the message-level 156, but without group navigation this is the best available approximation. (medium confidence)
872    fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
873        let dtm_segs = ctx.find_segments_with_qualifier("DTM", 0, "156");
874        match dtm_segs.first() {
875            Some(dtm) => {
876                match dtm
877                    .elements
878                    .first()
879                    .and_then(|e| e.get(1))
880                    .map(|s| s.as_str())
881                {
882                    Some(value) if !value.is_empty() => {
883                        // Format 303 = CCYYMMDDHHMM; 1.1.2023 0:00 = "202301010000"
884                        ConditionResult::from(value <= "202301010000")
885                    }
886                    _ => ConditionResult::Unknown,
887                }
888            }
889            None => ConditionResult::False, // segment absent → condition not applicable
890        }
891    }
892
893    /// [53] Wenn IMD++Z45 vorhanden
894    fn evaluate_53(&self, ctx: &EvaluationContext) -> ConditionResult {
895        ctx.has_qualifier("IMD", 1, "Z45")
896    }
897
898    /// [54] Wenn IMD++ABR/JVR/ZVR vorhanden
899    fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
900        {
901            let segs = ctx.find_segments("IMD");
902            if segs.is_empty() {
903                return ConditionResult::False;
904            }
905            ConditionResult::from(segs.iter().any(|s| {
906                s.elements
907                    .get(1)
908                    .and_then(|e| e.first())
909                    .is_some_and(|v| matches!(v.as_str(), "ABR" | "JVR" | "ZVR"))
910            }))
911        }
912    }
913
914    /// [55] Wenn IMD++ABS vorhanden
915    fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
916        ctx.has_qualifier("IMD", 1, "ABS")
917    }
918
919    /// [56] Wenn DTM+137 (Nachrichtendatum ≥ 1.1.2023 0:00 gesetzlicher deutscher Zeit
920    // REVIEW: DTM+137 Nachrichtendatum >= 2023-01-01. Parses the date value (format 303 = YYYYMMDDHHmm) and compares the first 8 characters against '20230101'. String comparison is valid for ISO date format. (medium confidence)
921    fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
922        {
923            let segs = ctx.find_segments_with_qualifier("DTM", 0, "137");
924            match segs.first() {
925                Some(dtm) => match dtm.elements.first().and_then(|e| e.get(1)) {
926                    Some(val) if val.len() >= 8 => {
927                        ConditionResult::from(val[..8].cmp("20230101") != std::cmp::Ordering::Less)
928                    }
929                    _ => ConditionResult::Unknown,
930                },
931                None => ConditionResult::False, // segment absent → condition not applicable
932            }
933        }
934    }
935
936    /// [57] Wenn DTM+156 (Abrechnungszeitraum Ende) ≤ 1.1.2023 0:00 gesetzlicher deutscher Zeit
937    // REVIEW: DTM+156 (Abrechnungszeitraum Ende) <= 2023-01-01 0:00. Compares first 8 chars of date value against '20230101'. Returns True when the end date is on or before Jan 1 2023. (medium confidence)
938    fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
939        {
940            let segs = ctx.find_segments_with_qualifier("DTM", 0, "156");
941            match segs.first() {
942                Some(dtm) => match dtm.elements.first().and_then(|e| e.get(1)) {
943                    Some(val) if val.len() >= 8 => ConditionResult::from(
944                        val[..8].cmp("20230101") != std::cmp::Ordering::Greater,
945                    ),
946                    _ => ConditionResult::Unknown,
947                },
948                None => ConditionResult::False, // segment absent → condition not applicable
949            }
950        }
951    }
952
953    /// [58] Wenn in dieser SG52 MOA+113 vorhanden
954    fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
955        ctx.any_group_has_qualifier("MOA", 0, "113", &["SG52"])
956    }
957
958    /// [59] Wenn SG26 DTM+155 (Positionsbezogener Abrechnungszeitraum Beginn) ≥ 1.1.2023 0:00 gesetzlicher deutscher Zeit
959    // REVIEW: SG26 DTM+155 (Positionsbezogener Abrechnungszeitraum Beginn) >= 2023-01-01 0:00. Finds DTM with qualifier 155 and compares first 8 chars of the date value against '20230101'. (medium confidence)
960    fn evaluate_59(&self, ctx: &EvaluationContext) -> ConditionResult {
961        {
962            let segs = ctx.find_segments_with_qualifier("DTM", 0, "155");
963            match segs.first() {
964                Some(dtm) => match dtm.elements.first().and_then(|e| e.get(1)) {
965                    Some(val) if val.len() >= 8 => {
966                        ConditionResult::from(val[..8].cmp("20230101") != std::cmp::Ordering::Less)
967                    }
968                    _ => ConditionResult::Unknown,
969                },
970                None => ConditionResult::False, // segment absent → condition not applicable
971            }
972        }
973    }
974
975    /// [60] Wenn DTM+203 DE2379 in demselben Segment mit Wert 303 vorhanden
976    fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
977        ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "303"))
978    }
979
980    /// [61] Wenn DTM+203 DE2379 in demselben Segment mit Wert 102 vorhanden
981    fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
982        ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "102"))
983    }
984
985    /// [64] Wenn DTM+156 (Abrechnungszeitraum Ende) ≤ 1.1.2024 0:00 gesetzlicher deutscher Zeit
986    // REVIEW: DTM+156 (Abrechnungszeitraum Ende) <= 2024-01-01 0:00. Same pattern as condition 57 but with threshold '20240101'. (medium confidence)
987    fn evaluate_64(&self, ctx: &EvaluationContext) -> ConditionResult {
988        {
989            let segs = ctx.find_segments_with_qualifier("DTM", 0, "156");
990            match segs.first() {
991                Some(dtm) => match dtm.elements.first().and_then(|e| e.get(1)) {
992                    Some(val) if val.len() >= 8 => ConditionResult::from(
993                        val[..8].cmp("20240101") != std::cmp::Ordering::Greater,
994                    ),
995                    _ => ConditionResult::Unknown,
996                },
997                None => ConditionResult::False, // segment absent → condition not applicable
998            }
999        }
1000    }
1001
1002    /// [65] Wenn SG26 DTM+155 (Positionsbezogener Abrechnungszeitraum Beginn) ≥ 1.1.2024 0:00 gesetzlicher deutscher Zeit
1003    // REVIEW: SG26 DTM+155 (Positionsbezogener Abrechnungszeitraum Beginn) >= 2024-01-01 0:00. Same pattern as condition 59 but with threshold '20240101'. (medium confidence)
1004    fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
1005        {
1006            let segs = ctx.find_segments_with_qualifier("DTM", 0, "155");
1007            match segs.first() {
1008                Some(dtm) => match dtm.elements.first().and_then(|e| e.get(1)) {
1009                    Some(val) if val.len() >= 8 => {
1010                        ConditionResult::from(val[..8].cmp("20240101") != std::cmp::Ordering::Less)
1011                    }
1012                    _ => ConditionResult::Unknown,
1013                },
1014                None => ConditionResult::False, // segment absent → condition not applicable
1015            }
1016        }
1017    }
1018
1019    /// [66] Wenn IMD++KON vorhanden
1020    fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
1021        ctx.has_qualifier("IMD", 1, "KON")
1022    }
1023
1024    /// [70] Wenn IMD++Z45 nicht vorhanden
1025    fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
1026        let imd_segments = ctx.find_segments("IMD");
1027        let found = imd_segments.iter().any(|s| {
1028            s.elements
1029                .get(1)
1030                .and_then(|e| e.first())
1031                .is_some_and(|v| v == "Z45")
1032        });
1033        ConditionResult::from(!found)
1034    }
1035
1036    /// [71] Wenn DTM+155 (Abrechnungszeitraum Beginn) ≥ 1.1.2024 0:00 gesetzlicher deutscher Zeit
1037    // REVIEW: DTM+155 means elements[0][0]=="155". Value is at elements[0][1] in 303 format (YYYYMMDDhhmm). String comparison works for chronological ordering. Timezone nuance (German legal time) ignored — medium confidence. (medium confidence)
1038    fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
1039        let dtm_segments = ctx.find_segments_with_qualifier("DTM", 0, "155");
1040        match dtm_segments.first() {
1041            Some(dtm) => {
1042                match dtm
1043                    .elements
1044                    .first()
1045                    .and_then(|e| e.get(1))
1046                    .map(|s| s.as_str())
1047                {
1048                    Some(value) if !value.is_empty() => {
1049                        // 303 format: YYYYMMDDhhmm — lexicographic comparison is chronological for zero-padded strings
1050                        ConditionResult::from(value >= "202401010000")
1051                    }
1052                    _ => ConditionResult::Unknown,
1053                }
1054            }
1055            None => ConditionResult::False, // segment absent → condition not applicable
1056        }
1057    }
1058
1059    /// [72] Wenn MP-ID in NAD+MR in der Rolle LF
1060    /// EXTERNAL: Requires context from outside the message.
1061    // REVIEW: Whether the MP-ID in NAD+MR has the market role LF (Lieferant) cannot be determined from the EDIFACT message alone — it requires a business registry lookup. The NAD+MR segment only provides the party identifier, not the assigned market role. (medium confidence)
1062    fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
1063        ctx.external.evaluate("recipient_is_lf")
1064    }
1065
1066    /// [73] Wenn IMD++MSB vorhanden
1067    fn evaluate_73(&self, ctx: &EvaluationContext) -> ConditionResult {
1068        let imd_segments = ctx.find_segments("IMD");
1069        let found = imd_segments.iter().any(|s| {
1070            s.elements
1071                .get(1)
1072                .and_then(|e| e.first())
1073                .is_some_and(|v| v == "MSB")
1074        });
1075        ConditionResult::from(found)
1076    }
1077
1078    /// [74] wenn im DE3155 in demselben COM der Code EM vorhanden ist
1079    fn evaluate_74(&self, ctx: &EvaluationContext) -> ConditionResult {
1080        let com_segments = ctx.find_segments("COM");
1081        let found = com_segments.iter().any(|s| {
1082            s.elements
1083                .first()
1084                .and_then(|e| e.get(1))
1085                .is_some_and(|v| v == "EM")
1086        });
1087        ConditionResult::from(found)
1088    }
1089
1090    /// [75] wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
1091    fn evaluate_75(&self, ctx: &EvaluationContext) -> ConditionResult {
1092        let com_segments = ctx.find_segments("COM");
1093        let found = com_segments.iter().any(|s| {
1094            s.elements
1095                .first()
1096                .and_then(|e| e.get(1))
1097                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
1098        });
1099        ConditionResult::from(found)
1100    }
1101
1102    /// [76] Wenn MP-ID in NAD+MR in der Rolle NB
1103    /// EXTERNAL: Requires context from outside the message.
1104    // REVIEW: Whether the MP-ID in NAD+MR has the market role NB (Netzbetreiber) cannot be determined from the EDIFACT message alone — it requires a business registry lookup. The NAD+MR segment only provides the party identifier, not the assigned market role. (medium confidence)
1105    fn evaluate_76(&self, ctx: &EvaluationContext) -> ConditionResult {
1106        ctx.external.evaluate("recipient_is_nb")
1107    }
1108
1109    /// [77] Wenn SG26 DTM+156 (Positionsbezogener Abrechnungszeitraum Ende) ≤ 1.1.2024 0:00 gesetzlicher deutscher Zeit
1110    // REVIEW: SG26 DTM+156 means elements[0][0]=="156". Value at elements[0][1] in 303 format. Compare <= "202401010000" (Jan 1 2024 00:00). Medium confidence due to timezone nuance. (medium confidence)
1111    fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
1112        let dtm_segments = ctx.find_segments_with_qualifier("DTM", 0, "156");
1113        match dtm_segments.first() {
1114            Some(dtm) => {
1115                match dtm
1116                    .elements
1117                    .first()
1118                    .and_then(|e| e.get(1))
1119                    .map(|s| s.as_str())
1120                {
1121                    Some(value) if !value.is_empty() => {
1122                        // 303 format: YYYYMMDDhhmm — lexicographic comparison is chronological
1123                        ConditionResult::from(value <= "202401010000")
1124                    }
1125                    _ => ConditionResult::Unknown,
1126                }
1127            }
1128            None => ConditionResult::False, // segment absent → condition not applicable
1129        }
1130    }
1131
1132    /// [78] Wenn GEI+Z08 nicht vorhanden
1133    // REVIEW: GEI schema shows elements[0] qualifier is always Z01, while the specification code is at elements[1][0] (DE7365) with codes Z01-Z06 +3 more. 'GEI+Z08' in AHB shorthand refers to elements[1][0]=="Z08". Absence check returns True when not found. (medium confidence)
1134    fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
1135        let found = ctx.find_segments("GEI").iter().any(|s| {
1136            s.elements
1137                .get(1)
1138                .and_then(|e| e.first())
1139                .is_some_and(|v| v == "Z08")
1140        });
1141        ConditionResult::from(!found)
1142    }
1143
1144    /// [79] Wenn GEI+Z09 nicht vorhanden
1145    // REVIEW: Same pattern as condition 78. 'GEI+Z09' in AHB shorthand refers to elements[1][0]=="Z09". (medium confidence)
1146    fn evaluate_79(&self, ctx: &EvaluationContext) -> ConditionResult {
1147        let found = ctx.find_segments("GEI").iter().any(|s| {
1148            s.elements
1149                .get(1)
1150                .and_then(|e| e.first())
1151                .is_some_and(|v| v == "Z09")
1152        });
1153        ConditionResult::from(!found)
1154    }
1155
1156    /// [80] Wenn GEI+Z01 nicht vorhanden
1157    // REVIEW: 'GEI+Z01' refers to elements[1][0]=="Z01" (the specification code, not the fixed qualifier at elements[0]). Absence check. (medium confidence)
1158    fn evaluate_80(&self, ctx: &EvaluationContext) -> ConditionResult {
1159        let found = ctx.find_segments("GEI").iter().any(|s| {
1160            s.elements
1161                .get(1)
1162                .and_then(|e| e.first())
1163                .is_some_and(|v| v == "Z01")
1164        });
1165        ConditionResult::from(!found)
1166    }
1167
1168    /// [81] Wenn GEI+Z04 nicht vorhanden
1169    // REVIEW: Same pattern as condition 78. 'GEI+Z04' refers to elements[1][0]=="Z04". (medium confidence)
1170    fn evaluate_81(&self, ctx: &EvaluationContext) -> ConditionResult {
1171        let found = ctx.find_segments("GEI").iter().any(|s| {
1172            s.elements
1173                .get(1)
1174                .and_then(|e| e.first())
1175                .is_some_and(|v| v == "Z04")
1176        });
1177        ConditionResult::from(!found)
1178    }
1179
1180    /// [82] Wenn GEI+Z05 nicht vorhanden
1181    // REVIEW: Same pattern as condition 78. 'GEI+Z05' refers to elements[1][0]=="Z05". (medium confidence)
1182    fn evaluate_82(&self, ctx: &EvaluationContext) -> ConditionResult {
1183        let found = ctx.find_segments("GEI").iter().any(|s| {
1184            s.elements
1185                .get(1)
1186                .and_then(|e| e.first())
1187                .is_some_and(|v| v == "Z05")
1188        });
1189        ConditionResult::from(!found)
1190    }
1191
1192    /// [84] Wenn in dieser SG39 ALC+C+:Z02 / Z03 / Z04 vorhanden
1193    fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
1194        ctx.any_group_has_qualified_value("ALC", 0, "C", 1, 1, &["Z02", "Z03", "Z04"], &["SG39"])
1195    }
1196
1197    /// [85] Wenn in diesem Segment DE6411 = DAY
1198    // REVIEW: DE6411 is the measurement unit code, appearing in QTY segments as C186 composite: elements[0][0]=DE6063 (qualifier), elements[0][1]=DE6060 (quantity), elements[0][2]=DE6411 (unit code). 'DAY' is the unit code for daily quantities. Medium confidence as the specific segment context ('in diesem Segment') may require group scoping. (medium confidence)
1199    fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
1200        let qty_segments = ctx.find_segments("QTY");
1201        let found = qty_segments.iter().any(|s| {
1202            s.elements
1203                .first()
1204                .and_then(|e| e.get(2))
1205                .is_some_and(|v| v == "DAY")
1206        });
1207        ConditionResult::from(found)
1208    }
1209
1210    /// [86] Wenn in diesem Segment DE6411 = MON/ANN
1211    // REVIEW: DE6411 is the unit of measure code in QTY segments at elements[0][2] (C186 component 2). 'In diesem Segment' means within the current QTY context. Check message-wide for QTY with MON or ANN unit code. (medium confidence)
1212    fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
1213        let qty_segments = ctx.find_segments("QTY");
1214        ConditionResult::from(qty_segments.iter().any(|s| {
1215            s.elements
1216                .first()
1217                .and_then(|e| e.get(2))
1218                .map(|v| v == "MON" || v == "ANN")
1219                .unwrap_or(false)
1220        }))
1221    }
1222
1223    /// [88] Wenn DTM+155 (Abrechnungszeitraum Beginn) ≥ 1.1.2026 0:00 gesetzlicher deutscher Zeit
1224    fn evaluate_88(&self, ctx: &EvaluationContext) -> ConditionResult {
1225        let dtm_segs = ctx.find_segments_with_qualifier("DTM", 0, "155");
1226        match dtm_segs.first() {
1227            Some(dtm) => {
1228                match dtm
1229                    .elements
1230                    .first()
1231                    .and_then(|e| e.get(1))
1232                    .map(|s| s.as_str())
1233                {
1234                    Some(value) if value.len() >= 8 => {
1235                        let date_part = &value[..8];
1236                        ConditionResult::from(date_part >= "20260101")
1237                    }
1238                    _ => ConditionResult::Unknown,
1239                }
1240            }
1241            None => ConditionResult::False, // segment absent → condition not applicable
1242        }
1243    }
1244
1245    /// [490] wenn Wert in diesem DE, an der Stelle CCYYMMDD ein Datum aus dem angegeben Zeitraum der Tabelle Kapitel 3.5 „Prozesszeitpunkt bei MESZ mit UTC“ ist
1246    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
1247        match ctx.resolved_value {
1248            Some(val) => is_mesz_utc(val),
1249            None => ConditionResult::False,
1250        }
1251    }
1252
1253    /// [491] wenn Wert in diesem DE, an der Stelle CCYYMMDD ein Datum aus dem angegeben Zeitraum der Tabelle Kapitel 3.6 „Prozesszeitpunkt bei MEZ mit UTC“ ist
1254    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
1255        match ctx.resolved_value {
1256            Some(val) => is_mez_utc(val),
1257            None => ConditionResult::False,
1258        }
1259    }
1260
1261    /// [492] Wenn MP-ID in NAD+MR (Nachrichtenempfänger) aus Sparte Strom
1262    /// EXTERNAL: Requires context from outside the message.
1263    // REVIEW: Checks whether the NAD+MR (message recipient) MP-ID belongs to the electricity (Strom) sector. The NAD+MR element structure is known (elements[0]=MR, elements[1][0]=MP-ID), but sector membership cannot be determined from the EDIFACT message content alone — it requires an external market participant registry. Delegated to external provider. (medium confidence)
1264    fn evaluate_492(&self, ctx: &EvaluationContext) -> ConditionResult {
1265        ctx.external.evaluate("recipient_is_strom")
1266    }
1267
1268    /// [493] Wenn MP-ID in NAD+MR (Nachrichtenempfänger) aus Sparte Gas
1269    /// EXTERNAL: Requires context from outside the message.
1270    // REVIEW: Checks whether the NAD+MR (message recipient) MP-ID belongs to the gas (Gas) sector. Same reasoning as 492 — sector membership cannot be determined from the EDIFACT message alone; requires external market participant registry. Delegated to external provider. (medium confidence)
1271    fn evaluate_493(&self, ctx: &EvaluationContext) -> ConditionResult {
1272        ctx.external.evaluate("recipient_is_gas")
1273    }
1274
1275    /// [501] Hinweis: Dokumentennummer der ORDERS
1276    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
1277        // Hinweis: Dokumentennummer der ORDERS — informational note, always applies
1278        ConditionResult::True
1279    }
1280
1281    /// [502] Hinweis: Dokumentennummer der Bilanzierungs-MSCONS
1282    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
1283        // Hinweis: Dokumentennummer der Bilanzierungs-MSCONS — informational note, always applies
1284        ConditionResult::True
1285    }
1286
1287    /// [503] Hinweis: Ein positiver Betrag ist eine Forderung des Rechnungsstellers.
1288    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
1289        // Hinweis: Ein positiver Betrag ist eine Forderung des Rechnungsstellers — informational note, always applies
1290        ConditionResult::True
1291    }
1292
1293    /// [504] Hinweis: Ein positiver Betrag ist eine Forderung des Rechnungsempfängers.
1294    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
1295        // Hinweis: Ein positiver Betrag ist eine Forderung des Rechnungsempfängers — informational note, always applies
1296        ConditionResult::True
1297    }
1298
1299    /// [507] Hinweis: Dokumentennummer der SSQNOT
1300    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
1301        // Hinweis: Dokumentennummer der SSQNOT — informational note, always applies
1302        ConditionResult::True
1303    }
1304
1305    /// [508] Hinweis: Dokumentennummer der QUOTES
1306    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
1307        // Hinweis: Dokumentennummer der QUOTES — informational note, always applies
1308        ConditionResult::True
1309    }
1310
1311    /// [509] Hinweis: Verwendung der ID der Marktlokation
1312    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
1313        // Hinweis: Verwendung der ID der Marktlokation — informational note, always applies
1314        ConditionResult::True
1315    }
1316
1317    /// [510] Hinweis: Verwendung der ID der Messlokation
1318    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
1319        // Hinweis: Verwendung der ID der Messlokation — informational note, always applies
1320        ConditionResult::True
1321    }
1322
1323    /// [512] Hinweis: Hier ist entweder der Betrag aus MOA+203 oder der um den gültigen Steuerbetrag erhöhte Betrag aus MOA+203 anzugeben.
1324    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
1325        // Hinweis: Hier ist entweder der Betrag aus MOA+203 oder der um den gültigen Steuerbetrag erhöhte Betrag aus MOA+203 anzugeben — informational note, always applies
1326        ConditionResult::True
1327    }
1328
1329    /// [513] Hinweis: Hier ist das Ergebnis der Multiplikation von MOA+25 mit PCD+3 anzugeben.
1330    fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
1331        // Hinweis: Hier ist das Ergebnis der Multiplikation von MOA+25 mit PCD+3 anzugeben — informational note, always applies
1332        ConditionResult::True
1333    }
1334
1335    /// [514] Hinweis: Dokumentennummer der Lieferschein-MSCONS
1336    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
1337        // Hinweis: Dokumentennummer der Lieferschein-MSCONS — informational note, always applies
1338        ConditionResult::True
1339    }
1340
1341    /// [515] Hinweis: BGM DE1004 aus INVOIC-Nachricht, die storniert werden soll
1342    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
1343        // Hinweis: BGM DE1004 aus INVOIC-Nachricht, die storniert werden soll — informational note, always applies
1344        ConditionResult::True
1345    }
1346
1347    /// [516] Hinweis: Ein Lieferschein zu einer Rechnung ist für alle Abrechnungszeiträume, die erstmals nach dem 1.12.2019 abgerechnet werden und für alle Abrechnungszeiträume, für die sich nach dem 1.12....
1348    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
1349        // Hinweis: Ein Lieferschein zu einer Rechnung ist für alle Abrechnungszeiträume, die erstmals nach dem 1.12.2019 abgerechnet werden und für alle Abrechnungszeiträume, für die sich nach dem 1.12.2019 geänderte Mengen oder Leistungswerte ergeben, nötig.
1350        ConditionResult::True
1351    }
1352
1353    /// [517] Hinweis: Dokumentennummer der PDF-Kapazitätsrechnung
1354    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
1355        // Hinweis: Dokumentennummer der PDF-Kapazitätsrechnung — informational annotation, always applies
1356        ConditionResult::True
1357    }
1358
1359    /// [518] Hinweis:  Im Fall der Stornierung des Auftrags der Unterbrechung: Der Tag an dem der NB die Stornierung empfangen hat Bei erfolgreicher Sperrung: Tag der durchgeführten Sperrung Bei erfolgloser Sp...
1360    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
1361        // Hinweis: Date semantics for cancellation/blocking scenarios — informational annotation, always applies
1362        ConditionResult::True
1363    }
1364
1365    /// [519] Hinweis: Stornierte Abschlagsrechnungen sind nicht aufzuführen
1366    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
1367        // Hinweis: Stornierte Abschlagsrechnungen sind nicht aufzuführen — informational annotation, always applies
1368        ConditionResult::True
1369    }
1370
1371    /// [520] Hinweis: Es sind nur die Artikel-IDs aus dem Preisblatt erlaubt
1372    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
1373        // Hinweis: Es sind nur die Artikel-IDs aus dem Preisblatt erlaubt — informational annotation, always applies
1374        ConditionResult::True
1375    }
1376
1377    /// [521] Hinweis: BGM DE1004 aus der INVOIC-Nachricht, für die Verzugskosten erhoben werden
1378    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
1379        // Hinweis: BGM DE1004 aus der INVOIC-Nachricht, für die Verzugskosten erhoben werden — informational note, always applies
1380        ConditionResult::True
1381    }
1382
1383    /// [522] Hinweis: Verwendung der ID der Netzlokation
1384    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
1385        // Hinweis: Verwendung der ID der Netzlokation — informational note, always applies
1386        ConditionResult::True
1387    }
1388
1389    /// [523] Hinweis: Verwendung der ID der Steuerbaren Ressource
1390    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
1391        // Hinweis: Verwendung der ID der Steuerbaren Ressource — informational note, always applies
1392        ConditionResult::True
1393    }
1394
1395    /// [524] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
1396    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
1397        // Hinweis: Es darf nur eine Information im DE3148 übermittelt werden — informational note, always applies
1398        ConditionResult::True
1399    }
1400
1401    /// [525] Hinweis: Verwendung, sofern Netzentgelte geringer als die Pauschale Netzentgeltreduzierung
1402    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
1403        // Hinweis: Verwendung, sofern Netzentgelte geringer als die Pauschale Netzentgeltreduzierung — informational note, always applies
1404        ConditionResult::True
1405    }
1406
1407    /// [526] Hinweis: Der hier angegebene Geldbetrag muss mit dem identisch sein, der in SG50 MOA+77 im DE5004 der Abschlagsrechnung steht, die die Rechnungsnummer hat, die in dieser SG50 in SG51-RFF+AFL in DE1...
1408    fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
1409        // Hinweis: Der hier angegebene Geldbetrag muss mit dem identisch sein, der in SG50 MOA+77
1410        // im DE5004 der Abschlagsrechnung steht, die die Rechnungsnummer hat, die in dieser SG50
1411        // in SG51-RFF+AFL in DE1154 genannt ist.
1412        // Informational note — always applies
1413        ConditionResult::True
1414    }
1415
1416    /// [902] Format: Möglicher Wert: ≥ 0
1417    // REVIEW: Format condition requiring value >= 0, applied to MOA amount (DE5004 at elements[0][1]). Medium confidence because exact segment/group context is inferred from INVOIC structure — the full AHB table row would clarify which specific MOA qualifier this applies to. (medium confidence)
1418    fn evaluate_902(&self, ctx: &EvaluationContext) -> ConditionResult {
1419        ctx.format_check("MOA", 0, 1, |val| validate_numeric(val, ">=", 0.0))
1420    }
1421
1422    /// [906] Format: max. 3 Nachkommastellen
1423    // REVIEW: Format condition requiring at most 3 decimal places. Most likely applies to MOA DE5004 (Geldbetrag) in INVOIC. Medium confidence — the exact MOA qualifier or group (SG27/SG42/SG50/SG52) depends on the AHB table row context not included in the condition description alone. (medium confidence)
1424    fn evaluate_906(&self, ctx: &EvaluationContext) -> ConditionResult {
1425        ctx.format_check("MOA", 0, 1, |val| validate_max_decimal_places(val, 3))
1426    }
1427
1428    /// [908] Format: Mögliche Werte: 1 bis n
1429    // REVIEW: Format condition requiring value in range 1..n (strictly positive). Applied to QTY quantity values in INVOIC (DE6060 at elements[0][1]). Medium confidence — the exact segment context depends on the full AHB table row. (medium confidence)
1430    fn evaluate_908(&self, ctx: &EvaluationContext) -> ConditionResult {
1431        ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">=", 1.0))
1432    }
1433
1434    /// [910] Format: Möglicher Wert: &lt; 0 oder ≥ 0
1435    // REVIEW: Format condition accepting any numeric value (< 0 OR >= 0 is universally true). This documents that the field accepts negative values (unlike condition 902 which restricts to >= 0). Validates the value is a parseable number. Medium confidence on the exact segment — likely a MOA that can be a credit/deduction amount. (medium confidence)
1436    fn evaluate_910(&self, ctx: &EvaluationContext) -> ConditionResult {
1437        // Format: Möglicher Wert: < 0 oder ≥ 0 — any numeric value is valid (no sign constraint)
1438        // Applied to MOA amounts that may be negative (e.g., credits, deductions)
1439        let segs = ctx.find_segments("MOA");
1440        match segs
1441            .first()
1442            .and_then(|s| s.elements.first())
1443            .and_then(|e| e.get(1))
1444        {
1445            Some(val) if !val.is_empty() => {
1446                // < 0 or >= 0 covers all real numbers — validate it is a parseable number
1447                match val.trim_start_matches('-').parse::<f64>() {
1448                    Ok(_) => ConditionResult::True,
1449                    Err(_) => ConditionResult::False,
1450                }
1451            }
1452            _ => ConditionResult::Unknown,
1453        }
1454    }
1455
1456    /// [911] Format: Mögliche Werte: 1 bis n, je Nachricht oder Segmentgruppe bei 1 beginnend und fortlaufend aufsteigend
1457    // REVIEW: Sequential numbering validation (1 to n, ascending) — only the >= 1 integer constraint can be checked on a single value. Full sequential ordering cannot be verified without stateful iteration across all instances. Applies to LIN line item numbers in INVOIC SG26. Medium confidence due to segment assumption and inability to verify sequential ordering in isolation. (medium confidence)
1458    fn evaluate_911(&self, ctx: &EvaluationContext) -> ConditionResult {
1459        // Format: Mögliche Werte: 1 bis n, je Nachricht oder Segmentgruppe bei 1 beginnend und fortlaufend aufsteigend
1460        // Validates that the value is a positive integer >= 1 (sequential enforcement requires external state)
1461        let segs = ctx.find_segments("LIN");
1462        match segs
1463            .first()
1464            .and_then(|s| s.elements.first())
1465            .and_then(|e| e.first())
1466        {
1467            Some(val) => match val.parse::<i64>() {
1468                Ok(n) if n >= 1 => ConditionResult::True,
1469                Ok(_) => ConditionResult::False,
1470                Err(_) => ConditionResult::False,
1471            },
1472            None => ConditionResult::False, // segment absent → condition not applicable
1473        }
1474    }
1475
1476    /// [912] Format: max. 6 Nachkommastellen
1477    fn evaluate_912(&self, ctx: &EvaluationContext) -> ConditionResult {
1478        ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 6))
1479    }
1480
1481    /// [914] Format: Möglicher Wert: &gt; 0
1482    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
1483        ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">", 0.0))
1484    }
1485
1486    /// [927] Format: Möglicher Wert: -1
1487    // REVIEW: Value must be exactly -1. Used for special indicator values (e.g., a reversal or placeholder quantity). validate_numeric with == operator handles exact match. Medium confidence due to segment assumption — the -1 constraint is unusual and the exact segment context (QTY vs MOA) depends on which AHB data element references this condition. (medium confidence)
1488    fn evaluate_927(&self, ctx: &EvaluationContext) -> ConditionResult {
1489        ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, "==", -1.0))
1490    }
1491
1492    /// [930] Format: max. 2 Nachkommastellen
1493    fn evaluate_930(&self, ctx: &EvaluationContext) -> ConditionResult {
1494        ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 2))
1495    }
1496
1497    /// [931] Format: ZZZ = +00
1498    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
1499        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
1500    }
1501
1502    /// [932] Format: HHMM = 2200
1503    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
1504        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
1505    }
1506
1507    /// [933] Format: HHMM = 2300
1508    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
1509        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
1510    }
1511
1512    /// [934] Format: HHMM = 0400
1513    fn evaluate_934(&self, ctx: &EvaluationContext) -> ConditionResult {
1514        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0400"))
1515    }
1516
1517    /// [935] Format: HHMM = 0500
1518    fn evaluate_935(&self, ctx: &EvaluationContext) -> ConditionResult {
1519        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0500"))
1520    }
1521
1522    /// [937] Format: keine Nachkommastelle
1523    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
1524        ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 0))
1525    }
1526
1527    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
1528    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
1529        ctx.format_check("COM", 0, 0, validate_email)
1530    }
1531
1532    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
1533    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
1534        ctx.format_check("COM", 0, 0, validate_phone)
1535    }
1536
1537    /// [946] Format: max. 11 Nachkommastellen
1538    fn evaluate_946(&self, ctx: &EvaluationContext) -> ConditionResult {
1539        ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 11))
1540    }
1541
1542    /// [950] Format: Marktlokations-ID
1543    fn evaluate_950(&self, ctx: &EvaluationContext) -> ConditionResult {
1544        ctx.format_check_qualified("LOC", 0, "Z16", 1, 0, validate_malo_id)
1545    }
1546
1547    /// [951] Format: Zählpunktbezeichnung
1548    fn evaluate_951(&self, ctx: &EvaluationContext) -> ConditionResult {
1549        ctx.format_check_qualified("LOC", 0, "Z17", 1, 0, validate_zahlpunkt)
1550    }
1551
1552    /// [960] Format: Netzlokations-ID
1553    fn evaluate_960(&self, ctx: &EvaluationContext) -> ConditionResult {
1554        ctx.format_check_qualified("LOC", 0, "Z18", 1, 0, validate_malo_id)
1555    }
1556
1557    /// [961] Format: SR-ID
1558    fn evaluate_961(&self, ctx: &EvaluationContext) -> ConditionResult {
1559        ctx.format_check_qualified("LOC", 0, "Z19", 1, 0, validate_malo_id)
1560    }
1561}