Skip to main content

automapper_validation/generated/fv2504/
orders_conditions_fv2504.rs

1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2504/ORDERS_AHB_1_0a_Fehlerkorrektur_20250623.xml
4// Generated: 2026-03-12T10:26:58Z
5// </auto-generated>
6
7// LLM-produced bodies aren't idiomatic Rust — silence stylistic lints at
8// file scope so `cargo clippy -D warnings` stays green. Correctness,
9// suspicious, and perf categories stay on so real bugs still surface.
10#![allow(clippy::style, clippy::complexity)]
11
12#[allow(unused_imports)]
13use crate::eval::format_validators::*;
14use crate::eval::{ConditionEvaluator, ConditionResult, EvaluationContext};
15
16/// Generated condition evaluator for ORDERS FV2504.
17pub struct OrdersConditionEvaluatorFV2504 {
18    // External condition IDs that require runtime context.
19    external_conditions: std::collections::HashSet<u32>,
20}
21
22impl Default for OrdersConditionEvaluatorFV2504 {
23    fn default() -> Self {
24        let mut external_conditions = std::collections::HashSet::new();
25        external_conditions.insert(6);
26        external_conditions.insert(7);
27        external_conditions.insert(9);
28        external_conditions.insert(15);
29        external_conditions.insert(16);
30        external_conditions.insert(17);
31        external_conditions.insert(23);
32        external_conditions.insert(26);
33        external_conditions.insert(27);
34        external_conditions.insert(28);
35        external_conditions.insert(29);
36        external_conditions.insert(36);
37        external_conditions.insert(45);
38        external_conditions.insert(60);
39        external_conditions.insert(61);
40        external_conditions.insert(62);
41        external_conditions.insert(63);
42        external_conditions.insert(76);
43        external_conditions.insert(78);
44        external_conditions.insert(82);
45        external_conditions.insert(86);
46        external_conditions.insert(91);
47        external_conditions.insert(95);
48        external_conditions.insert(96);
49        external_conditions.insert(100);
50        external_conditions.insert(101);
51        external_conditions.insert(104);
52        external_conditions.insert(105);
53        external_conditions.insert(125);
54        external_conditions.insert(126);
55        external_conditions.insert(127);
56        external_conditions.insert(128);
57        external_conditions.insert(129);
58        external_conditions.insert(131);
59        external_conditions.insert(132);
60        external_conditions.insert(133);
61        external_conditions.insert(140);
62        external_conditions.insert(141);
63        external_conditions.insert(142);
64        external_conditions.insert(143);
65        external_conditions.insert(152);
66        external_conditions.insert(153);
67        external_conditions.insert(154);
68        external_conditions.insert(155);
69        external_conditions.insert(156);
70        external_conditions.insert(157);
71        external_conditions.insert(164);
72        external_conditions.insert(165);
73        external_conditions.insert(166);
74        external_conditions.insert(172);
75        external_conditions.insert(178);
76        external_conditions.insert(179);
77        external_conditions.insert(492);
78        external_conditions.insert(493);
79        external_conditions.insert(2001);
80        external_conditions.insert(2065);
81        Self {
82            external_conditions,
83        }
84    }
85}
86
87impl ConditionEvaluator for OrdersConditionEvaluatorFV2504 {
88    fn message_type(&self) -> &str {
89        "ORDERS"
90    }
91
92    fn format_version(&self) -> &str {
93        "FV2504"
94    }
95
96    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
97        match condition {
98            1 => self.evaluate_1(ctx),
99            2 => self.evaluate_2(ctx),
100            6 => self.evaluate_6(ctx),
101            7 => self.evaluate_7(ctx),
102            9 => self.evaluate_9(ctx),
103            12 => self.evaluate_12(ctx),
104            13 => self.evaluate_13(ctx),
105            15 => self.evaluate_15(ctx),
106            16 => self.evaluate_16(ctx),
107            17 => self.evaluate_17(ctx),
108            18 => self.evaluate_18(ctx),
109            19 => self.evaluate_19(ctx),
110            21 => self.evaluate_21(ctx),
111            23 => self.evaluate_23(ctx),
112            24 => self.evaluate_24(ctx),
113            26 => self.evaluate_26(ctx),
114            27 => self.evaluate_27(ctx),
115            28 => self.evaluate_28(ctx),
116            29 => self.evaluate_29(ctx),
117            33 => self.evaluate_33(ctx),
118            34 => self.evaluate_34(ctx),
119            35 => self.evaluate_35(ctx),
120            36 => self.evaluate_36(ctx),
121            38 => self.evaluate_38(ctx),
122            45 => self.evaluate_45(ctx),
123            46 => self.evaluate_46(ctx),
124            47 => self.evaluate_47(ctx),
125            51 => self.evaluate_51(ctx),
126            54 => self.evaluate_54(ctx),
127            55 => self.evaluate_55(ctx),
128            56 => self.evaluate_56(ctx),
129            57 => self.evaluate_57(ctx),
130            60 => self.evaluate_60(ctx),
131            61 => self.evaluate_61(ctx),
132            62 => self.evaluate_62(ctx),
133            63 => self.evaluate_63(ctx),
134            64 => self.evaluate_64(ctx),
135            65 => self.evaluate_65(ctx),
136            67 => self.evaluate_67(ctx),
137            68 => self.evaluate_68(ctx),
138            69 => self.evaluate_69(ctx),
139            70 => self.evaluate_70(ctx),
140            76 => self.evaluate_76(ctx),
141            77 => self.evaluate_77(ctx),
142            78 => self.evaluate_78(ctx),
143            79 => self.evaluate_79(ctx),
144            80 => self.evaluate_80(ctx),
145            81 => self.evaluate_81(ctx),
146            82 => self.evaluate_82(ctx),
147            83 => self.evaluate_83(ctx),
148            84 => self.evaluate_84(ctx),
149            85 => self.evaluate_85(ctx),
150            86 => self.evaluate_86(ctx),
151            87 => self.evaluate_87(ctx),
152            91 => self.evaluate_91(ctx),
153            93 => self.evaluate_93(ctx),
154            94 => self.evaluate_94(ctx),
155            95 => self.evaluate_95(ctx),
156            96 => self.evaluate_96(ctx),
157            97 => self.evaluate_97(ctx),
158            99 => self.evaluate_99(ctx),
159            100 => self.evaluate_100(ctx),
160            101 => self.evaluate_101(ctx),
161            102 => self.evaluate_102(ctx),
162            103 => self.evaluate_103(ctx),
163            104 => self.evaluate_104(ctx),
164            105 => self.evaluate_105(ctx),
165            106 => self.evaluate_106(ctx),
166            107 => self.evaluate_107(ctx),
167            108 => self.evaluate_108(ctx),
168            109 => self.evaluate_109(ctx),
169            110 => self.evaluate_110(ctx),
170            112 => self.evaluate_112(ctx),
171            113 => self.evaluate_113(ctx),
172            114 => self.evaluate_114(ctx),
173            115 => self.evaluate_115(ctx),
174            117 => self.evaluate_117(ctx),
175            118 => self.evaluate_118(ctx),
176            119 => self.evaluate_119(ctx),
177            120 => self.evaluate_120(ctx),
178            121 => self.evaluate_121(ctx),
179            122 => self.evaluate_122(ctx),
180            123 => self.evaluate_123(ctx),
181            124 => self.evaluate_124(ctx),
182            125 => self.evaluate_125(ctx),
183            126 => self.evaluate_126(ctx),
184            127 => self.evaluate_127(ctx),
185            128 => self.evaluate_128(ctx),
186            129 => self.evaluate_129(ctx),
187            130 => self.evaluate_130(ctx),
188            131 => self.evaluate_131(ctx),
189            132 => self.evaluate_132(ctx),
190            133 => self.evaluate_133(ctx),
191            134 => self.evaluate_134(ctx),
192            135 => self.evaluate_135(ctx),
193            136 => self.evaluate_136(ctx),
194            137 => self.evaluate_137(ctx),
195            138 => self.evaluate_138(ctx),
196            139 => self.evaluate_139(ctx),
197            140 => self.evaluate_140(ctx),
198            141 => self.evaluate_141(ctx),
199            142 => self.evaluate_142(ctx),
200            143 => self.evaluate_143(ctx),
201            147 => self.evaluate_147(ctx),
202            148 => self.evaluate_148(ctx),
203            152 => self.evaluate_152(ctx),
204            153 => self.evaluate_153(ctx),
205            154 => self.evaluate_154(ctx),
206            155 => self.evaluate_155(ctx),
207            156 => self.evaluate_156(ctx),
208            157 => self.evaluate_157(ctx),
209            158 => self.evaluate_158(ctx),
210            159 => self.evaluate_159(ctx),
211            160 => self.evaluate_160(ctx),
212            161 => self.evaluate_161(ctx),
213            162 => self.evaluate_162(ctx),
214            163 => self.evaluate_163(ctx),
215            164 => self.evaluate_164(ctx),
216            165 => self.evaluate_165(ctx),
217            166 => self.evaluate_166(ctx),
218            167 => self.evaluate_167(ctx),
219            168 => self.evaluate_168(ctx),
220            169 => self.evaluate_169(ctx),
221            170 => self.evaluate_170(ctx),
222            171 => self.evaluate_171(ctx),
223            172 => self.evaluate_172(ctx),
224            173 => self.evaluate_173(ctx),
225            174 => self.evaluate_174(ctx),
226            175 => self.evaluate_175(ctx),
227            176 => self.evaluate_176(ctx),
228            177 => self.evaluate_177(ctx),
229            178 => self.evaluate_178(ctx),
230            179 => self.evaluate_179(ctx),
231            180 => self.evaluate_180(ctx),
232            490 => self.evaluate_490(ctx),
233            491 => self.evaluate_491(ctx),
234            492 => self.evaluate_492(ctx),
235            493 => self.evaluate_493(ctx),
236            494 => self.evaluate_494(ctx),
237            495 => self.evaluate_495(ctx),
238            500 => self.evaluate_500(ctx),
239            501 => self.evaluate_501(ctx),
240            503 => self.evaluate_503(ctx),
241            506 => self.evaluate_506(ctx),
242            507 => self.evaluate_507(ctx),
243            514 => self.evaluate_514(ctx),
244            515 => self.evaluate_515(ctx),
245            517 => self.evaluate_517(ctx),
246            518 => self.evaluate_518(ctx),
247            519 => self.evaluate_519(ctx),
248            521 => self.evaluate_521(ctx),
249            522 => self.evaluate_522(ctx),
250            523 => self.evaluate_523(ctx),
251            525 => self.evaluate_525(ctx),
252            527 => self.evaluate_527(ctx),
253            530 => self.evaluate_530(ctx),
254            531 => self.evaluate_531(ctx),
255            532 => self.evaluate_532(ctx),
256            533 => self.evaluate_533(ctx),
257            535 => self.evaluate_535(ctx),
258            536 => self.evaluate_536(ctx),
259            539 => self.evaluate_539(ctx),
260            540 => self.evaluate_540(ctx),
261            542 => self.evaluate_542(ctx),
262            543 => self.evaluate_543(ctx),
263            545 => self.evaluate_545(ctx),
264            547 => self.evaluate_547(ctx),
265            548 => self.evaluate_548(ctx),
266            549 => self.evaluate_549(ctx),
267            550 => self.evaluate_550(ctx),
268            551 => self.evaluate_551(ctx),
269            552 => self.evaluate_552(ctx),
270            553 => self.evaluate_553(ctx),
271            554 => self.evaluate_554(ctx),
272            555 => self.evaluate_555(ctx),
273            557 => self.evaluate_557(ctx),
274            558 => self.evaluate_558(ctx),
275            559 => self.evaluate_559(ctx),
276            560 => self.evaluate_560(ctx),
277            561 => self.evaluate_561(ctx),
278            562 => self.evaluate_562(ctx),
279            563 => self.evaluate_563(ctx),
280            564 => self.evaluate_564(ctx),
281            566 => self.evaluate_566(ctx),
282            567 => self.evaluate_567(ctx),
283            568 => self.evaluate_568(ctx),
284            569 => self.evaluate_569(ctx),
285            570 => self.evaluate_570(ctx),
286            571 => self.evaluate_571(ctx),
287            572 => self.evaluate_572(ctx),
288            573 => self.evaluate_573(ctx),
289            574 => self.evaluate_574(ctx),
290            575 => self.evaluate_575(ctx),
291            576 => self.evaluate_576(ctx),
292            903 => self.evaluate_903(ctx),
293            906 => self.evaluate_906(ctx),
294            911 => self.evaluate_911(ctx),
295            914 => self.evaluate_914(ctx),
296            922 => self.evaluate_922(ctx),
297            930 => self.evaluate_930(ctx),
298            931 => self.evaluate_931(ctx),
299            932 => self.evaluate_932(ctx),
300            933 => self.evaluate_933(ctx),
301            934 => self.evaluate_934(ctx),
302            935 => self.evaluate_935(ctx),
303            939 => self.evaluate_939(ctx),
304            940 => self.evaluate_940(ctx),
305            950 => self.evaluate_950(ctx),
306            951 => self.evaluate_951(ctx),
307            955 => self.evaluate_955(ctx),
308            960 => self.evaluate_960(ctx),
309            961 => self.evaluate_961(ctx),
310            962 => self.evaluate_962(ctx),
311            967 => self.evaluate_967(ctx),
312            2001 => self.evaluate_2001(ctx),
313            2002 => self.evaluate_2002(ctx),
314            2003 => self.evaluate_2003(ctx),
315            2004 => self.evaluate_2004(ctx),
316            2005 => self.evaluate_2005(ctx),
317            2006 => self.evaluate_2006(ctx),
318            2007 => self.evaluate_2007(ctx),
319            2044 => self.evaluate_2044(ctx),
320            2050 => self.evaluate_2050(ctx),
321            2060 => self.evaluate_2060(ctx),
322            2061 => self.evaluate_2061(ctx),
323            2062 => self.evaluate_2062(ctx),
324            2063 => self.evaluate_2063(ctx),
325            2064 => self.evaluate_2064(ctx),
326            2065 => self.evaluate_2065(ctx),
327            2066 => self.evaluate_2066(ctx),
328            2088 => self.evaluate_2088(ctx),
329            2089 => self.evaluate_2089(ctx),
330            2090 => self.evaluate_2090(ctx),
331            2092 => self.evaluate_2092(ctx),
332            2094 => self.evaluate_2094(ctx),
333            2095 => self.evaluate_2095(ctx),
334            2096 => self.evaluate_2096(ctx),
335            2097 => self.evaluate_2097(ctx),
336            2098 => self.evaluate_2098(ctx),
337            2099 => self.evaluate_2099(ctx),
338            _ => ConditionResult::Unknown,
339        }
340    }
341
342    fn is_external(&self, condition: u32) -> bool {
343        self.external_conditions.contains(&condition)
344    }
345    fn is_known(&self, condition: u32) -> bool {
346        matches!(
347            condition,
348            1 | 2
349                | 6
350                | 7
351                | 9
352                | 12
353                | 13
354                | 15
355                | 16
356                | 17
357                | 18
358                | 19
359                | 21
360                | 23
361                | 24
362                | 26
363                | 27
364                | 28
365                | 29
366                | 33
367                | 34
368                | 35
369                | 36
370                | 38
371                | 45
372                | 46
373                | 47
374                | 51
375                | 54
376                | 55
377                | 56
378                | 57
379                | 60
380                | 61
381                | 62
382                | 63
383                | 64
384                | 65
385                | 67
386                | 68
387                | 69
388                | 70
389                | 76
390                | 77
391                | 78
392                | 79
393                | 80
394                | 81
395                | 82
396                | 83
397                | 84
398                | 85
399                | 86
400                | 87
401                | 91
402                | 93
403                | 94
404                | 95
405                | 96
406                | 97
407                | 99
408                | 100
409                | 101
410                | 102
411                | 103
412                | 104
413                | 105
414                | 106
415                | 107
416                | 108
417                | 109
418                | 110
419                | 112
420                | 113
421                | 114
422                | 115
423                | 117
424                | 118
425                | 119
426                | 120
427                | 121
428                | 122
429                | 123
430                | 124
431                | 125
432                | 126
433                | 127
434                | 128
435                | 129
436                | 130
437                | 131
438                | 132
439                | 133
440                | 134
441                | 135
442                | 136
443                | 137
444                | 138
445                | 139
446                | 140
447                | 141
448                | 142
449                | 143
450                | 147
451                | 148
452                | 152
453                | 153
454                | 154
455                | 155
456                | 156
457                | 157
458                | 158
459                | 159
460                | 160
461                | 161
462                | 162
463                | 163
464                | 164
465                | 165
466                | 166
467                | 167
468                | 168
469                | 169
470                | 170
471                | 171
472                | 172
473                | 173
474                | 174
475                | 175
476                | 176
477                | 177
478                | 178
479                | 179
480                | 180
481                | 490
482                | 491
483                | 492
484                | 493
485                | 494
486                | 495
487                | 500
488                | 501
489                | 503
490                | 506
491                | 507
492                | 514
493                | 515
494                | 517
495                | 518
496                | 519
497                | 521
498                | 522
499                | 523
500                | 525
501                | 527
502                | 530
503                | 531
504                | 532
505                | 533
506                | 535
507                | 536
508                | 539
509                | 540
510                | 542
511                | 543
512                | 545
513                | 547
514                | 548
515                | 549
516                | 550
517                | 551
518                | 552
519                | 553
520                | 554
521                | 555
522                | 557
523                | 558
524                | 559
525                | 560
526                | 561
527                | 562
528                | 563
529                | 564
530                | 566
531                | 567
532                | 568
533                | 569
534                | 570
535                | 571
536                | 572
537                | 573
538                | 574
539                | 575
540                | 576
541                | 903
542                | 906
543                | 911
544                | 914
545                | 922
546                | 930
547                | 931
548                | 932
549                | 933
550                | 934
551                | 935
552                | 939
553                | 940
554                | 950
555                | 951
556                | 955
557                | 960
558                | 961
559                | 962
560                | 967
561                | 2001
562                | 2002
563                | 2003
564                | 2004
565                | 2005
566                | 2006
567                | 2007
568                | 2044
569                | 2050
570                | 2060
571                | 2061
572                | 2062
573                | 2063
574                | 2064
575                | 2065
576                | 2066
577                | 2088
578                | 2089
579                | 2090
580                | 2092
581                | 2094
582                | 2095
583                | 2096
584                | 2097
585                | 2098
586                | 2099
587        )
588    }
589}
590
591impl OrdersConditionEvaluatorFV2504 {
592    /// [16] Wenn eine untergeordnete SG vorhanden
593    /// EXTERNAL: Requires context from outside the message.
594    // REVIEW: Generic structural condition: 'if a subordinate SG is present'. Depends entirely on which child segment group is being checked in context — the condition number is reused across many segment rows with different child SGs. Cannot be resolved from the EDIFACT message content alone; requires the validation framework to supply structural position context. (medium confidence)
595    fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
596        ctx.external.evaluate("subordinate_sg_present")
597    }
598
599    /// [17] Wenn ein Segment innerhalb der SG vorhanden
600    /// EXTERNAL: Requires context from outside the message.
601    // REVIEW: Generic structural condition: 'if a segment within the SG is present'. Like [16], this condition number is reused across many rows and the specific segment being checked depends on the validation context. Cannot determine which segment to check without positional context supplied externally. (medium confidence)
602    fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
603        ctx.external.evaluate("segment_within_sg_present")
604    }
605
606    /// [78] Wenn in derselben SG29 mit Z55 in LIN DE1229 (Erforderliches Produkt der Netzlokation) das PIA+5 DE7140 mit einem Messprodukt aus Codeliste der Konfigurationen, Kapitel 2 „Codeliste der Standard-...
607    /// EXTERNAL: Requires context from outside the message.
608    // REVIEW: Requires checking PIA DE7140 against an external code list from 'Codeliste der Konfigurationen, Kapitel 2 – Codeliste der Standard-Messprodukt Strom für Werte nach Typ 1' filtered to those with the Werteigenschaft 'Blindarbeit'. The structural check (LIN Z55 + PIA+5 in same SG29) is possible via navigator, but the code list membership for 'Blindarbeit' products is an external reference that cannot be determined from the EDIFACT message alone. (medium confidence)
609    fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
610        ctx.external
611            .evaluate("pia_has_blindarbeit_product_netzlokation")
612    }
613
614    /// [82] Wenn eine andere SG29 mit Z54 in LIN DE1229 (Erforderliches Produkt der Marktlokation) mit PIA+5 DE7140 mit einem Messprodukt aus Codeliste der Konfigurationen, Kapitel 2.1.1 „Standard-Messproduk...
615    /// EXTERNAL: Requires context from outside the message.
616    // REVIEW: Requires: (1) finding ANOTHER SG29 (not the current one) where LIN DE1229 = Z54, (2) that SG29 also has PIA+5, and (3) PIA DE7140 is from 'Codeliste der Konfigurationen, Kapitel 2.1.1 – Standard-Messprodukt der Marktlokation mit der Wahlmöglichkeit der Zuordnung einer Zählzeit'. The code list membership is external and cannot be determined from the EDIFACT message alone. (medium confidence)
617    fn evaluate_82(&self, ctx: &EvaluationContext) -> ConditionResult {
618        ctx.external
619            .evaluate("other_sg29_z54_with_zaehlzeit_product")
620    }
621
622    /// [83] Wenn keine andere SG29 mit Z54 in LIN DE1229 (Erforderliches Produkt der Marktlokation) mit PIA+5 DE7140 mit einem Messprodukt aus Codeliste der Konfigurationen Kapitel 2.1.1 „Standard-Messproduk...
623    // REVIEW: Logical negation of condition [82]: 'wenn KEINE andere SG29 mit Z54 ... vorhanden ist'. Implemented by inverting the external result for [82]. Returns Unknown when the underlying external condition is Unknown (e.g., code list not available). (medium confidence)
624    fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
625        match ctx
626            .external
627            .evaluate("other_sg29_z54_with_zaehlzeit_product")
628        {
629            ConditionResult::True => ConditionResult::False,
630            ConditionResult::False => ConditionResult::True,
631            ConditionResult::Unknown => ConditionResult::Unknown,
632        }
633    }
634
635    /// [86] Wenn in derselben SG29 mit Z19 in LIN DE1229 (Erforderliches Produkt der Messlokation) das PIA+5 DE7140 mit einem Messprodukt aus Codeliste der Konfigurationen Kapitel 2.3.1 "Standard-Messprodukt d...
636    /// EXTERNAL: Requires context from outside the message.
637    fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
638        ctx.external
639            .evaluate("pia_z19_has_standard_messprodukt_with_zählzeit_wahlmöglichkeit")
640    }
641
642    /// [91] Wenn in diesem Datenelement kein anderes Paket zur Möglichkeit der Angabe von mindestens einem anderen Code führt.
643    /// EXTERNAL: Requires context from outside the message.
644    fn evaluate_91(&self, ctx: &EvaluationContext) -> ConditionResult {
645        ctx.external
646            .evaluate("no_other_package_enables_additional_code")
647    }
648
649    /// [126] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.2 „Konfigurationsprodukte Leistungskurvendefinition“ enthalten sind.
650    /// EXTERNAL: Requires context from outside the message.
651    fn evaluate_126(&self, ctx: &EvaluationContext) -> ConditionResult {
652        ctx.external
653            .evaluate("product_in_konfigurationsprodukte_leistungskurvendefinition")
654    }
655
656    /// [127] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.3 „Konfigurationsprodukte Ad-Hoc-Steuerkanal“ enthalten sind.
657    /// EXTERNAL: Requires context from outside the message.
658    fn evaluate_127(&self, ctx: &EvaluationContext) -> ConditionResult {
659        ctx.external
660            .evaluate("product_in_konfigurationsprodukte_ad_hoc_steuerkanal")
661    }
662
663    /// [128] Es sind nur die Messprodukte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.5 „Messprodukte für Werte nach Typ 2 aus Backend für LF und NB“ enthalten sind.
664    /// EXTERNAL: Requires context from outside the message.
665    fn evaluate_128(&self, ctx: &EvaluationContext) -> ConditionResult {
666        ctx.external
667            .evaluate("product_in_messprodukte_typ2_backend_lf_nb")
668    }
669
670    /// [129] Es sind nur die Messprodukte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.4 „Messprodukte mit Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW“ enthalten sind.
671    /// EXTERNAL: Requires context from outside the message.
672    fn evaluate_129(&self, ctx: &EvaluationContext) -> ConditionResult {
673        ctx.external
674            .evaluate("product_in_smgw_typ2_konfiguration_codeliste")
675    }
676
677    /// [141] Es sind nur die Messprodukt-Position-Codes erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.7 „Art der Werte für Messprodukte nach Typ 2“ enthalten sind.
678    /// EXTERNAL: Requires context from outside the message.
679    fn evaluate_141(&self, ctx: &EvaluationContext) -> ConditionResult {
680        ctx.external
681            .evaluate("messprodukt_position_code_in_typ2_art_codeliste")
682    }
683
684    /// [142] Wenn innerhalb derselben SG29 LIN im PIA+5 DE7140 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) ein Produkt angegeben ist, das in der Codeliste der Konfigurationen...
685    /// EXTERNAL: Requires context from outside the message.
686    fn evaluate_142(&self, ctx: &EvaluationContext) -> ConditionResult {
687        ctx.external
688            .evaluate("smgw_product_triggers_on_threshold_crossing")
689    }
690
691    /// [143] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Steuerbaren Ressource angegeben ist
692    /// EXTERNAL: Requires context from outside the message.
693    fn evaluate_143(&self, ctx: &EvaluationContext) -> ConditionResult {
694        ctx.external.evaluate("meldepunkt_is_steuerbare_ressource")
695    }
696
697    /// [152] Wenn in SG29 LIN (Erforderliches Produkt der Messlokation) das PIA+5 DE7140 mit einem Produkt-Code vorhanden ist der in der Codeliste der Konfigurationen im Kapitel 7 "Produkte zur Bestellung einer...
698    /// EXTERNAL: Requires context from outside the message.
699    fn evaluate_152(&self, ctx: &EvaluationContext) -> ConditionResult {
700        ctx.external
701            .evaluate("messlokation_product_is_weitere_energieflussrichtung")
702    }
703
704    /// [153] Es sind nur die Produkt-Codes erlaubt, die in der Codeliste der Konfigurationen im Kapitel 7 "Produkte zur Bestellung einer Änderung an einer Lokation" die in der Spalte "Ebene" mit dem Wert "Mess...
705    /// EXTERNAL: Requires context from outside the message.
706    fn evaluate_153(&self, ctx: &EvaluationContext) -> ConditionResult {
707        ctx.external.evaluate("product_code_level_messlokation")
708    }
709
710    /// [154] Es sind nur die Produkt-Codes erlaubt, die in der Codeliste der Konfigurationen im Kapitel 7 "Produkte zur Bestellung einer Änderung an einer Lokation" die in der Spalte "Ebene" mit dem Wert "Netz...
711    /// EXTERNAL: Requires context from outside the message.
712    fn evaluate_154(&self, ctx: &EvaluationContext) -> ConditionResult {
713        ctx.external.evaluate("product_code_level_netzlokation")
714    }
715
716    /// [155] Es sind nur die Produkt-Codes erlaubt, die in der Codeliste der Konfigurationen im Kapitel 7 "Produkte zur Bestellung einer Änderung an einer Lokation" die in der Spalte "Ebene" mit dem Wert "Steu...
717    /// EXTERNAL: Requires context from outside the message.
718    fn evaluate_155(&self, ctx: &EvaluationContext) -> ConditionResult {
719        ctx.external
720            .evaluate("product_code_level_steuerbare_ressource")
721    }
722
723    /// [156] Es sind weiterhin nur die Produkt-Codes erlaubt, die in der Codeliste der Konfigurationen im Kapitel 7 "Produkte zur Bestellung einer Änderung an einer Lokation" die in der Spalte "Produkt gegenü...
724    /// EXTERNAL: Requires context from outside the message.
725    fn evaluate_156(&self, ctx: &EvaluationContext) -> ConditionResult {
726        ctx.external.evaluate("product_code_orderable_by_nb")
727    }
728
729    /// [157] Es sind weiterhin nur die Produkt-Codes erlaubt, die in der Codeliste der Konfigurationen im Kapitel 7 "Produkte zur Bestellung einer Änderung an einer Lokation" die in der Spalte "Produkt gegenü...
730    /// EXTERNAL: Requires context from outside the message.
731    fn evaluate_157(&self, ctx: &EvaluationContext) -> ConditionResult {
732        ctx.external.evaluate("product_code_orderable_by_lf")
733    }
734
735    /// [158] Es sind nur die Produkt-Codes erlaubt, die in der Codeliste der Konfigurationen im Kapitel 6.2 "Produkte zur Bestellung einer Änderung von Abrechnungsdaten" genannt sind.
736    fn evaluate_158(&self, ctx: &EvaluationContext) -> ConditionResult {
737        // HAND-EDITED: Codeliste der Konfigurationen 1.3c, Kapitel 6.2 (embedded).
738        crate::eval::abrechnungsdaten::product_is_listed(
739            ctx,
740            crate::eval::abrechnungsdaten::Edition::V1_3c,
741        )
742    }
743
744    /// [159] Wenn in derselben SG29 LIN im PIA+5 (Erforderliches Produkt Abrechnungsdaten) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6.2 "Produkte zur Bestellung e...
745    fn evaluate_159(&self, ctx: &EvaluationContext) -> ConditionResult {
746        // HAND-EDITED: Codeliste der Konfigurationen 1.3c, Kapitel 6.2 (embedded).
747        crate::eval::abrechnungsdaten::has_property_codes(
748            ctx,
749            crate::eval::abrechnungsdaten::Edition::V1_3c,
750        )
751    }
752
753    /// [160] Es sind nur die Codes der Produkteigenschaft zu dem in derselben SG29 LIN im PIA+5 (Erforderliches Produkt Abrechnungsdaten) DE7140 erlaubt, die in der Codeliste der Konfigurationen im Kapitel 6.2 ...
754    fn evaluate_160(&self, ctx: &EvaluationContext) -> ConditionResult {
755        // HAND-EDITED: Codeliste der Konfigurationen 1.3c, Kapitel 6.2 (embedded).
756        crate::eval::abrechnungsdaten::property_code_allowed(
757            ctx,
758            crate::eval::abrechnungsdaten::Edition::V1_3c,
759        )
760    }
761
762    /// [162] Es ist nur der Wertebereich erlaubt, der zu dem in derselben SG29 LIN im PIA+5 (Erforderliches Produkt Abrechnungsdaten) DE7140 genannten Produkt, das in der Codeliste der Konfigurationen im Kapite...
763    fn evaluate_162(&self, ctx: &EvaluationContext) -> ConditionResult {
764        // HAND-EDITED: Codeliste der Konfigurationen 1.3c, Kapitel 6.2 (embedded).
765        crate::eval::abrechnungsdaten::wertedetails_value_fits(
766            ctx,
767            crate::eval::abrechnungsdaten::Edition::V1_3c,
768        )
769    }
770
771    /// [164] Wenn die Konfiguration der in SG34 RFF+Z20 DE1154 (Referenz auf ID der Tranche) genannte Tranche innerhalb derselben SG29 LIN++Z16 (Erforderliches Produkt der Tranche) unverändert bestehen bleibt.
772    /// EXTERNAL: Requires context from outside the message.
773    fn evaluate_164(&self, ctx: &EvaluationContext) -> ConditionResult {
774        ctx.external.evaluate("tranche_configuration_unchanged")
775    }
776
777    /// [165] Wenn die Konfiguration der in SG34 RFF+Z20 DE1154 (Referenz auf ID der Tranche) genannte Tranche innerhalb derselben SG29 LIN++Z16 (Erforderliches Produkt der Tranche) aufgrund einer Zuordnung eine...
778    /// EXTERNAL: Requires context from outside the message.
779    fn evaluate_165(&self, ctx: &EvaluationContext) -> ConditionResult {
780        ctx.external
781            .evaluate("tranche_reconfiguration_by_lf_assignment")
782    }
783
784    /// [166] Wenn es sich um die Einrichtung der Konfigurationen aufgrund einer Zuordnung eines LF zu einer Tranche handelt
785    /// EXTERNAL: Requires context from outside the message.
786    fn evaluate_166(&self, ctx: &EvaluationContext) -> ConditionResult {
787        ctx.external
788            .evaluate("configuration_setup_by_lf_tranche_assignment")
789    }
790
791    /// [172] Wenn es sich bei der in SG2 LOC+172 DE3225 genannten Lokation um die Integration in eine Kundenanlage oder Herauslösung aus einer Kundenanlage nach §20 Abs.1d EnWG handelt. Details siehe UTILMD A...
792    /// EXTERNAL: Requires context from outside the message.
793    fn evaluate_172(&self, ctx: &EvaluationContext) -> ConditionResult {
794        ctx.external
795            .evaluate("location_is_kundenanlage_integration_or_removal")
796    }
797
798    /// [178] Wenn vom NB eine Abtretungserklärung vom Kunden gewünscht ist.
799    /// EXTERNAL: Requires context from outside the message.
800    fn evaluate_178(&self, ctx: &EvaluationContext) -> ConditionResult {
801        ctx.external.evaluate("nb_requires_assignment_declaration")
802    }
803
804    /// [179] Wenn in derselben SG29 LIN im PIA+5 (Erforderliches Produkt Abrechnungsdaten) DE7140 das Produkt "Empfänger der Vergütung zur Einspeisung" aus der Codeliste der Konfigurationen im Kapitel 6.2 "Pr...
805    /// EXTERNAL: Requires context from outside the message — codelist chapter 6.2 "Produkte zur Bestellung …" defines the product codes.
806    fn evaluate_179(&self, ctx: &EvaluationContext) -> ConditionResult {
807        ctx.external
808            .evaluate("empfaenger_verguetung_einspeisung_and_lieferant")
809    }
810
811    /// [180] Wenn in derselben SG29 LIN im PIA+5 (Erforderliches Produkt Abrechnungsdaten) DE7140 ein Produkt-Code genannt ist der in der Codeliste der Konfigurationen im Kapitel 6.2 "Produkte zur Bestellung ei...
812    fn evaluate_180(&self, ctx: &EvaluationContext) -> ConditionResult {
813        // HAND-EDITED: Codeliste der Konfigurationen 1.3c, Kapitel 6.2 (embedded).
814        crate::eval::abrechnungsdaten::has_wertedetails(
815            ctx,
816            crate::eval::abrechnungsdaten::Edition::V1_3c,
817        )
818    }
819
820    /// [490] wenn Wert in diesem DE, an der Stelle CCYYMMDDHHMM ein Zeitpunkt aus dem angegeben Zeitraum der Tabelle Kapitel 3.5 „Übersicht gesetzliche deutsche Sommerzeit (MESZ)“ der Spalten: „Sommerzei...
821    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
822        match ctx.resolved_value {
823            Some(val) => is_mesz_utc(val),
824            None => ConditionResult::False,
825        }
826    }
827
828    /// [491] wenn Wert in diesem DE, an der Stelle CCYYMMDDHHMM ein Zeitpunkt aus dem angegeben Zeitraum der Tabelle Kapitel 3.6 „Übersicht gesetzliche deutsche Zeit (MEZ)“ der Spalten: „Winterzeit (MEZ)...
829    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
830        match ctx.resolved_value {
831            Some(val) => is_mez_utc(val),
832            None => ConditionResult::False,
833        }
834    }
835
836    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
837    fn evaluate_967(&self, ctx: &EvaluationContext) -> ConditionResult {
838        // Structural check on the certificate body (base64 charset + plausible
839        // length). Cryptographic validation (signature, CA, BSI profile) is
840        // out of scope for static AHB validation.
841        match ctx.resolved_value {
842            Some(val) => validate_x509_cert_body(val),
843            None => ConditionResult::Unknown,
844        }
845    }
846
847    /// [2001] Die SG29 ist so oft zu wiederholen, wie ab dem DTM+203 (Ausführungsdatum) Tranchen zu der in der SG2 genannten Marktlokation vorhanden sind.
848    /// EXTERNAL: Requires context from outside the message.
849    fn evaluate_2001(&self, ctx: &EvaluationContext) -> ConditionResult {
850        ctx.external.evaluate("sg29_tranche_count_matches")
851    }
852
853    /// [2003] Die SG29 ist so oft zu wiederholen, wie ab dem DTM+203 (Ausführungsdatum) Messlokationen zu der in der SG2 genannten Marktlokation vorhanden sind und für jede dieser Messlokationen müssen alle M...
854    fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
855        // Hinweis: SG29 repetition cardinality rule — informational annotation documenting that
856        // SG29 must repeat once per Messlokation associated with the Marktlokation from DTM+203,
857        // and all Messprodukte for each Messlokation (including Tranchen) must be listed.
858        // This is a structural cardinality guidance note, always applies.
859        ConditionResult::True
860    }
861
862    /// [2065] Diese SG30 ist so oft zu wiederholen, wie zu den unterschiedlichen Messprodukt-Position-Codes zu dem innerhalb derselben SG29 LIN im PIA+5 DE7140 (Erforderliches Produkt Konfigurationserlaubnis fü...
863    /// EXTERNAL: Requires context from outside the message.
864    fn evaluate_2065(&self, ctx: &EvaluationContext) -> ConditionResult {
865        ctx.external
866            .evaluate("sg30_configuration_threshold_product_cardinality")
867    }
868
869    /// [1] Wenn IMD+Z03 vorhanden
870    fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
871        ctx.has_qualifier("IMD", 0, "Z03")
872    }
873
874    /// [2] Wenn BGM+7 vorhanden
875    fn evaluate_2(&self, ctx: &EvaluationContext) -> ConditionResult {
876        ctx.has_qualifier("BGM", 0, "7")
877    }
878
879    /// [6] Wenn MP-ID in SG2 NAD+MS mit Rolle LF vorhanden
880    /// EXTERNAL: Requires context from outside the message.
881    fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
882        ctx.external.evaluate("sender_is_lf")
883    }
884
885    /// [7] Wenn MP-ID in SG2 NAD+MS mit Rolle NB vorhanden
886    /// EXTERNAL: Requires context from outside the message.
887    fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
888        ctx.external.evaluate("sender_is_nb")
889    }
890
891    /// [9] Wenn bekannt
892    /// EXTERNAL: Requires context from outside the message.
893    // REVIEW: 'Wenn bekannt' (when known) depends on whether a value is known in the business context — cannot be derived from EDIFACT segments alone. (medium confidence)
894    fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
895        ctx.external.evaluate("information_is_known")
896    }
897
898    /// [12] Wenn vorhanden
899    // REVIEW: Condition 12 'Wenn vorhanden' is a generic self-referential meta-condition indicating that the attached validation rule applies whenever the field is populated. The validator framework handles actual presence detection; this evaluator simply returns True to indicate the rule is always applicable when invoked. (medium confidence)
900    fn evaluate_12(&self, _ctx: &EvaluationContext) -> ConditionResult {
901        // Wenn vorhanden — generic 'when present' meta-condition
902        // In AHB context this means: validation applies whenever the field is populated.
903        // Returning True here defers the presence check to the validator framework itself.
904        ConditionResult::True
905    }
906
907    /// [13] Wenn SG2 LOC+172 nicht vorhanden
908    fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
909        ctx.lacks_qualifier("LOC", 0, "172")
910    }
911
912    /// [15] Wenn MP-ID in SG2 NAD+MS mit Rolle MSB vorhanden
913    /// EXTERNAL: Requires context from outside the message.
914    fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
915        ctx.external.evaluate("sender_is_msb")
916    }
917
918    /// [18] Wenn IMD++Z11 vorhanden
919    fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
920        let imds = ctx.find_segments("IMD");
921        ConditionResult::from(imds.iter().any(|s| {
922            s.elements
923                .get(1)
924                .and_then(|e| e.first())
925                .is_some_and(|v| v == "Z11")
926        }))
927    }
928
929    /// [19] Wenn IMD++Z12 vorhanden
930    fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
931        let imds = ctx.find_segments("IMD");
932        ConditionResult::from(imds.iter().any(|s| {
933            s.elements
934                .get(1)
935                .and_then(|e| e.first())
936                .is_some_and(|v| v == "Z12")
937        }))
938    }
939
940    /// [21] Wenn BGM+Z28 vorhanden
941    fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
942        ctx.has_qualifier("BGM", 0, "Z28")
943    }
944
945    /// [23] Wenn MP-ID in SG2 NAD+MR mit Rolle NB vorhanden
946    /// EXTERNAL: Requires context from outside the message.
947    fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
948        ctx.external.evaluate("recipient_is_nb")
949    }
950
951    /// [24] Wenn IMD++Z35 vorhanden
952    fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
953        let imds = ctx.find_segments("IMD");
954        ConditionResult::from(imds.iter().any(|s| {
955            s.elements
956                .get(1)
957                .and_then(|e| e.first())
958                .is_some_and(|v| v == "Z35")
959        }))
960    }
961
962    /// [26] Wenn MP-ID in SG2 NAD+MS mit Rolle ÜNB vorhanden
963    /// EXTERNAL: Requires context from outside the message.
964    // REVIEW: Whether the NAD+MS market participant holds the role ÜNB (Übertragungsnetzbetreiber) cannot be determined from the EDIFACT message content alone. The role is a business context attribute of the MP-ID that requires an external master data lookup (e.g. Marktstammdatenregister or internal role registry). Marked external with name 'sender_is_uenb'. (medium confidence)
965    fn evaluate_26(&self, ctx: &EvaluationContext) -> ConditionResult {
966        ctx.external.evaluate("sender_is_uenb")
967    }
968
969    /// [27] Wenn MP-ID in SG2 NAD+MR mit Rolle MSB vorhanden
970    /// EXTERNAL: Requires context from outside the message.
971    // REVIEW: Whether the NAD+MR market participant holds the role MSB (Messstellenbetreiber) cannot be determined from the EDIFACT message content alone. The role is a business context attribute of the MP-ID requiring external master data. Marked external with name 'recipient_is_msb'. (medium confidence)
972    fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
973        ctx.external.evaluate("recipient_is_msb")
974    }
975
976    /// [28] Wenn MP-ID in SG2 NAD+MR aus Sparte Strom
977    /// EXTERNAL: Requires context from outside the message.
978    fn evaluate_28(&self, ctx: &EvaluationContext) -> ConditionResult {
979        ctx.external.evaluate("recipient_market_sector_is_strom")
980    }
981
982    /// [29] Wenn MP-ID in SG2 NAD+MR aus Sparte Gas
983    /// EXTERNAL: Requires context from outside the message.
984    fn evaluate_29(&self, ctx: &EvaluationContext) -> ConditionResult {
985        ctx.external.evaluate("recipient_market_sector_is_gas")
986    }
987
988    /// [33] Wenn IMD+Z01 vorhanden
989    fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
990        ctx.has_qualifier("IMD", 1, "Z01")
991    }
992
993    /// [34] Wenn IMD+Z02 vorhanden
994    fn evaluate_34(&self, ctx: &EvaluationContext) -> ConditionResult {
995        ctx.has_qualifier("IMD", 1, "Z02")
996    }
997
998    /// [35] IMD++Z14+Z06 vorhanden
999    fn evaluate_35(&self, ctx: &EvaluationContext) -> ConditionResult {
1000        let imds = ctx.find_segments("IMD");
1001        let found = imds.iter().any(|s| {
1002            let has_z14 = s
1003                .elements
1004                .get(1)
1005                .and_then(|e| e.first())
1006                .is_some_and(|v| v == "Z14");
1007            let has_z06 = s
1008                .elements
1009                .get(2)
1010                .and_then(|e| e.first())
1011                .is_some_and(|v| v == "Z06");
1012            has_z14 && has_z06
1013        });
1014        ConditionResult::from(found)
1015    }
1016
1017    /// [36] Wenn MP-ID in SG2 NAD+MR mit Rolle NB nicht vorhanden
1018    /// EXTERNAL: Requires context from outside the message.
1019    fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
1020        ctx.external.evaluate("recipient_role_is_not_nb")
1021    }
1022
1023    /// [38] Wenn FTX+Z04/Z05 vorhanden
1024    fn evaluate_38(&self, ctx: &EvaluationContext) -> ConditionResult {
1025        let found = ctx.find_segments("FTX").iter().any(|s| {
1026            s.elements
1027                .first()
1028                .and_then(|e| e.first())
1029                .is_some_and(|v| v == "Z04" || v == "Z05")
1030        });
1031        ConditionResult::from(found)
1032    }
1033
1034    /// [45] Wenn MP-ID in SG2 NAD+MS mit Rolle MSB nicht vorhanden
1035    /// EXTERNAL: Requires context from outside the message.
1036    fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
1037        ctx.external.evaluate("sender_role_is_not_msb")
1038    }
1039
1040    /// [46] Wenn SG29 IMD++Z46 vorhanden
1041    fn evaluate_46(&self, ctx: &EvaluationContext) -> ConditionResult {
1042        ctx.has_qualifier("IMD", 1, "Z46")
1043    }
1044
1045    /// [47] Wenn SG29 IMD++Z46  nicht vorhanden
1046    fn evaluate_47(&self, ctx: &EvaluationContext) -> ConditionResult {
1047        ctx.lacks_qualifier("IMD", 1, "Z46")
1048    }
1049
1050    /// [51] Wenn BGM+Z48 vorhanden
1051    fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
1052        ctx.has_qualifier("BGM", 0, "Z48")
1053    }
1054
1055    /// [54] Wenn FTX+Z06 vorhanden
1056    fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
1057        ctx.has_qualifier("FTX", 0, "Z06")
1058    }
1059
1060    /// [55] Wenn DTM+469 (Beginn zum nächstmöglichen Termin) nicht vorhanden
1061    fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
1062        ctx.lacks_qualifier("DTM", 0, "469")
1063    }
1064
1065    /// [56] Wenn DTM+203 (Ausführungsdatum) nicht vorhanden
1066    fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
1067        ctx.lacks_qualifier("DTM", 0, "203")
1068    }
1069
1070    /// [57] Wenn im selben SG2 NAD DE3124 nicht vorhanden
1071    // REVIEW: Checks if any NAD segment in the message lacks DE3124 (C058 at elements[2][0]). 'Im selben SG2' requires group context; falls back to message-wide check. DE3124 is the first component of C058 (Zeile für Name und Anschrift / Zusatzinfo). (medium confidence)
1072    fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
1073        let nads = ctx.find_segments("NAD");
1074        if nads.is_empty() {
1075            return ConditionResult::Unknown;
1076        }
1077        let any_without = nads.iter().any(|nad| {
1078            nad.elements
1079                .get(2)
1080                .and_then(|e| e.first())
1081                .map(|v| v.is_empty())
1082                .unwrap_or(true)
1083        });
1084        ConditionResult::from(any_without)
1085    }
1086
1087    /// [60] MP-ID nur aus Sparte Gas
1088    /// EXTERNAL: Requires context from outside the message.
1089    fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
1090        ctx.external.evaluate("market_location_is_gas")
1091    }
1092
1093    /// [61] MP-ID nur aus Sparte Strom
1094    /// EXTERNAL: Requires context from outside the message.
1095    fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
1096        ctx.external.evaluate("market_location_is_electricity")
1097    }
1098
1099    /// [62] MP-ID mit Rolle MSB
1100    /// EXTERNAL: Requires context from outside the message.
1101    fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
1102        ctx.external.evaluate("sender_is_msb")
1103    }
1104
1105    /// [63] MP-ID mit Rolle NB
1106    /// EXTERNAL: Requires context from outside the message.
1107    fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
1108        ctx.external.evaluate("sender_is_nb")
1109    }
1110
1111    /// [64] Wenn SG2 NAD+Z03 nicht vorhanden
1112    fn evaluate_64(&self, ctx: &EvaluationContext) -> ConditionResult {
1113        ctx.lacks_qualifier("NAD", 0, "Z03")
1114    }
1115
1116    /// [65] Wenn FTX+Z08 vorhanden
1117    fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
1118        ctx.has_qualifier("FTX", 0, "Z08")
1119    }
1120
1121    /// [67] Wenn DTM+163 nicht vorhanden
1122    fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
1123        ctx.lacks_qualifier("DTM", 0, "163")
1124    }
1125
1126    /// [68] Wenn DTM+7 nicht vorhanden
1127    fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
1128        ctx.lacks_qualifier("DTM", 0, "7")
1129    }
1130
1131    /// [69] Wenn NAD+Z23 nicht vorhanden
1132    fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
1133        ctx.lacks_qualifier("NAD", 0, "Z23")
1134    }
1135
1136    /// [70] Wenn NAD+Z03 nicht vorhanden
1137    fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
1138        ctx.lacks_qualifier("NAD", 0, "Z03")
1139    }
1140
1141    /// [76] Wenn in derselben SG29 mit Z54 in LIN DE1229 (Erforderliches Produkt der Marktlokation) das PIA+5 DE7140 mit einem Messprodukt aus Codeliste der Konfigurationen Kapitel 2.1.1 "Standard-Messprodukt ...
1142    /// EXTERNAL: Requires context from outside the message.
1143    fn evaluate_76(&self, ctx: &EvaluationContext) -> ConditionResult {
1144        ctx.external
1145            .evaluate("pia_product_in_standard_messprodukt_wahlmoeglichkeit_list")
1146    }
1147
1148    /// [77] Wenn im selben CCI im DE7059 der Code Z39 (Code der Zählzeitdefinition) vorhanden ist
1149    // REVIEW: Checks if a CCI segment in the same SG29 group has DE7059 == 'Z39' (Code der Zählzeitdefinition). DE7059 is the class/type code at elements[0][0] in CCI. Group-scoped to SG29 with message-wide fallback. (medium confidence)
1150    fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
1151        ctx.any_group_has_qualifier("CCI", 0, "Z39", &["SG29"])
1152    }
1153
1154    /// [79] Wenn eine andere SG29 mit Z54 in LIN DE1229 (Erforderliches Produkt der Marktlokation) mit PIA+5+9991000000078:Z11 (für Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
1155    // REVIEW: Checks if any SG29 instance contains both LIN with Z54 at elements[1][0] and PIA with product code 9991000000078 at elements[1][0]. The 'another SG29' nuance is approximated as 'any SG29' since we have no current-group context. (medium confidence)
1156    fn evaluate_79(&self, ctx: &EvaluationContext) -> ConditionResult {
1157        ctx.any_group_has_co_occurrence(
1158            "LIN",
1159            1,
1160            &["Z54"],
1161            "PIA",
1162            1,
1163            0,
1164            &["9991000000078"],
1165            &["SG29"],
1166        )
1167    }
1168
1169    /// [80] Wenn keine andere SG29 mit Z54 in LIN DE1229 (Erforderliches Produkt der Marktlokation) mit PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
1170    // REVIEW: Negation of condition 79 — no SG29 with LIN+Z54 AND PIA product 9991000000078. (medium confidence)
1171    fn evaluate_80(&self, ctx: &EvaluationContext) -> ConditionResult {
1172        match ctx.any_group_has_co_occurrence(
1173            "LIN",
1174            1,
1175            &["Z54"],
1176            "PIA",
1177            1,
1178            0,
1179            &["9991000000078"],
1180            &["SG29"],
1181        ) {
1182            ConditionResult::True => ConditionResult::False,
1183            ConditionResult::False => ConditionResult::True,
1184            ConditionResult::Unknown => ConditionResult::Unknown,
1185        }
1186    }
1187
1188    /// [81] Wenn in derselben SG29 mit Z54 in LIN DE1229 (Erforderliches Produkt der Marktlokation), das PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
1189    // REVIEW: Group-scoped co-occurrence in the same SG29: LIN with Z54 at elements[1][0] AND PIA with product code 9991000000078 at elements[1][0]. Functionally equivalent to condition 79 since the API checks within the same group instance. (medium confidence)
1190    fn evaluate_81(&self, ctx: &EvaluationContext) -> ConditionResult {
1191        // HAND-EDITED: "in derselben SG29 mit Z54 in LIN … PIA+5+9991000000078:Z11" — the enclosing SG29 only, when the scope
1192        // reaches one; the message as before otherwise.
1193        if let Some(scope) = ctx.enclosing_scope("SG29") {
1194            return ConditionResult::from(
1195                ctx.find_segments_at(scope, "LIN")
1196                    .iter()
1197                    .any(|s| s.get_element(1) == "Z54")
1198                    && ctx.find_segments_at(scope, "PIA").iter().any(|s| {
1199                        s.get_element(0) == "5"
1200                            && s.get_component(1, 0) == "9991000000078"
1201                            && s.get_component(1, 1) == "Z11"
1202                    }),
1203            );
1204        }
1205        ctx.any_group_has_co_occurrence(
1206            "LIN",
1207            1,
1208            &["Z54"],
1209            "PIA",
1210            1,
1211            0,
1212            &["9991000000078"],
1213            &["SG29"],
1214        )
1215    }
1216
1217    /// [84] IMD++Z14+Z07 vorhanden
1218    // REVIEW: Checks for IMD segment where elements[1][0]='Z14' (item characteristic) and elements[2][0]='Z07' (item description code). IMD++Z14+Z07 maps directly to these positions. (medium confidence)
1219    fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
1220        {
1221            let segments = ctx.find_segments("IMD");
1222            ConditionResult::from(segments.iter().any(|s| {
1223                s.elements
1224                    .get(1)
1225                    .and_then(|e| e.first())
1226                    .is_some_and(|v| v == "Z14")
1227                    && s.elements
1228                        .get(2)
1229                        .and_then(|e| e.first())
1230                        .is_some_and(|v| v == "Z07")
1231            }))
1232        }
1233    }
1234
1235    /// [85] Wenn in derselben SG29 mit Z54 in LIN DE1229 (Erforderliches Produkt der Marktlokation), das PIA+5+9991000000086:Z11 (Wirkarbeit höchste 1/4 Stunde im Monat) vorhanden ist
1236    // REVIEW: Group-scoped co-occurrence in SG29: LIN with Z54 at elements[1][0] AND PIA with product code 9991000000086 (Wirkarbeit höchste 1/4 Stunde im Monat) at elements[1][0]. (medium confidence)
1237    fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
1238        // HAND-EDITED: "in derselben SG29 mit Z54 in LIN … PIA+5+9991000000086:Z11" — the enclosing SG29 only, when the scope
1239        // reaches one; the message as before otherwise.
1240        if let Some(scope) = ctx.enclosing_scope("SG29") {
1241            return ConditionResult::from(
1242                ctx.find_segments_at(scope, "LIN")
1243                    .iter()
1244                    .any(|s| s.get_element(1) == "Z54")
1245                    && ctx.find_segments_at(scope, "PIA").iter().any(|s| {
1246                        s.get_element(0) == "5"
1247                            && s.get_component(1, 0) == "9991000000086"
1248                            && s.get_component(1, 1) == "Z11"
1249                    }),
1250            );
1251        }
1252        ctx.any_group_has_co_occurrence(
1253            "LIN",
1254            1,
1255            &["Z54"],
1256            "PIA",
1257            1,
1258            0,
1259            &["9991000000086"],
1260            &["SG29"],
1261        )
1262    }
1263
1264    /// [87] Wenn FTX+Z09/Z10 vorhanden
1265    fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
1266        {
1267            let segs = ctx.find_segments("FTX");
1268            ConditionResult::from(segs.iter().any(|s| {
1269                s.elements
1270                    .first()
1271                    .and_then(|e| e.first())
1272                    .is_some_and(|v| v == "Z09" || v == "Z10")
1273            }))
1274        }
1275    }
1276
1277    /// [93] Wenn in derselben Nachricht eine SG29 mit Z16 in LIN DE1229 (Erforderliches Produkt der Tranche) nicht vorhanden
1278    fn evaluate_93(&self, ctx: &EvaluationContext) -> ConditionResult {
1279        ctx.lacks_qualifier("LIN", 1, "Z16")
1280    }
1281
1282    /// [94] Wenn in derselben Nachricht eine SG29 mit Z54 in LIN DE1229 (Erforderliches Produkt der Marktlokation) nicht vorhanden
1283    fn evaluate_94(&self, ctx: &EvaluationContext) -> ConditionResult {
1284        ctx.lacks_qualifier("LIN", 1, "Z54")
1285    }
1286
1287    /// [95] Messprodukt-Code aus dem Kapitel 2.1 "Standard-Messprodukte der Marktlokation" der Codeliste der Konfigurationen
1288    /// EXTERNAL: Requires context from outside the message.
1289    fn evaluate_95(&self, ctx: &EvaluationContext) -> ConditionResult {
1290        ctx.external.evaluate("messprodukt_standard_marktlokation")
1291    }
1292
1293    /// [96] Messprodukt-Code aus dem Kapitel 2.2 "Standard-Messprodukte der Tranche" der Codeliste der Konfigurationen
1294    /// EXTERNAL: Requires context from outside the message.
1295    fn evaluate_96(&self, ctx: &EvaluationContext) -> ConditionResult {
1296        ctx.external.evaluate("messprodukt_standard_tranche")
1297    }
1298
1299    /// [97] Wenn FTX+Z10 vorhanden
1300    fn evaluate_97(&self, ctx: &EvaluationContext) -> ConditionResult {
1301        ctx.has_qualifier("FTX", 0, "Z10")
1302    }
1303
1304    /// [99] Wenn FTX+Z08/Z10 vorhanden
1305    fn evaluate_99(&self, ctx: &EvaluationContext) -> ConditionResult {
1306        ctx.any_group_has_any_qualifier("FTX", 0, &["Z08", "Z10"], &["SG29"])
1307    }
1308
1309    /// [100] Messprodukt-Code aus dem Kapitel 2.3 "Standard-Messprodukte der Messlokation" der Codeliste der Konfigurationen
1310    /// EXTERNAL: Requires context from outside the message.
1311    fn evaluate_100(&self, ctx: &EvaluationContext) -> ConditionResult {
1312        ctx.external.evaluate("messprodukt_standard_messlokation")
1313    }
1314
1315    /// [101] Wenn MP-ID in SG2 NAD+MR mit Rolle MSB in der Sparte Gas nicht vorhanden
1316    /// EXTERNAL: Requires context from outside the message.
1317    // REVIEW: Checking whether the NAD+MR recipient has role MSB in Sparte Gas requires external business context about market participant roles — not determinable from message content alone. (medium confidence)
1318    fn evaluate_101(&self, ctx: &EvaluationContext) -> ConditionResult {
1319        ctx.external.evaluate("recipient_is_msb_gas")
1320    }
1321
1322    /// [102] Wenn in derselben SG29 LIN das CCI+++Z25 (Geräteart Wandler) vorhanden
1323    fn evaluate_102(&self, ctx: &EvaluationContext) -> ConditionResult {
1324        // HAND-EDITED: "in dieser/derselben SG29" — scoped to the enclosing group.
1325        ctx.this_group_has_qualifier("CCI", 2, "Z25", &["SG29"])
1326    }
1327
1328    /// [103] Wenn in derselben SG29 LIN das CCI+++Z25 (Geräteart Wandler) nicht vorhanden
1329    fn evaluate_103(&self, ctx: &EvaluationContext) -> ConditionResult {
1330        // HAND-EDITED: "in dieser/derselben SG29" — scoped to the enclosing group.
1331        match ctx.this_group_has_qualifier("CCI", 2, "Z25", &["SG29"]) {
1332            ConditionResult::True => ConditionResult::False,
1333            ConditionResult::False => ConditionResult::True,
1334            ConditionResult::Unknown => ConditionResult::Unknown,
1335        }
1336    }
1337
1338    /// [104] Wenn MP-ID in SG2 NAD+VY mit Rolle LF vorhanden
1339    /// EXTERNAL: Requires context from outside the message.
1340    // REVIEW: Checking whether the NAD+VY party has role LF requires external market participant role context — cannot be determined from the EDIFACT message alone. (medium confidence)
1341    fn evaluate_104(&self, ctx: &EvaluationContext) -> ConditionResult {
1342        ctx.external.evaluate("nad_vy_is_lf")
1343    }
1344
1345    /// [105] Wenn die bisherige Konfiguration mit Zählzeiten des LF vom LF beendet werden soll
1346    /// EXTERNAL: Requires context from outside the message.
1347    fn evaluate_105(&self, ctx: &EvaluationContext) -> ConditionResult {
1348        ctx.external
1349            .evaluate("bisherige_konfiguration_zaehltzeiten_lf_beenden")
1350    }
1351
1352    /// [106] wenn IMD++Z60 (Abbestellung Messprodukt mit Zählzeitdefinition des LF) nicht vorhanden
1353    fn evaluate_106(&self, ctx: &EvaluationContext) -> ConditionResult {
1354        ctx.lacks_qualifier("IMD", 1, "Z60")
1355    }
1356
1357    /// [107] Wenn IMD++Z57 (Abbestellung Zählzeitdefinition) nicht vorhanden
1358    fn evaluate_107(&self, ctx: &EvaluationContext) -> ConditionResult {
1359        ctx.lacks_qualifier("IMD", 1, "Z57")
1360    }
1361
1362    /// [108] Wenn RFF+AGK (Konfigurations-ID) nicht vorhanden
1363    fn evaluate_108(&self, ctx: &EvaluationContext) -> ConditionResult {
1364        ctx.lacks_qualifier("RFF", 0, "AGK")
1365    }
1366
1367    /// [109] Wenn LOC+172 (Meldepunkt) nicht vorhanden
1368    fn evaluate_109(&self, ctx: &EvaluationContext) -> ConditionResult {
1369        ctx.lacks_qualifier("LOC", 0, "172")
1370    }
1371
1372    /// [110] wenn DTM+163 (Beginn Zeitraum für Wertanfrage) vorhanden
1373    fn evaluate_110(&self, ctx: &EvaluationContext) -> ConditionResult {
1374        ctx.has_qualifier("DTM", 0, "163")
1375    }
1376
1377    /// [112] Wenn in derselben SG29 LIN++Z54 (Erforderliches Produkt der Marktlokation), das PIA+5+9991000000078: Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
1378    // REVIEW: In same SG29: LIN with elements[1][0]=Z54 (Erforderliches Produkt der Marktlokation) co-occurs with PIA where elements[1][0]=9991000000078 (Wirkarbeit Lastgang 1/4h, product code from C212.DE7140). Co-occurrence check scoped to SG29. (medium confidence)
1379    fn evaluate_112(&self, ctx: &EvaluationContext) -> ConditionResult {
1380        // HAND-EDITED: "in derselben SG29 LIN++Z54 … PIA+5+9991000000078:Z11" — the enclosing SG29 only, when the scope
1381        // reaches one; the message as before otherwise.
1382        if let Some(scope) = ctx.enclosing_scope("SG29") {
1383            return ConditionResult::from(
1384                ctx.find_segments_at(scope, "LIN")
1385                    .iter()
1386                    .any(|s| s.get_element(1) == "Z54")
1387                    && ctx.find_segments_at(scope, "PIA").iter().any(|s| {
1388                        s.get_element(0) == "5"
1389                            && s.get_component(1, 0) == "9991000000078"
1390                            && s.get_component(1, 1) == "Z11"
1391                    }),
1392            );
1393        }
1394        ctx.any_group_has_co_occurrence(
1395            "LIN",
1396            1,
1397            &["Z54"],
1398            "PIA",
1399            1,
1400            0,
1401            &["9991000000078"],
1402            &["SG29"],
1403        )
1404    }
1405
1406    /// [113] Wenn LIN DE1229 mit Code Z42 (Zählzeitdefinition) vorhanden
1407    fn evaluate_113(&self, ctx: &EvaluationContext) -> ConditionResult {
1408        ctx.has_qualifier("LIN", 1, "Z42")
1409    }
1410
1411    /// [114] Wenn LIN DE1229 mit Code Z69 (Schaltzeitdefinition) vorhanden
1412    fn evaluate_114(&self, ctx: &EvaluationContext) -> ConditionResult {
1413        ctx.has_qualifier("LIN", 1, "Z69")
1414    }
1415
1416    /// [115] Wenn LIN DE1229 mit Code Z70 (Leistungskurvendefinition) vorhanden
1417    fn evaluate_115(&self, ctx: &EvaluationContext) -> ConditionResult {
1418        ctx.has_qualifier("LIN", 1, "Z70")
1419    }
1420
1421    /// [117] Wenn SG29 LIN++Z64 (Erforderliches Produkt Schaltzeitdefinitionen) vorhanden
1422    fn evaluate_117(&self, ctx: &EvaluationContext) -> ConditionResult {
1423        ctx.has_qualifier("LIN", 1, "Z64")
1424    }
1425
1426    /// [118] Wenn SG29 LIN++Z65 (Erforderliches Produkt Leistungskurvendefinitionen) vorhanden
1427    fn evaluate_118(&self, ctx: &EvaluationContext) -> ConditionResult {
1428        ctx.has_qualifier("LIN", 1, "Z65")
1429    }
1430
1431    /// [119] Wenn SG29 LIN++Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) vorhanden
1432    fn evaluate_119(&self, ctx: &EvaluationContext) -> ConditionResult {
1433        ctx.has_qualifier("LIN", 1, "Z66")
1434    }
1435
1436    /// [120] Wenn SG29 LIN++Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) vorhanden
1437    fn evaluate_120(&self, ctx: &EvaluationContext) -> ConditionResult {
1438        ctx.has_qualifier("LIN", 1, "Z67")
1439    }
1440
1441    /// [121] Wenn SG29 LIN++Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) vorhanden
1442    fn evaluate_121(&self, ctx: &EvaluationContext) -> ConditionResult {
1443        ctx.has_qualifier("LIN", 1, "Z68")
1444    }
1445
1446    /// [122] Wenn SG1 RFF+Z41 (Referenznummer des Vorgangs der Anmeldung nach WiM) nicht vorhanden
1447    fn evaluate_122(&self, ctx: &EvaluationContext) -> ConditionResult {
1448        ctx.lacks_qualifier("RFF", 0, "Z41")
1449    }
1450
1451    /// [123] Wenn DTM+203 (Ausführungsdatum) nicht vorhanden
1452    fn evaluate_123(&self, ctx: &EvaluationContext) -> ConditionResult {
1453        ctx.lacks_qualifier("DTM", 0, "203")
1454    }
1455
1456    /// [124] Wenn SG1 RFF+Z41 (Referenznummer des Vorgangs der Anmeldung nach WiM) vorhanden
1457    fn evaluate_124(&self, ctx: &EvaluationContext) -> ConditionResult {
1458        ctx.has_qualifier("RFF", 0, "Z41")
1459    }
1460
1461    /// [125] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.1 „Konfigurationsprodukte Schaltzeitdefinition“ enthalten sind.
1462    /// EXTERNAL: Requires context from outside the message.
1463    fn evaluate_125(&self, ctx: &EvaluationContext) -> ConditionResult {
1464        ctx.external
1465            .evaluate("konfigurationsprodukt_schaltzeitdefinition")
1466    }
1467
1468    /// [130] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Marktlokation angegeben ist
1469    fn evaluate_130(&self, ctx: &EvaluationContext) -> ConditionResult {
1470        // Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Marktlokation angegeben ist
1471        // LOC Meldepunkt: elements[0][0]=="172", elements[1][0]==DE3225 (Identifikator)
1472        let locs = ctx.find_segments_with_qualifier("LOC", 0, "172");
1473        match locs
1474            .first()
1475            .and_then(|s| s.elements.get(1))
1476            .and_then(|e| e.first())
1477        {
1478            Some(val) if !val.is_empty() => validate_malo_id(val),
1479            Some(_) => ConditionResult::False,
1480            None => ConditionResult::False, // segment absent → condition not applicable
1481        }
1482    }
1483
1484    /// [131] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Messlokation angegeben ist
1485    /// EXTERNAL: Requires context from outside the message.
1486    fn evaluate_131(&self, ctx: &EvaluationContext) -> ConditionResult {
1487        ctx.external.evaluate("meldepunkt_is_messlokation_id")
1488    }
1489
1490    /// [132] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Netzlokation angegeben ist
1491    /// EXTERNAL: Requires context from outside the message.
1492    fn evaluate_132(&self, ctx: &EvaluationContext) -> ConditionResult {
1493        ctx.external.evaluate("meldepunkt_is_netzlokation_id")
1494    }
1495
1496    /// [133] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Steuerbaren Ressource angegeben ist
1497    /// EXTERNAL: Requires context from outside the message.
1498    fn evaluate_133(&self, ctx: &EvaluationContext) -> ConditionResult {
1499        ctx.external
1500            .evaluate("meldepunkt_is_steuerbare_ressource_id")
1501    }
1502
1503    /// [134] Wenn die Position in der ursprünglichen Antwort auf die Bestellung aus SG1 RFF+Z42 vorhanden war und reklamiert werden soll
1504    // REVIEW: RFF+Z42 in SG1 (elements[0][0]=="Z42") references the original order response position. Presence of this segment in the current message indicates the reclaim scenario. The 'reklamiert werden soll' clause is implied by the reference existing. (medium confidence)
1505    fn evaluate_134(&self, ctx: &EvaluationContext) -> ConditionResult {
1506        ctx.has_qualifier("RFF", 0, "Z42")
1507    }
1508
1509    /// [135] Wenn SG29 LIN++Z64 (Erforderliches Produkt Schaltzeitdefinitionen) nicht vorhanden
1510    fn evaluate_135(&self, ctx: &EvaluationContext) -> ConditionResult {
1511        ctx.lacks_qualifier("LIN", 1, "Z64")
1512    }
1513
1514    /// [136] Wenn SG29 LIN++Z65 (Erforderliches Produkt Leistungskurvendefinitionen) nicht vorhanden
1515    fn evaluate_136(&self, ctx: &EvaluationContext) -> ConditionResult {
1516        ctx.lacks_qualifier("LIN", 1, "Z65")
1517    }
1518
1519    /// [137] Wenn SG29 LIN++Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) nicht vorhanden
1520    fn evaluate_137(&self, ctx: &EvaluationContext) -> ConditionResult {
1521        ctx.lacks_qualifier("LIN", 1, "Z66")
1522    }
1523
1524    /// [138] Wenn SG29 LIN++Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) nicht vorhanden
1525    fn evaluate_138(&self, ctx: &EvaluationContext) -> ConditionResult {
1526        ctx.lacks_qualifier("LIN", 1, "Z67")
1527    }
1528
1529    /// [139] Wenn SG29 LIN++Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) nicht vorhanden
1530    fn evaluate_139(&self, ctx: &EvaluationContext) -> ConditionResult {
1531        ctx.lacks_qualifier("LIN", 1, "Z68")
1532    }
1533
1534    /// [140] Messprodukt-Code aus dem Kapitel 2.4 "Standard-Messprodukte der Netzlokation" der Codeliste der Konfigurationen
1535    /// EXTERNAL: Requires context from outside the message.
1536    fn evaluate_140(&self, ctx: &EvaluationContext) -> ConditionResult {
1537        ctx.external
1538            .evaluate("is_standard_messprodukt_netzlokation_code")
1539    }
1540
1541    /// [147] wenn im DE3155 in demselben COM der Code EM vorhanden ist
1542    fn evaluate_147(&self, ctx: &EvaluationContext) -> ConditionResult {
1543        let com_segments = ctx.find_segments("COM");
1544        ConditionResult::from(com_segments.iter().any(|s| {
1545            s.elements
1546                .first()
1547                .and_then(|e| e.get(1))
1548                .is_some_and(|v| v == "EM")
1549        }))
1550    }
1551
1552    /// [148] wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
1553    fn evaluate_148(&self, ctx: &EvaluationContext) -> ConditionResult {
1554        let com_segments = ctx.find_segments("COM");
1555        ConditionResult::from(com_segments.iter().any(|s| {
1556            s.elements
1557                .first()
1558                .and_then(|e| e.get(1))
1559                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
1560        }))
1561    }
1562
1563    /// [161] Wenn in derselben SG29 LIN (Erforderliches Produkt Abrechnungsdaten) das SG30 CAV+ZH9 (Code der Produkteigenschaft) nicht vorhanden ist.
1564    fn evaluate_161(&self, ctx: &EvaluationContext) -> ConditionResult {
1565        // HAND-EDITED: "in derselben SG29 LIN das SG30 CAV+ZH9 nicht vorhanden" — the enclosing SG29 only, when the scope
1566        // reaches one; the message as before otherwise.
1567        if let Some(scope) = ctx.enclosing_scope("SG29") {
1568            return ConditionResult::from(
1569                !ctx.find_segments_at(scope, "CAV")
1570                    .iter()
1571                    .any(|s| s.get_element(0) == "ZH9"),
1572            );
1573        }
1574        ctx.any_group_has_qualifier_without("PIA", 0, "5", "CAV", 0, "ZH9", &["SG4", "SG29"])
1575    }
1576
1577    /// [163] Wenn innerhalb der Nachricht eine SG30 CCI+++ZA9 (Messprodukt für Übertragungsnetzbetreiber relevant) vorhanden ist.
1578    fn evaluate_163(&self, ctx: &EvaluationContext) -> ConditionResult {
1579        ctx.has_qualifier("CCI", 2, "ZA9")
1580    }
1581
1582    /// [167] Wenn SG2 NAD+DP (Meldepunkt), SG3 RFF+Z20 (Referenz auf ID der Tranche) vorhanden
1583    fn evaluate_167(&self, ctx: &EvaluationContext) -> ConditionResult {
1584        match (
1585            ctx.has_qualifier("NAD", 0, "DP"),
1586            ctx.has_qualifier("RFF", 0, "Z20"),
1587        ) {
1588            (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
1589            (ConditionResult::False, _) | (_, ConditionResult::False) => ConditionResult::False,
1590            _ => ConditionResult::Unknown,
1591        }
1592    }
1593
1594    /// [168] Wenn SG2 NAD+DP (Meldepunkt), SG3 RFF+Z20 (Referenz auf ID der Tranche) nicht vorhanden
1595    fn evaluate_168(&self, ctx: &EvaluationContext) -> ConditionResult {
1596        match ctx.has_qualifier("NAD", 0, "DP") {
1597            ConditionResult::True => ctx.lacks_qualifier("RFF", 0, "Z20"),
1598            other => other,
1599        }
1600    }
1601
1602    /// [169] Wenn in SG29 LIN++Z19 (Erforderliches Produkt der Messlokation) in SG30 CCI (Zugeordnete Zählzeitdefinition im DE7059 der Code Z39 (Code der Zählzeitdefinition) vorhanden ist
1603    fn evaluate_169(&self, ctx: &EvaluationContext) -> ConditionResult {
1604        ctx.any_group_has_co_occurrence("LIN", 1, &["Z19"], "CCI", 0, 0, &["Z39"], &["SG29"])
1605    }
1606
1607    /// [170] Wenn in derselben SG29 LIN die SG30 CCI+Z37++ZD1 (Basis zur Bildung der Tranchengröße) (Prozentual) vorhanden
1608    fn evaluate_170(&self, ctx: &EvaluationContext) -> ConditionResult {
1609        // HAND-EDITED: "in dieser/derselben SG29" — scoped to the enclosing group.
1610        ctx.this_group_has_qualified_value("CCI", 0, "Z37", 2, 0, &["ZD1"], &["SG29"])
1611    }
1612
1613    /// [171] Wenn in derselben SG29 LIN (Erforderliches Produkt der Tranche) das IMD+++Z64 (Leistungsbeschreibung Konfiguration) (Neukonfiguration) vorhanden ist
1614    fn evaluate_171(&self, ctx: &EvaluationContext) -> ConditionResult {
1615        // HAND-EDITED: "in dieser/derselben SG29" — scoped to the enclosing group.
1616        ctx.this_group_has_qualifier("IMD", 2, "Z64", &["SG29"])
1617    }
1618
1619    /// [173] Wenn IMD++Z66 (Beendigung Konfiguration aufgrund Überführung der Marktlokation zu ruhender Marktlokation) vorhanden
1620    fn evaluate_173(&self, ctx: &EvaluationContext) -> ConditionResult {
1621        ctx.has_qualifier("IMD", 1, "Z66")
1622    }
1623
1624    /// [174] Wenn IMD++Z67 (Aktivierung Konfiguration in der derzeit ruhenden Marktlokation) vorhanden
1625    fn evaluate_174(&self, ctx: &EvaluationContext) -> ConditionResult {
1626        ctx.has_qualifier("IMD", 1, "Z67")
1627    }
1628
1629    /// [175] Wenn SG2 NAD+DP (Meldepunkt), SG3 RFF+Z59 (Referenz auf die ID der Marktlokation der Kundenanlage) vorhanden
1630    fn evaluate_175(&self, ctx: &EvaluationContext) -> ConditionResult {
1631        match (
1632            ctx.has_qualifier("NAD", 0, "DP"),
1633            ctx.has_qualifier("RFF", 0, "Z59"),
1634        ) {
1635            (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
1636            (ConditionResult::False, _) | (_, ConditionResult::False) => ConditionResult::False,
1637            _ => ConditionResult::Unknown,
1638        }
1639    }
1640
1641    /// [176] Wenn SG2 NAD+DP (Meldepunkt), SG3 RFF+Z59 (Referenz auf die ID der Marktlokation der Kundenanlage) nicht vorhanden
1642    fn evaluate_176(&self, ctx: &EvaluationContext) -> ConditionResult {
1643        match ctx.has_qualifier("NAD", 0, "DP") {
1644            ConditionResult::True => ctx.lacks_qualifier("RFF", 0, "Z59"),
1645            other => other,
1646        }
1647    }
1648
1649    /// [177] Wenn IMD++Z66 (Beendigung Konfiguration aufgrund Überführung der Marktlokation zu ruhender Marktlokation) nicht vorhanden
1650    fn evaluate_177(&self, ctx: &EvaluationContext) -> ConditionResult {
1651        ctx.lacks_qualifier("IMD", 1, "Z66")
1652    }
1653
1654    /// [492] wenn MP-ID in NAD+MR aus Sparte Strom
1655    /// EXTERNAL: Requires context from outside the message.
1656    // REVIEW: Whether the MP-ID in NAD+MR belongs to the Strom (electricity) sector cannot be determined from the EDIFACT message alone — it requires a market participant registry lookup. (medium confidence)
1657    fn evaluate_492(&self, ctx: &EvaluationContext) -> ConditionResult {
1658        ctx.external.evaluate("recipient_is_strom")
1659    }
1660
1661    /// [493] wenn MP-ID in NAD+MR aus Sparte Gas
1662    /// EXTERNAL: Requires context from outside the message.
1663    // REVIEW: Whether the MP-ID in NAD+MR belongs to the Gas sector cannot be determined from the EDIFACT message alone — it requires a market participant registry lookup. (medium confidence)
1664    fn evaluate_493(&self, ctx: &EvaluationContext) -> ConditionResult {
1665        ctx.external.evaluate("recipient_is_gas")
1666    }
1667
1668    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt.
1669    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
1670        // HAND-EDITED: subject is the current DTM's DE2380 (via ctx.resolved_value),
1671        // not a guessed other qualifier (it compared DTM+203 with DTM+137 and failed
1672        // DTM+137 itself whenever DTM+203 was later). Lexicographic compare is valid
1673        // for fixed-width CCYYMMDDHHMM+ZZ (format 303).
1674        let current = match ctx.resolved_value {
1675            Some(v) if !v.is_empty() => v,
1676            _ => return ConditionResult::Unknown,
1677        };
1678        let threshold = match ctx
1679            .find_segments_with_qualifier("DTM", 0, "137")
1680            .first()
1681            .and_then(|s| s.elements.first())
1682            .and_then(|e| e.get(1))
1683        {
1684            Some(v) if !v.is_empty() => v.clone(),
1685            _ => return ConditionResult::Unknown,
1686        };
1687        ConditionResult::from(current <= threshold.as_str())
1688    }
1689
1690    /// [495] Der Zeitpunkt muss ≤ dem Wert im DE2380 des DTM+137 sein
1691    fn evaluate_495(&self, ctx: &EvaluationContext) -> ConditionResult {
1692        // HAND-EDITED: subject is the current DTM's DE2380 (via ctx.resolved_value),
1693        // not a guessed other qualifier (it compared DTM+203 with DTM+137 and failed
1694        // DTM+137 itself whenever DTM+203 was later). Lexicographic compare is valid
1695        // for fixed-width CCYYMMDDHHMM+ZZ (format 303).
1696        let current = match ctx.resolved_value {
1697            Some(v) if !v.is_empty() => v,
1698            _ => return ConditionResult::Unknown,
1699        };
1700        let threshold = match ctx
1701            .find_segments_with_qualifier("DTM", 0, "137")
1702            .first()
1703            .and_then(|s| s.elements.first())
1704            .and_then(|e| e.get(1))
1705        {
1706            Some(v) if !v.is_empty() => v.clone(),
1707            _ => return ConditionResult::Unknown,
1708        };
1709        ConditionResult::from(current <= threshold.as_str())
1710    }
1711
1712    /// [500] Hinweis: Zählpunkt der BAS
1713    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
1714        ConditionResult::Unknown
1715    }
1716
1717    /// [501] Hinweis: Zählpunkt der DZR
1718    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
1719        ConditionResult::Unknown
1720    }
1721
1722    /// [503] Hinweis: Angabe eines technischen Ansprechpartners für die Geräteübernahme
1723    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
1724        ConditionResult::Unknown
1725    }
1726
1727    /// [506] Hinweis: Datum, bis zu dem der MSBA zur Fortführung verpflichtet wird
1728    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
1729        ConditionResult::Unknown
1730    }
1731
1732    /// [507] Hinweis: Es müssen alle nach Durchführung der Messlokationsänderung eingesetzten/genutzten OBIS- Kennzahlen übermittelt werden (wird eine "Erweiterung" des Messumfangs beauftragt, sind auch die...
1733    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
1734        ConditionResult::Unknown
1735    }
1736
1737    /// [514] Hinweis: Das Abonnement kann frühestens ab dem aktuellen Liefermonat beim Netzbetreiber gestartet werden.
1738    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
1739        ConditionResult::Unknown
1740    }
1741
1742    /// [515] Hinweis: Das angegebene Betrachtungszeitintervall bestimmt beim Start Abo bzw. Ende Abo ab bzw. bis wann (einschließlich) das Abo laufen soll.
1743    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
1744        ConditionResult::Unknown
1745    }
1746
1747    /// [517] Hinweis: Zählpunkt der LF-AASZR
1748    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
1749        ConditionResult::Unknown
1750    }
1751
1752    /// [518] Hinweis: Das angegebene Ausführungsdatum bestimmt beim Start Abo bzw. Ende Abo ab bzw. bis wann (einschließlich) das Abo laufen soll.
1753    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
1754        ConditionResult::Unknown
1755    }
1756
1757    /// [519] Hinweis: Bei Gas bezieht sich die Anforderung immer sowohl auf die vorläufigen Profilwerte als auch auf die endgültigen Profilwerte, falls diese bereits vorliegen.
1758    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
1759        ConditionResult::Unknown
1760    }
1761
1762    /// [521] Hinweis: Verwendung der ID der Marktlokation
1763    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
1764        // Hinweis: Verwendung der ID der Marktlokation — informational note, always applies
1765        ConditionResult::True
1766    }
1767
1768    /// [522] Hinweis: Verwendung der ID der Messlokation
1769    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
1770        // Hinweis: Verwendung der ID der Messlokation — informational note, always applies
1771        ConditionResult::True
1772    }
1773
1774    /// [523] Hinweis: Verwendung der ID der Tranche
1775    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
1776        // Hinweis: Verwendung der ID der Tranche — informational note, always applies
1777        ConditionResult::True
1778    }
1779
1780    /// [525] Hinweis: Wert aus BGM DE1004 der MSCONS
1781    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
1782        // Hinweis: Wert aus BGM DE1004 der MSCONS — informational note, always applies
1783        ConditionResult::True
1784    }
1785
1786    /// [527] Hinweis: Zählpunkt der BG-SZR (Kategorie B)
1787    fn evaluate_527(&self, _ctx: &EvaluationContext) -> ConditionResult {
1788        // Hinweis: Zählpunkt der BG-SZR (Kategorie B) — informational note, always applies
1789        ConditionResult::True
1790    }
1791
1792    /// [530] Hinweis: Wert aus BGM+310 DE1004 der QUOTES mit der das Angebot erfolgt ist.
1793    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
1794        // Hinweis: Wert aus BGM+310 DE1004 der QUOTES mit der das Angebot erfolgt ist — informational note, always applies
1795        ConditionResult::True
1796    }
1797
1798    /// [531] Hinweis: Wert aus LIN DE1082 der QUOTES, mit der das Angebot erfolgt ist
1799    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
1800        // Hinweis: Wert aus LIN DE1082 der QUOTES, mit der das Angebot erfolgt ist — informational note, always applies
1801        ConditionResult::True
1802    }
1803
1804    /// [532] Hinweis: Wert aus BGM+Z29 DE1004 der QUOTES, mit der das Angebot zur Abrechnung des Messstellenbetriebs erfolgt ist.
1805    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
1806        // Hinweis: Wert aus BGM+Z29 DE1004 der QUOTES, mit der das Angebot zur Abrechnung des Messstellenbetriebs erfolgt ist — informational note, always applies
1807        ConditionResult::True
1808    }
1809
1810    /// [533] Hinweis: Gerichtsvollzieher hat Termin vorgegeben
1811    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
1812        // Hinweis: Gerichtsvollzieher hat Termin vorgegeben — informational note, always applies
1813        ConditionResult::True
1814    }
1815
1816    /// [535] Hinweis: Verwendung der ID der Messlokation der Sparte Strom
1817    fn evaluate_535(&self, _ctx: &EvaluationContext) -> ConditionResult {
1818        // Hinweis: Verwendung der ID der Messlokation der Sparte Strom — informational note, always applies
1819        ConditionResult::True
1820    }
1821
1822    /// [536] Hinweis: Vorgangsnummer aus IDE DE7402 der UTILTS mit BGM+Z59
1823    fn evaluate_536(&self, _ctx: &EvaluationContext) -> ConditionResult {
1824        // Hinweis: Vorgangsnummer aus IDE DE7402 der UTILTS mit BGM+Z59 — informational note, always applies
1825        ConditionResult::True
1826    }
1827
1828    /// [539] Hinweis: Es sind nur Codes von Zählzeiten aus Liste des NB anzugeben
1829    fn evaluate_539(&self, _ctx: &EvaluationContext) -> ConditionResult {
1830        // Hinweis: Es sind nur Codes von Zählzeiten aus Liste des NB anzugeben — informational note, always applies
1831        ConditionResult::True
1832    }
1833
1834    /// [540] Hinweis: Es sind nur Codes von Zählzeiten aus Liste des LF anzugeben
1835    fn evaluate_540(&self, _ctx: &EvaluationContext) -> ConditionResult {
1836        // Hinweis: Es sind nur Codes von Zählzeiten aus Liste des LF anzugeben — informational note, always applies
1837        ConditionResult::True
1838    }
1839
1840    /// [542] Hinweis: Wert aus BGM+Z57 DE1004 der QUOTES mit der das Angebot erfolgt ist.
1841    fn evaluate_542(&self, _ctx: &EvaluationContext) -> ConditionResult {
1842        // Hinweis: Wert aus BGM+Z57 DE1004 der QUOTES mit der das Angebot erfolgt ist — informational note, always applies
1843        ConditionResult::True
1844    }
1845
1846    /// [543] Hinweis: Wert aus BGM+Z57 DE1004 der ORDERS mit der die Bestellung der Werte erfolgt ist.
1847    fn evaluate_543(&self, _ctx: &EvaluationContext) -> ConditionResult {
1848        // Hinweis: Wert aus BGM+Z57 DE1004 der ORDERS mit der die Bestellung der Werte erfolgt ist — informational note, always applies
1849        ConditionResult::True
1850    }
1851
1852    /// [545] Hinweis: Es werden nur die Messprodukte der Messlokationen angegeben, die für die in der SG2 genannte Marktlokation bzw. deren Tranchen erforderlich sind. Messprodukte an der Messlokation für wei...
1853    fn evaluate_545(&self, _ctx: &EvaluationContext) -> ConditionResult {
1854        // Hinweis: Es werden nur die Messprodukte der Messlokationen angegeben... — informational note, always applies
1855        ConditionResult::True
1856    }
1857
1858    /// [547] Hinweis: Dokumentennummer aus BGM+Z60 DE1004 der UTILTS
1859    fn evaluate_547(&self, _ctx: &EvaluationContext) -> ConditionResult {
1860        // Hinweis: Dokumentennummer aus BGM+Z60 DE1004 der UTILTS — informational note, always applies
1861        ConditionResult::True
1862    }
1863
1864    /// [548] Hinweis: Wenn die Änderung der Gerätekonfiguration mit einer Zählzeit übermittelt wird, ist hier die MP-ID des Eigentümers der Liste der Zählzeit einzutragen. Wenn anstatt der bisherigen Zäh...
1865    fn evaluate_548(&self, _ctx: &EvaluationContext) -> ConditionResult {
1866        // Hinweis: MP-ID des Eigentümers der Zählzeitliste — informational note, always applies
1867        ConditionResult::True
1868    }
1869
1870    /// [549] Hinweis: Findet bei der Reklamation von Zählerständen immer Anwendung. Einzige Ausnahme ist, wenn es sich um die Reklamation eines fehlenden Zählerstandes aufgrund einer Turnusablesung handelt, ...
1871    fn evaluate_549(&self, _ctx: &EvaluationContext) -> ConditionResult {
1872        // Hinweis: Reklamation von Zählerständen — Zeitpunktangabe vs. Zeitintervall — informational note, always applies
1873        ConditionResult::True
1874    }
1875
1876    /// [550] Hinweis: Findet nur dann Anwendung, wenn es sich um die Reklamation eines fehlenden Zählerstandes aufgrund einer Turnusablesung handelt, bei welcher der MSB am Objekt der Marktlokation die Informa...
1877    fn evaluate_550(&self, _ctx: &EvaluationContext) -> ConditionResult {
1878        // Hinweis: Reklamation fehlender Zählerstand bei Turnusablesung — informational note, always applies
1879        ConditionResult::True
1880    }
1881
1882    /// [551] Hinweis: Die SG29 ist so oft zu wiederholen, dass alle Messprodukte und Zählzeiten genannt werden, die ab dem in DTM+203 genannten Zeitpunkt auf der Messlokation durch den MSB konfiguriert werden ...
1883    fn evaluate_551(&self, _ctx: &EvaluationContext) -> ConditionResult {
1884        // Hinweis: SG29 Wiederholung für alle Messprodukte und Zählzeiten — informational note, always applies
1885        ConditionResult::True
1886    }
1887
1888    /// [552] Hinweis: Verwendung der ID der Netzlokation
1889    fn evaluate_552(&self, _ctx: &EvaluationContext) -> ConditionResult {
1890        // Hinweis: Verwendung der ID der Netzlokation — informational note, always applies
1891        ConditionResult::True
1892    }
1893
1894    /// [553] Hinweis: Verwendung der ID der Steuerbaren Ressource
1895    fn evaluate_553(&self, _ctx: &EvaluationContext) -> ConditionResult {
1896        // Hinweis: Verwendung der ID der Steuerbaren Ressource — informational note, always applies
1897        ConditionResult::True
1898    }
1899
1900    /// [554] Hinweis: Wert aus BGM+Z74 DE1004 der QUOTES mit der das Angebot erfolgt ist.
1901    fn evaluate_554(&self, _ctx: &EvaluationContext) -> ConditionResult {
1902        // Hinweis: Wert aus BGM+Z74 DE1004 der QUOTES — informational note about value origin, always applies
1903        ConditionResult::True
1904    }
1905
1906    /// [555] Hinweis: Vorgangsnummer aus SG4 IDE+24 DE7402 der UTILMD mit BGM+E01 mit der die Anmeldung des MSB-Wechsels erfolgt ist.
1907    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
1908        // Hinweis: Vorgangsnummer aus SG4 IDE+24 DE7402 der UTILMD — informational note about value origin, always applies
1909        ConditionResult::True
1910    }
1911
1912    /// [557] Hinweis: Es werden nur die Messprodukte der Messlokationen angegeben, die für die in der SG2 genannte Netzlokation erforderlich sind. Messprodukte an der Messlokation für weitere Netzlokationen o...
1913    fn evaluate_557(&self, _ctx: &EvaluationContext) -> ConditionResult {
1914        // Hinweis: Only Messprodukte for Messlokationen required for the named Netzlokation are specified — informational note, always applies
1915        ConditionResult::True
1916    }
1917
1918    /// [558] Hinweis: Dokumentennummer aus BGM+Z78 DE1004 der UTILTS
1919    fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
1920        // Hinweis: Dokumentennummer aus BGM+Z78 DE1004 der UTILTS — informational note, always applies
1921        ConditionResult::True
1922    }
1923
1924    /// [559] Hinweis: Dokumentennummer aus BGM+Z79 DE1004 der UTILTS
1925    fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
1926        // Hinweis: Dokumentennummer aus BGM+Z79 DE1004 der UTILTS — informational note, always applies
1927        ConditionResult::True
1928    }
1929
1930    /// [560] Hinweis: Vorgangsnummer aus IDE DE7402 der UTILTS mit BGM+Z80
1931    fn evaluate_560(&self, _ctx: &EvaluationContext) -> ConditionResult {
1932        // Hinweis: Vorgangsnummer aus IDE DE7402 der UTILTS mit BGM+Z80 — informational note, always applies
1933        ConditionResult::True
1934    }
1935
1936    /// [561] Hinweis: Vorgangsnummer aus IDE DE7402 der UTILTS mit BGM+Z81
1937    fn evaluate_561(&self, _ctx: &EvaluationContext) -> ConditionResult {
1938        // Hinweis: Vorgangsnummer aus IDE DE7402 der UTILTS mit BGM+Z81 — informational note, always applies
1939        ConditionResult::True
1940    }
1941
1942    /// [562] Hinweis: Wert aus BGM+Z73 DE1004 der IFTSTA mit der die Antwort auf die Bestellung der Konfiguration übermittelt wurde
1943    fn evaluate_562(&self, _ctx: &EvaluationContext) -> ConditionResult {
1944        // Hinweis: Wert aus BGM+Z73 DE1004 der IFTSTA — informational note, always applies
1945        ConditionResult::True
1946    }
1947
1948    /// [563] Hinweis: Vorgangsnummer aus CNI DE1490 der IFTSTA mit BGM+Z73 mit der die Antwort auf die Bestellung der Konfiguration übermittelt wurde
1949    fn evaluate_563(&self, _ctx: &EvaluationContext) -> ConditionResult {
1950        // Hinweis: Vorgangsnummer aus CNI DE1490 der IFTSTA mit BGM+Z73 mit der die Antwort auf die Bestellung der Konfiguration übermittelt wurde — informational note, always applies
1951        ConditionResult::True
1952    }
1953
1954    /// [564] Hinweis: Für den Empfang der Werte nach Typ 2 aus dem SMGW.
1955    fn evaluate_564(&self, _ctx: &EvaluationContext) -> ConditionResult {
1956        // Hinweis: Für den Empfang der Werte nach Typ 2 aus dem SMGW — informational note, always applies
1957        ConditionResult::True
1958    }
1959
1960    /// [566] Hinweis: Verwendung der ID der Technischen Ressource
1961    fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
1962        // Hinweis: Verwendung der ID der Technischen Ressource — informational note, always applies
1963        ConditionResult::True
1964    }
1965
1966    /// [567] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
1967    fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
1968        // Hinweis: Es darf nur eine Information im DE3148 übermittelt werden — informational note, always applies
1969        ConditionResult::True
1970    }
1971
1972    /// [568] Hinweis: Wenn die Einrichtung der Konfiguration mit einer Zählzeit übermittelt wird, ist hier die MP-ID des NB als Eigentümer der Liste der Zählzeit einzutragen.
1973    fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
1974        // Hinweis: Wenn die Einrichtung der Konfiguration mit einer Zählzeit übermittelt wird, ist hier die MP-ID des NB als Eigentümer der Liste der Zählzeit einzutragen — informational note, always applies
1975        ConditionResult::True
1976    }
1977
1978    /// [569] Hinweis: MaBiS-Zählpunkt der LF-SZR
1979    fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
1980        // Hinweis: MaBiS-Zählpunkt der LF-SZR — informational note, always applies
1981        ConditionResult::True
1982    }
1983
1984    /// [570] Hinweis: zur Angabe von Kontaktdaten des Kunden des Lieferanten um die Änderung an der Technik zu vereinfachen.
1985    fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
1986        // Hinweis: zur Angabe von Kontaktdaten des Kunden des Lieferanten um die Änderung an der Technik zu vereinfachen — informational note, always applies
1987        ConditionResult::True
1988    }
1989
1990    /// [571] Hinweis: MSB der Marktlokation, an den die Werte der weiteren Energieflussrichtung der Messlokation zu übermitteln sind.
1991    fn evaluate_571(&self, _ctx: &EvaluationContext) -> ConditionResult {
1992        // Hinweis: MSB der Marktlokation, an den die Werte der weiteren Energieflussrichtung der Messlokation zu übermitteln sind — informational note, always applies
1993        ConditionResult::True
1994    }
1995
1996    /// [572] Hinweis: MSB der Marktlokation der Kundenanlage, in der die betroffene Lokation integriert wird.
1997    fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
1998        // Hinweis: MSB der Marktlokation der Kundenanlage, in der die betroffene Lokation integriert wird — informational note, always applies
1999        ConditionResult::True
2000    }
2001
2002    /// [573] Hinweis: Es ist eine URI IPv4 für die Bereitstellung der Werte anzugeben.
2003    fn evaluate_573(&self, _ctx: &EvaluationContext) -> ConditionResult {
2004        // Hinweis: Es ist eine URI IPv4 für die Bereitstellung der Werte anzugeben — informational note, always applies
2005        ConditionResult::True
2006    }
2007
2008    /// [574] Hinweis: Es ist eine URI IPv6 für die Bereitstellung der Werte anzugeben.
2009    fn evaluate_574(&self, _ctx: &EvaluationContext) -> ConditionResult {
2010        // Hinweis: Es ist eine URI IPv6 für die Bereitstellung der Werte anzugeben.
2011        ConditionResult::True
2012    }
2013
2014    /// [575] Hinweis: Wenn der gewünschte Änderungszeitpunkt ein fixer Zeitpunkt ist.
2015    fn evaluate_575(&self, _ctx: &EvaluationContext) -> ConditionResult {
2016        // Hinweis: Wenn der gewünschte Änderungszeitpunkt ein fixer Zeitpunkt ist.
2017        ConditionResult::True
2018    }
2019
2020    /// [576] Hinweis: Wenn der gewünschte Änderungszeitpunkt ein nächst möglicher Termin zum oder nach dem angegebenen Zeitpunkt ist.
2021    fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
2022        // Hinweis: Wenn der gewünschte Änderungszeitpunkt ein nächst möglicher Termin zum oder nach dem angegebenen Zeitpunkt ist.
2023        ConditionResult::True
2024    }
2025
2026    /// [903] Format: Möglicher Wert: 1
2027    fn evaluate_903(&self, _ctx: &EvaluationContext) -> ConditionResult {
2028        ConditionResult::True
2029    }
2030
2031    /// [906] Format: max. 3 Nachkommastellen
2032    fn evaluate_906(&self, ctx: &EvaluationContext) -> ConditionResult {
2033        ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 3))
2034    }
2035
2036    /// [911] Format: Mögliche Werte: 1 bis n, je Nachricht oder Segmentgruppe bei 1 beginnend und fortlaufend aufsteigend
2037    fn evaluate_911(&self, _ctx: &EvaluationContext) -> ConditionResult {
2038        // Hinweis: Mögliche Werte: 1 bis n, je Nachricht oder Segmentgruppe bei 1 beginnend und fortlaufend aufsteigend
2039        // This is a cardinality/sequencing annotation — informational, always applies
2040        ConditionResult::True
2041    }
2042
2043    /// [914] Format: Möglicher Wert: &gt;0
2044    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
2045        ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">", 0.0))
2046    }
2047
2048    /// [922] Format: TR-ID
2049    // REVIEW: TR-ID (Transaktionsreferenz-ID) format. In ORDERS the transaction reference ID appears in BGM C106. The exact segment context depends on the AHB field definition — implemented as max-length-35 check on BGM, consistent with EDIFACT AN..35 data element constraints. Medium confidence because the target segment may differ by AHB row. (medium confidence)
2050    fn evaluate_922(&self, ctx: &EvaluationContext) -> ConditionResult {
2051        // Format: TR-ID — Transaktionsreferenz-ID, max 35 alphanumeric characters
2052        // In ORDERS, TR-ID typically appears in BGM element 1 (C106 document identifier)
2053        let segs = ctx.find_segments("BGM");
2054        match segs
2055            .first()
2056            .and_then(|s| s.elements.get(1))
2057            .and_then(|e| e.first())
2058        {
2059            Some(val) if !val.is_empty() => validate_max_length(val, 35),
2060            Some(_) => ConditionResult::False,
2061            None => ConditionResult::False, // segment absent → condition not applicable
2062        }
2063    }
2064
2065    /// [930] Format: max. 2 Nachkommastellen
2066    fn evaluate_930(&self, ctx: &EvaluationContext) -> ConditionResult {
2067        ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 2))
2068    }
2069
2070    /// [931] Format: ZZZ = +00
2071    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
2072        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
2073    }
2074
2075    /// [932] Format: HHMM = 2200
2076    // REVIEW: HHMM = 2200 time constraint on DTM. In ORDERS FV2504, delivery period end (qualifier 163) must terminate at 22:00 UTC, matching the standard German grid day-end convention. Medium confidence because the target DTM qualifier may differ for the specific AHB row — 163 is the most likely candidate for an end-of-period constraint. (medium confidence)
2077    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
2078        ctx.format_check_qualified("DTM", 0, "163", 0, 1, |val| {
2079            validate_hhmm_equals(val, "2200")
2080        })
2081    }
2082
2083    /// [933] Format: HHMM = 2300
2084    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
2085        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
2086    }
2087
2088    /// [934] Format: HHMM = 0400
2089    fn evaluate_934(&self, ctx: &EvaluationContext) -> ConditionResult {
2090        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0400"))
2091    }
2092
2093    /// [935] Format: HHMM = 0500
2094    fn evaluate_935(&self, ctx: &EvaluationContext) -> ConditionResult {
2095        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0500"))
2096    }
2097
2098    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
2099    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
2100        ctx.format_check("COM", 0, 0, validate_email)
2101    }
2102
2103    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
2104    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
2105        ctx.format_check("COM", 0, 0, validate_phone)
2106    }
2107
2108    /// [950] Format: Marktlokations-ID
2109    // HAND-EDITED (#373): the condition sits on LOC+172 and RFF C506 1154, not on
2110    // LOC+Z16; judge the value it is attached to.
2111    fn evaluate_950(&self, ctx: &EvaluationContext) -> ConditionResult {
2112        ctx.format_check_resolved(validate_malo_id)
2113    }
2114
2115    /// [951] Format: Zählpunktbezeichnung
2116    // HAND-EDITED (#373): the condition sits on LOC+172 and RFF C506 1154, not on
2117    // LOC+Z17; judge the value it is attached to.
2118    fn evaluate_951(&self, ctx: &EvaluationContext) -> ConditionResult {
2119        ctx.format_check_resolved(validate_zahlpunkt)
2120    }
2121
2122    /// [955] Format: Möglicher Wert: &lt;100
2123    // REVIEW: Format: Möglicher Wert: <100 — numeric value must be less than 100. Most likely applies to a QTY quantity value (C186.D6060, elements[0][1]). validate_numeric with "<" and threshold 100.0 covers this. Medium confidence because the exact segment context (QTY vs PRI vs PCT) is not specified — QTY is the most common numeric value carrier in ORDERS. (medium confidence)
2124    fn evaluate_955(&self, ctx: &EvaluationContext) -> ConditionResult {
2125        ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, "<", 100.0))
2126    }
2127
2128    /// [960] Format: Netzlokations-ID
2129    // HAND-EDITED: a Netzlokations-ID is "E" + ten alphanumerics (`E1688117482`),
2130    // not MaLo-shaped; `validate_malo_id` rejected every real one (#173).
2131    // HAND-EDITED (#373): the condition sits on LOC+172 and RFF C506 1154, not on
2132    // LOC+Z18; judge the value it is attached to.
2133    fn evaluate_960(&self, ctx: &EvaluationContext) -> ConditionResult {
2134        ctx.format_check_resolved(validate_nelo_id)
2135    }
2136
2137    /// [961] Format: SR-ID
2138    // HAND-EDITED: an SR-ID ("C" + 10 characters), not a MaLo-ID.
2139    // HAND-EDITED (#373): the condition sits on LOC+172 and RFF C506 1154, not on
2140    // LOC+Z19; judge the value it is attached to.
2141    fn evaluate_961(&self, ctx: &EvaluationContext) -> ConditionResult {
2142        ctx.format_check_resolved(validate_sr_id)
2143    }
2144
2145    /// [962] Format: max. 6 Vorkommastellen
2146    // REVIEW: Format: max 6 integer digits (Vorkommastellen). Applied to QTY element[0][1] as the most common numeric data field in ORDERS requiring integer-part validation. Uses validate_max_integer_digits helper. Confidence medium because the target segment is inferred from context rather than explicitly stated. (medium confidence)
2147    fn evaluate_962(&self, ctx: &EvaluationContext) -> ConditionResult {
2148        ctx.format_check("QTY", 0, 1, |val| validate_max_integer_digits(val, 6))
2149    }
2150
2151    /// [2002] Ist mindestens zwei Mal anzugeben
2152    // REVIEW: Must be specified at least twice. Counts DTM+203 (Ausführungsdatum, qualifier at elements[0][0]) occurrences as a proxy for SG29 group instance count since each SG29 contains exactly one DTM+203. Condition is True when count >= 2. (medium confidence)
2153    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
2154        // "Ist mindestens zwei Mal anzugeben" — SG29 must appear at least twice.
2155        // DTM+203 (Ausführungsdatum) occurs exactly once per SG29 instance,
2156        // so counting DTM+203 segments gives the SG29 repetition count.
2157        let count = ctx.find_segments_with_qualifier("DTM", 0, "203").len();
2158        ConditionResult::from(count >= 2)
2159    }
2160
2161    /// [2004] Die SG29 ist so oft zu wiederholen, dass alle Geräte der betroffenen Geräteart die im Rahmen des Gerätewechsels aufgrund MSB-Wechsel getauscht werden sollen genannt sind.
2162    fn evaluate_2004(&self, _ctx: &EvaluationContext) -> ConditionResult {
2163        // Hinweis: Die SG29 ist so oft zu wiederholen, dass alle Geräte der betroffenen Geräteart
2164        // die im Rahmen des Gerätewechsels aufgrund MSB-Wechsel getauscht werden sollen, genannt sind.
2165        // Informational cardinality note — always applies.
2166        ConditionResult::True
2167    }
2168
2169    /// [2005] Pro SG29 LIN ist die SG34 RFF+Z09 (Gerätenummer) genau einmal anzugeben
2170    // HAND-EDITED (#392, reported by mako-twin): "pro SG29 LIN" — the RFF+Z09
2171    // of this SG29, not of the message: two LINs with one Gerätenummer each
2172    // made [2005] false for both.
2173    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
2174        ConditionResult::from(geraetenummern_in_this_lin(ctx) == 1)
2175    }
2176
2177    /// [2006] Pro SG29 LIN ist die SG34 RFF+Z09 (Gerätenummer) bis zu dreimal anzugeben
2178    // HAND-EDITED (#392, reported by mako-twin): "pro SG29 LIN" — the RFF+Z09
2179    // of this SG29, not of the message: two LINs with one Gerätenummer each
2180    // made [2005] false for both.
2181    fn evaluate_2006(&self, ctx: &EvaluationContext) -> ConditionResult {
2182        ConditionResult::from(geraetenummern_in_this_lin(ctx) <= 3)
2183    }
2184
2185    /// [2007] Die SG29 ist so oft zu wiederholen, wie ab dem DTM+203 (Ausführungsdatum) Messlokationen zu der in der SG2 genannten Netzlokation vorhanden sind und für jede dieser Messlokationen müssen alle Me...
2186    fn evaluate_2007(&self, _ctx: &EvaluationContext) -> ConditionResult {
2187        // Hinweis: Die SG29 ist so oft zu wiederholen, wie ab dem DTM+203 (Ausführungsdatum)
2188        // Messlokationen zu der in der SG2 genannten Netzlokation vorhanden sind und für jede
2189        // dieser Messlokationen müssen alle Messprodukte genannt sein, die ab dem DTM+203
2190        // (Ausführungsdatum) in der SG2 genannten Netzlokation vorhanden sind.
2191        // Informational cardinality note — always applies.
2192        ConditionResult::True
2193    }
2194
2195    /// [2044] Pro SG29 LIN ist die SG29 PIA genau einmal anzugeben
2196    // REVIEW: Per SG29 LIN, the SG29 PIA segment must appear exactly once. We count PIA segments in the message and check for exactly one occurrence. This is a structural cardinality check on the PIA segment. (medium confidence)
2197    fn evaluate_2044(&self, ctx: &EvaluationContext) -> ConditionResult {
2198        {
2199            let pias = ctx.find_segments("PIA");
2200            ConditionResult::from(pias.len() == 1)
2201        }
2202    }
2203
2204    /// [2050] Pro Nachricht ist die SG29 genau einmal anzugeben
2205    // REVIEW: Per message, SG29 (identified by its entry segment LIN) must appear exactly once. We count all LIN segments in the message and check for exactly one occurrence. (medium confidence)
2206    fn evaluate_2050(&self, ctx: &EvaluationContext) -> ConditionResult {
2207        {
2208            let lins = ctx.find_segments("LIN");
2209            ConditionResult::from(lins.len() == 1)
2210        }
2211    }
2212
2213    /// [2060] Pro Nachricht ist die SG29 LIN+Z64 (Erforderliches Produkt Schaltzeitdefinitionen) maximal einmal anzugeben
2214    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
2215        {
2216            let lins = ctx.find_segments("LIN");
2217            let count = lins
2218                .iter()
2219                .filter(|s| {
2220                    s.elements
2221                        .get(1)
2222                        .and_then(|e| e.first())
2223                        .is_some_and(|v| v == "Z64")
2224                })
2225                .count();
2226            ConditionResult::from(count <= 1)
2227        }
2228    }
2229
2230    /// [2061] Pro Nachricht ist die SG29 LIN++Z65 (Erforderliches Produkt Leistungskurvendefinitionen) maximal einmal anzugeben
2231    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
2232        {
2233            let lins = ctx.find_segments("LIN");
2234            let count = lins
2235                .iter()
2236                .filter(|s| {
2237                    s.elements
2238                        .get(1)
2239                        .and_then(|e| e.first())
2240                        .is_some_and(|v| v == "Z65")
2241                })
2242                .count();
2243            ConditionResult::from(count <= 1)
2244        }
2245    }
2246
2247    /// [2062] Pro Nachricht ist die SG29 LIN++Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) maximal einmal anzugeben
2248    // REVIEW: Count LIN segments where elements[1][0]=="Z66" (LIN++Z66, ad-hoc Steuerkanal). Condition is True when at most one such SG29 line item is present per message. (medium confidence)
2249    fn evaluate_2062(&self, ctx: &EvaluationContext) -> ConditionResult {
2250        let count = ctx
2251            .find_segments("LIN")
2252            .iter()
2253            .filter(|s| {
2254                s.elements
2255                    .get(1)
2256                    .and_then(|e| e.first())
2257                    .is_some_and(|v| v == "Z66")
2258            })
2259            .count();
2260        ConditionResult::from(count <= 1)
2261    }
2262
2263    /// [2063] Pro Nachricht ist die SG29 LIN++Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) maximal einmal anzugeben
2264    // REVIEW: Count LIN segments where elements[1][0]=="Z67" (LIN++Z67, Messprodukt Typ 2 Backend). Condition is True when at most one such SG29 line item is present per message. (medium confidence)
2265    fn evaluate_2063(&self, ctx: &EvaluationContext) -> ConditionResult {
2266        let count = ctx
2267            .find_segments("LIN")
2268            .iter()
2269            .filter(|s| {
2270                s.elements
2271                    .get(1)
2272                    .and_then(|e| e.first())
2273                    .is_some_and(|v| v == "Z67")
2274            })
2275            .count();
2276        ConditionResult::from(count <= 1)
2277    }
2278
2279    /// [2064] Pro Nachricht ist die SG29 LIN++Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) maximal einmal anzugeben
2280    // REVIEW: Count LIN segments where elements[1][0]=="Z68" (LIN++Z68, Konfigurationserlaubnis Typ 2 SMGW). Condition is True when at most one such SG29 line item is present per message. (medium confidence)
2281    fn evaluate_2064(&self, ctx: &EvaluationContext) -> ConditionResult {
2282        let count = ctx
2283            .find_segments("LIN")
2284            .iter()
2285            .filter(|s| {
2286                s.elements
2287                    .get(1)
2288                    .and_then(|e| e.first())
2289                    .is_some_and(|v| v == "Z68")
2290            })
2291            .count();
2292        ConditionResult::from(count <= 1)
2293    }
2294
2295    /// [2066] Die SG3 RFF+Z37 Referenz auf ID der Technischen Ressource ist so oft zu wiederholen, bis alle IDs der Technischen Ressourcen angegeben sind, die der Steuerbaren Ressource in LOC+172 DE3225 (Meldepu...
2296    fn evaluate_2066(&self, _ctx: &EvaluationContext) -> ConditionResult {
2297        // Hinweis: Die SG3 RFF+Z37 ist so oft zu wiederholen, bis alle IDs der Technischen
2298        // Ressourcen angegeben sind, die der Steuerbaren Ressource in LOC+172 DE3225 (Meldepunkt)
2299        // mit diesem Vorgang zugeordnet werden sollen.
2300        // Informational cardinality note — always applies.
2301        ConditionResult::True
2302    }
2303
2304    /// [2088] Die SG29 ist so oft zu wiederholen, dass alle Messprodukte ab dem DTM+203 (Ausführungsdatum) zu der in der SG2 genannten Netzlokation genannt sind.
2305    fn evaluate_2088(&self, _ctx: &EvaluationContext) -> ConditionResult {
2306        // Hinweis: Die SG29 ist so oft zu wiederholen, dass alle Messprodukte ab dem DTM+203
2307        // (Ausführungsdatum) zu der in der SG2 genannten Netzlokation genannt sind.
2308        // Informational cardinality note — always applies.
2309        ConditionResult::True
2310    }
2311
2312    /// [2089] Die SG29 ist so oft zu wiederholen, dass alle Messprodukte ab dem DTM+203 (Ausführungsdatum) zu der in der SG2 genannten Marktlokation genannt sind.
2313    fn evaluate_2089(&self, _ctx: &EvaluationContext) -> ConditionResult {
2314        // Hinweis: Die SG29 ist so oft zu wiederholen, dass alle Messprodukte ab dem DTM+203
2315        // (Ausführungsdatum) zu der in der SG2 genannten Marktlokation genannt sind.
2316        // Informational cardinality note about SG29 repetition count — always applies.
2317        ConditionResult::True
2318    }
2319
2320    /// [2090] Für 33-stellige ID im SG2 LOC+172 (Meldepunkt) DE3225 mindestens einmal anzugeben
2321    fn evaluate_2090(&self, ctx: &EvaluationContext) -> ConditionResult {
2322        let locs = ctx.find_segments_with_qualifier("LOC", 0, "172");
2323        ConditionResult::from(locs.iter().any(|s| {
2324            s.elements
2325                .get(1)
2326                .and_then(|e| e.first())
2327                .is_some_and(|v| v.len() == 33)
2328        }))
2329    }
2330
2331    /// [2092] Pro Nachricht ist die SG29 maximal einmal anzugeben
2332    // REVIEW: SG29 is initiated by the LIN segment. Count all LIN segments in the message and verify there is at most one, making the condition True when the cardinality constraint is satisfied. (medium confidence)
2333    fn evaluate_2092(&self, ctx: &EvaluationContext) -> ConditionResult {
2334        ConditionResult::from(ctx.find_segments("LIN").len() <= 1)
2335    }
2336
2337    /// [2094] Pro Nachricht ist die SG29 LIN (Positionsdaten) so oft anzugeben, wie Positionen aus dem Angebot, welches in SG1 RFF+AAG (Referenz Nachrichtennummer), DE1154 angegeben ist, bestellt werden sollen.
2338    fn evaluate_2094(&self, _ctx: &EvaluationContext) -> ConditionResult {
2339        // Hinweis: Pro Nachricht ist die SG29 LIN (Positionsdaten) so oft anzugeben, wie
2340        // Positionen aus dem Angebot (referenziert in SG1 RFF+AAG DE1154) bestellt werden sollen.
2341        // Informational cardinality note — the required repetition count depends on an external
2342        // offer document that cannot be inspected from within this EDIFACT message.
2343        ConditionResult::True
2344    }
2345
2346    /// [2095] Diese SG29 ist so oft zu wiederholen, dass alle Produkte zur Lokation die ab dem DTM+203 (Ausführungsdatum) gewünscht sind und deren Kombination gemäß Codeliste der Konfigurationen Kapitel 7 "P...
2347    fn evaluate_2095(&self, _ctx: &EvaluationContext) -> ConditionResult {
2348        // Hinweis: Diese SG29 ist so oft zu wiederholen, dass alle Produkte zur Lokation
2349        // ab dem DTM+203 (Ausführungsdatum) genannt sind, deren Kombination gemäß
2350        // Codeliste der Konfigurationen Kapitel 7 möglich sind.
2351        // Informational cardinality note — valid combinations are defined by an external
2352        // code list (Kapitel 7) and cannot be validated from EDIFACT content alone.
2353        ConditionResult::True
2354    }
2355
2356    /// [2096] Die SG3 Referenz auf die ID der Tranche ist so oft zu wiederholen, wie ab dem DTM+203 (Ausführungsdatum) Tranchen zu der in der SG2 genannten Marktlokation vorhanden sind.
2357    fn evaluate_2096(&self, _ctx: &EvaluationContext) -> ConditionResult {
2358        // Hinweis: Die SG3 Referenz auf die ID der Tranche ist so oft zu wiederholen, wie
2359        // ab dem DTM+203 (Ausführungsdatum) Tranchen zu der in der SG2 genannten
2360        // Marktlokation vorhanden sind.
2361        // Informational cardinality note — the number of Tranchen for a Marktlokation is
2362        // business context that cannot be determined from the EDIFACT message alone.
2363        ConditionResult::True
2364    }
2365
2366    /// [2097] Die SG29 ist so oft zu wiederholen, dass für alle in SG3 Referenz auf ID der Tranche genannten Tranchen mindestens eine SG29 vorhanden ist.
2367    fn evaluate_2097(&self, _ctx: &EvaluationContext) -> ConditionResult {
2368        // Hinweis: Die SG29 ist so oft zu wiederholen, dass für alle in SG3 RFF+Z20
2369        // (Referenz auf ID der Tranche) genannten Tranchen mindestens eine SG29 vorhanden ist.
2370        // Informational completeness note — each Tranche referenced in SG3 must have
2371        // at least one corresponding SG29. Documents structural pairing requirements.
2372        ConditionResult::True
2373    }
2374
2375    /// [2098] Die SG2 Lieferant an der Lokation ist so oft zu wiederholen, dass für alle in SG3 Referenz auf ID der Tranche genannten Tranchen genau eine SG2 Lieferant der Lokation vorhanden ist.
2376    // REVIEW: Cardinality rule: SG2 repetition count must equal the number of SG3 tranche references. Implemented by comparing count_in_group for both groups within SG29. Medium confidence because exact group paths and SG3 RFF qualifier for 'Tranche ID' are not confirmed without the full MIG segment structure reference — the paths &["SG29", "SG2"] and &["SG29", "SG3"] are based on standard ORDERS tx_group conventions from project memory. (medium confidence)
2377    fn evaluate_2098(&self, ctx: &EvaluationContext) -> ConditionResult {
2378        // SG2 Lieferant an der Lokation must repeat once per SG3 tranche reference.
2379        // Count SG2 group instances (NAD as entry segment) and SG3 RFF tranche references.
2380        // Both counts must be equal and non-zero for the condition to be satisfied.
2381        let sg2_count = ctx.count_in_group("NAD", &["SG29", "SG2"]);
2382        let sg3_count = ctx.count_in_group("RFF", &["SG29", "SG3"]);
2383        if sg2_count == 0 || sg3_count == 0 {
2384            return ConditionResult::Unknown;
2385        }
2386        ConditionResult::from(sg2_count == sg3_count)
2387    }
2388
2389    /// [2099] Die SG2 Lieferant an der Lokation ist genau einmal anzugeben.
2390    // REVIEW: Simple cardinality check: SG2 must appear exactly once (genau einmal). Implemented using count_in_group on NAD (the entry segment of SG2) within SG29. Returns Unknown when no SG2 is found at all (not yet present), False when count > 1, True when exactly 1. Medium confidence due to unconfirmed group path without the full EDIFACT Segment Structure Reference for this PID. (medium confidence)
2391    fn evaluate_2099(&self, ctx: &EvaluationContext) -> ConditionResult {
2392        // SG2 Lieferant an der Lokation must appear exactly once.
2393        // Count NAD segments (entry segment of SG2) within the SG29 SG2 group path.
2394        let count = ctx.count_in_group("NAD", &["SG29", "SG2"]);
2395        if count == 0 {
2396            return ConditionResult::Unknown;
2397        }
2398        ConditionResult::from(count == 1)
2399    }
2400}
2401
2402/// The SG34 RFF+Z09 (Gerätenummer) of the SG29 LIN being validated; of the
2403/// message where no SG29 is resolved.
2404fn geraetenummern_in_this_lin(ctx: &EvaluationContext) -> usize {
2405    let is_z09 = |s: &mig_types::segment::OwnedSegment| {
2406        s.elements
2407            .first()
2408            .and_then(|e| e.first())
2409            .is_some_and(|q| q == "Z09")
2410    };
2411    match ctx.enclosing_scope("SG29") {
2412        Some(scope) => ctx
2413            .find_segments_at(scope, "RFF")
2414            .iter()
2415            .filter(|s| is_z09(s))
2416            .count(),
2417        None => ctx
2418            .find_segments("RFF")
2419            .into_iter()
2420            .filter(|s| is_z09(s))
2421            .count(),
2422    }
2423}