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        ctx.format_check("MOA", 0, 1, |val| validate_numeric(val, ">=", 0.0))
1408    }
1409
1410    /// [906] Format: max. 3 Nachkommastellen
1411    // 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)
1412    fn evaluate_906(&self, ctx: &EvaluationContext) -> ConditionResult {
1413        ctx.format_check("MOA", 0, 1, |val| validate_max_decimal_places(val, 3))
1414    }
1415
1416    /// [908] Format: Mögliche Werte: 1 bis n
1417    // 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)
1418    fn evaluate_908(&self, ctx: &EvaluationContext) -> ConditionResult {
1419        ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">=", 1.0))
1420    }
1421
1422    /// [910] Format: Möglicher Wert: &lt; 0 oder ≥ 0
1423    // 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)
1424    fn evaluate_910(&self, ctx: &EvaluationContext) -> ConditionResult {
1425        // Format: Möglicher Wert: < 0 oder ≥ 0 — any numeric value is valid (no sign constraint)
1426        // Applied to MOA amounts that may be negative (e.g., credits, deductions)
1427        let segs = ctx.find_segments("MOA");
1428        match segs
1429            .first()
1430            .and_then(|s| s.elements.first())
1431            .and_then(|e| e.get(1))
1432        {
1433            Some(val) if !val.is_empty() => {
1434                // < 0 or >= 0 covers all real numbers — validate it is a parseable number
1435                match val.trim_start_matches('-').parse::<f64>() {
1436                    Ok(_) => ConditionResult::True,
1437                    Err(_) => ConditionResult::False,
1438                }
1439            }
1440            _ => ConditionResult::Unknown,
1441        }
1442    }
1443
1444    /// [911] Format: Mögliche Werte: 1 bis n, je Nachricht oder Segmentgruppe bei 1 beginnend und fortlaufend aufsteigend
1445    // 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)
1446    fn evaluate_911(&self, ctx: &EvaluationContext) -> ConditionResult {
1447        // Format: Mögliche Werte: 1 bis n, je Nachricht oder Segmentgruppe bei 1 beginnend und fortlaufend aufsteigend
1448        // Validates that the value is a positive integer >= 1 (sequential enforcement requires external state)
1449        let segs = ctx.find_segments("LIN");
1450        match segs
1451            .first()
1452            .and_then(|s| s.elements.first())
1453            .and_then(|e| e.first())
1454        {
1455            Some(val) => match val.parse::<i64>() {
1456                Ok(n) if n >= 1 => ConditionResult::True,
1457                Ok(_) => ConditionResult::False,
1458                Err(_) => ConditionResult::False,
1459            },
1460            None => ConditionResult::False, // segment absent → condition not applicable
1461        }
1462    }
1463
1464    /// [912] Format: max. 6 Nachkommastellen
1465    fn evaluate_912(&self, ctx: &EvaluationContext) -> ConditionResult {
1466        ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 6))
1467    }
1468
1469    /// [914] Format: Möglicher Wert: &gt; 0
1470    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
1471        ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">", 0.0))
1472    }
1473
1474    /// [927] Format: Möglicher Wert: -1
1475    // 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)
1476    fn evaluate_927(&self, ctx: &EvaluationContext) -> ConditionResult {
1477        ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, "==", -1.0))
1478    }
1479
1480    /// [930] Format: max. 2 Nachkommastellen
1481    fn evaluate_930(&self, ctx: &EvaluationContext) -> ConditionResult {
1482        ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 2))
1483    }
1484
1485    /// [931] Format: ZZZ = +00
1486    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
1487        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
1488    }
1489
1490    /// [932] Format: HHMM = 2200
1491    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
1492        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
1493    }
1494
1495    /// [933] Format: HHMM = 2300
1496    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
1497        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
1498    }
1499
1500    /// [934] Format: HHMM = 0400
1501    fn evaluate_934(&self, ctx: &EvaluationContext) -> ConditionResult {
1502        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0400"))
1503    }
1504
1505    /// [935] Format: HHMM = 0500
1506    fn evaluate_935(&self, ctx: &EvaluationContext) -> ConditionResult {
1507        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0500"))
1508    }
1509
1510    /// [937] Format: keine Nachkommastelle
1511    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
1512        ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 0))
1513    }
1514
1515    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
1516    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
1517        ctx.format_check("COM", 0, 0, validate_email)
1518    }
1519
1520    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
1521    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
1522        ctx.format_check("COM", 0, 0, validate_phone)
1523    }
1524
1525    /// [946] Format: max. 11 Nachkommastellen
1526    fn evaluate_946(&self, ctx: &EvaluationContext) -> ConditionResult {
1527        ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 11))
1528    }
1529
1530    /// [950] Format: Marktlokations-ID
1531    fn evaluate_950(&self, ctx: &EvaluationContext) -> ConditionResult {
1532        ctx.format_check_qualified("LOC", 0, "Z16", 1, 0, validate_malo_id)
1533    }
1534
1535    /// [951] Format: Zählpunktbezeichnung
1536    fn evaluate_951(&self, ctx: &EvaluationContext) -> ConditionResult {
1537        ctx.format_check_qualified("LOC", 0, "Z17", 1, 0, validate_zahlpunkt)
1538    }
1539
1540    /// [960] Format: Netzlokations-ID
1541    fn evaluate_960(&self, ctx: &EvaluationContext) -> ConditionResult {
1542        ctx.format_check_qualified("LOC", 0, "Z18", 1, 0, validate_malo_id)
1543    }
1544
1545    /// [961] Format: SR-ID
1546    fn evaluate_961(&self, ctx: &EvaluationContext) -> ConditionResult {
1547        ctx.format_check_qualified("LOC", 0, "Z19", 1, 0, validate_malo_id)
1548    }
1549}