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