Skip to main content

automapper_validation/generated/fv2504/
invoic_conditions_fv2504.rs

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