Skip to main content

automapper_validation/generated/fv2504/
utilmd_strom_conditions_fv2504.rs

1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2504/UTILMD_AHB_Strom_2_1_Fehlerkorrektur_20250623.xml
4// Generated: 2026-03-12T10:15:42Z
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::{Cluster, ConditionEvaluator, ConditionResult, EvaluationContext};
15// HAND-EDITED: shared cluster helpers for conditions [359]/[360]/[366]/[368].
16// Preserve across regeneration — see CLAUDE.md "Codelist Providers".
17use crate::eval::ebd_cluster::{all_sts_e01_in_cluster, ebd_ablehnung_except};
18
19/// Generated condition evaluator for UTILMD_Strom FV2504.
20pub struct UtilmdStromConditionEvaluatorFV2504 {
21    // External condition IDs that require runtime context.
22    external_conditions: std::collections::HashSet<u32>,
23}
24
25impl Default for UtilmdStromConditionEvaluatorFV2504 {
26    fn default() -> Self {
27        let mut external_conditions = std::collections::HashSet::new();
28        external_conditions.insert(1);
29        external_conditions.insert(4);
30        external_conditions.insert(5);
31        external_conditions.insert(6);
32        external_conditions.insert(8);
33        external_conditions.insert(9);
34        external_conditions.insert(14);
35        external_conditions.insert(31);
36        external_conditions.insert(33);
37        external_conditions.insert(36);
38        external_conditions.insert(38);
39        external_conditions.insert(39);
40        external_conditions.insert(40);
41        external_conditions.insert(75);
42        external_conditions.insert(98);
43        external_conditions.insert(99);
44        external_conditions.insert(151);
45        external_conditions.insert(233);
46        external_conditions.insert(241);
47        external_conditions.insert(251);
48        external_conditions.insert(292);
49        external_conditions.insert(314);
50        external_conditions.insert(315);
51        external_conditions.insert(323);
52        external_conditions.insert(412);
53        external_conditions.insert(425);
54        external_conditions.insert(426);
55        external_conditions.insert(427);
56        external_conditions.insert(428);
57        external_conditions.insert(429);
58        external_conditions.insert(433);
59        external_conditions.insert(435);
60        external_conditions.insert(453);
61        external_conditions.insert(459);
62        external_conditions.insert(460);
63        external_conditions.insert(706);
64        external_conditions.insert(717);
65        external_conditions.insert(2011);
66        Self {
67            external_conditions,
68        }
69    }
70}
71
72impl ConditionEvaluator for UtilmdStromConditionEvaluatorFV2504 {
73    fn message_type(&self) -> &str {
74        "UTILMD_Strom"
75    }
76
77    fn format_version(&self) -> &str {
78        "FV2504"
79    }
80
81    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
82        match condition {
83            1 => self.evaluate_1(ctx),
84            4 => self.evaluate_4(ctx),
85            5 => self.evaluate_5(ctx),
86            6 => self.evaluate_6(ctx),
87            7 => self.evaluate_7(ctx),
88            8 => self.evaluate_8(ctx),
89            9 => self.evaluate_9(ctx),
90            10 => self.evaluate_10(ctx),
91            11 => self.evaluate_11(ctx),
92            12 => self.evaluate_12(ctx),
93            13 => self.evaluate_13(ctx),
94            14 => self.evaluate_14(ctx),
95            15 => self.evaluate_15(ctx),
96            16 => self.evaluate_16(ctx),
97            17 => self.evaluate_17(ctx),
98            18 => self.evaluate_18(ctx),
99            19 => self.evaluate_19(ctx),
100            20 => self.evaluate_20(ctx),
101            21 => self.evaluate_21(ctx),
102            22 => self.evaluate_22(ctx),
103            23 => self.evaluate_23(ctx),
104            24 => self.evaluate_24(ctx),
105            25 => self.evaluate_25(ctx),
106            27 => self.evaluate_27(ctx),
107            30 => self.evaluate_30(ctx),
108            31 => self.evaluate_31(ctx),
109            32 => self.evaluate_32(ctx),
110            33 => self.evaluate_33(ctx),
111            34 => self.evaluate_34(ctx),
112            35 => self.evaluate_35(ctx),
113            36 => self.evaluate_36(ctx),
114            37 => self.evaluate_37(ctx),
115            38 => self.evaluate_38(ctx),
116            39 => self.evaluate_39(ctx),
117            40 => self.evaluate_40(ctx),
118            41 => self.evaluate_41(ctx),
119            42 => self.evaluate_42(ctx),
120            43 => self.evaluate_43(ctx),
121            44 => self.evaluate_44(ctx),
122            45 => self.evaluate_45(ctx),
123            46 => self.evaluate_46(ctx),
124            47 => self.evaluate_47(ctx),
125            48 => self.evaluate_48(ctx),
126            49 => self.evaluate_49(ctx),
127            50 => self.evaluate_50(ctx),
128            51 => self.evaluate_51(ctx),
129            52 => self.evaluate_52(ctx),
130            53 => self.evaluate_53(ctx),
131            54 => self.evaluate_54(ctx),
132            55 => self.evaluate_55(ctx),
133            56 => self.evaluate_56(ctx),
134            57 => self.evaluate_57(ctx),
135            58 => self.evaluate_58(ctx),
136            60 => self.evaluate_60(ctx),
137            61 => self.evaluate_61(ctx),
138            62 => self.evaluate_62(ctx),
139            63 => self.evaluate_63(ctx),
140            64 => self.evaluate_64(ctx),
141            65 => self.evaluate_65(ctx),
142            66 => self.evaluate_66(ctx),
143            67 => self.evaluate_67(ctx),
144            68 => self.evaluate_68(ctx),
145            69 => self.evaluate_69(ctx),
146            70 => self.evaluate_70(ctx),
147            71 => self.evaluate_71(ctx),
148            72 => self.evaluate_72(ctx),
149            73 => self.evaluate_73(ctx),
150            74 => self.evaluate_74(ctx),
151            75 => self.evaluate_75(ctx),
152            76 => self.evaluate_76(ctx),
153            77 => self.evaluate_77(ctx),
154            78 => self.evaluate_78(ctx),
155            83 => self.evaluate_83(ctx),
156            84 => self.evaluate_84(ctx),
157            85 => self.evaluate_85(ctx),
158            86 => self.evaluate_86(ctx),
159            87 => self.evaluate_87(ctx),
160            88 => self.evaluate_88(ctx),
161            89 => self.evaluate_89(ctx),
162            90 => self.evaluate_90(ctx),
163            91 => self.evaluate_91(ctx),
164            92 => self.evaluate_92(ctx),
165            93 => self.evaluate_93(ctx),
166            94 => self.evaluate_94(ctx),
167            95 => self.evaluate_95(ctx),
168            96 => self.evaluate_96(ctx),
169            97 => self.evaluate_97(ctx),
170            98 => self.evaluate_98(ctx),
171            99 => self.evaluate_99(ctx),
172            100 => self.evaluate_100(ctx),
173            101 => self.evaluate_101(ctx),
174            102 => self.evaluate_102(ctx),
175            103 => self.evaluate_103(ctx),
176            104 => self.evaluate_104(ctx),
177            105 => self.evaluate_105(ctx),
178            106 => self.evaluate_106(ctx),
179            107 => self.evaluate_107(ctx),
180            108 => self.evaluate_108(ctx),
181            110 => self.evaluate_110(ctx),
182            111 => self.evaluate_111(ctx),
183            112 => self.evaluate_112(ctx),
184            113 => self.evaluate_113(ctx),
185            114 => self.evaluate_114(ctx),
186            115 => self.evaluate_115(ctx),
187            116 => self.evaluate_116(ctx),
188            117 => self.evaluate_117(ctx),
189            118 => self.evaluate_118(ctx),
190            119 => self.evaluate_119(ctx),
191            120 => self.evaluate_120(ctx),
192            121 => self.evaluate_121(ctx),
193            122 => self.evaluate_122(ctx),
194            123 => self.evaluate_123(ctx),
195            124 => self.evaluate_124(ctx),
196            125 => self.evaluate_125(ctx),
197            126 => self.evaluate_126(ctx),
198            127 => self.evaluate_127(ctx),
199            128 => self.evaluate_128(ctx),
200            129 => self.evaluate_129(ctx),
201            130 => self.evaluate_130(ctx),
202            131 => self.evaluate_131(ctx),
203            132 => self.evaluate_132(ctx),
204            133 => self.evaluate_133(ctx),
205            134 => self.evaluate_134(ctx),
206            135 => self.evaluate_135(ctx),
207            136 => self.evaluate_136(ctx),
208            137 => self.evaluate_137(ctx),
209            138 => self.evaluate_138(ctx),
210            139 => self.evaluate_139(ctx),
211            140 => self.evaluate_140(ctx),
212            141 => self.evaluate_141(ctx),
213            142 => self.evaluate_142(ctx),
214            143 => self.evaluate_143(ctx),
215            144 => self.evaluate_144(ctx),
216            145 => self.evaluate_145(ctx),
217            146 => self.evaluate_146(ctx),
218            147 => self.evaluate_147(ctx),
219            148 => self.evaluate_148(ctx),
220            149 => self.evaluate_149(ctx),
221            150 => self.evaluate_150(ctx),
222            151 => self.evaluate_151(ctx),
223            152 => self.evaluate_152(ctx),
224            153 => self.evaluate_153(ctx),
225            156 => self.evaluate_156(ctx),
226            161 => self.evaluate_161(ctx),
227            162 => self.evaluate_162(ctx),
228            163 => self.evaluate_163(ctx),
229            164 => self.evaluate_164(ctx),
230            165 => self.evaluate_165(ctx),
231            166 => self.evaluate_166(ctx),
232            167 => self.evaluate_167(ctx),
233            170 => self.evaluate_170(ctx),
234            172 => self.evaluate_172(ctx),
235            173 => self.evaluate_173(ctx),
236            174 => self.evaluate_174(ctx),
237            175 => self.evaluate_175(ctx),
238            176 => self.evaluate_176(ctx),
239            177 => self.evaluate_177(ctx),
240            178 => self.evaluate_178(ctx),
241            179 => self.evaluate_179(ctx),
242            180 => self.evaluate_180(ctx),
243            184 => self.evaluate_184(ctx),
244            190 => self.evaluate_190(ctx),
245            191 => self.evaluate_191(ctx),
246            192 => self.evaluate_192(ctx),
247            193 => self.evaluate_193(ctx),
248            194 => self.evaluate_194(ctx),
249            195 => self.evaluate_195(ctx),
250            196 => self.evaluate_196(ctx),
251            197 => self.evaluate_197(ctx),
252            198 => self.evaluate_198(ctx),
253            199 => self.evaluate_199(ctx),
254            201 => self.evaluate_201(ctx),
255            202 => self.evaluate_202(ctx),
256            203 => self.evaluate_203(ctx),
257            204 => self.evaluate_204(ctx),
258            205 => self.evaluate_205(ctx),
259            206 => self.evaluate_206(ctx),
260            209 => self.evaluate_209(ctx),
261            210 => self.evaluate_210(ctx),
262            212 => self.evaluate_212(ctx),
263            213 => self.evaluate_213(ctx),
264            215 => self.evaluate_215(ctx),
265            216 => self.evaluate_216(ctx),
266            217 => self.evaluate_217(ctx),
267            219 => self.evaluate_219(ctx),
268            220 => self.evaluate_220(ctx),
269            221 => self.evaluate_221(ctx),
270            223 => self.evaluate_223(ctx),
271            224 => self.evaluate_224(ctx),
272            227 => self.evaluate_227(ctx),
273            229 => self.evaluate_229(ctx),
274            232 => self.evaluate_232(ctx),
275            233 => self.evaluate_233(ctx),
276            234 => self.evaluate_234(ctx),
277            237 => self.evaluate_237(ctx),
278            238 => self.evaluate_238(ctx),
279            239 => self.evaluate_239(ctx),
280            240 => self.evaluate_240(ctx),
281            241 => self.evaluate_241(ctx),
282            243 => self.evaluate_243(ctx),
283            244 => self.evaluate_244(ctx),
284            248 => self.evaluate_248(ctx),
285            249 => self.evaluate_249(ctx),
286            251 => self.evaluate_251(ctx),
287            252 => self.evaluate_252(ctx),
288            254 => self.evaluate_254(ctx),
289            255 => self.evaluate_255(ctx),
290            256 => self.evaluate_256(ctx),
291            257 => self.evaluate_257(ctx),
292            259 => self.evaluate_259(ctx),
293            261 => self.evaluate_261(ctx),
294            262 => self.evaluate_262(ctx),
295            265 => self.evaluate_265(ctx),
296            266 => self.evaluate_266(ctx),
297            267 => self.evaluate_267(ctx),
298            268 => self.evaluate_268(ctx),
299            269 => self.evaluate_269(ctx),
300            270 => self.evaluate_270(ctx),
301            273 => self.evaluate_273(ctx),
302            279 => self.evaluate_279(ctx),
303            280 => self.evaluate_280(ctx),
304            282 => self.evaluate_282(ctx),
305            284 => self.evaluate_284(ctx),
306            285 => self.evaluate_285(ctx),
307            286 => self.evaluate_286(ctx),
308            287 => self.evaluate_287(ctx),
309            288 => self.evaluate_288(ctx),
310            291 => self.evaluate_291(ctx),
311            292 => self.evaluate_292(ctx),
312            293 => self.evaluate_293(ctx),
313            294 => self.evaluate_294(ctx),
314            295 => self.evaluate_295(ctx),
315            296 => self.evaluate_296(ctx),
316            297 => self.evaluate_297(ctx),
317            298 => self.evaluate_298(ctx),
318            299 => self.evaluate_299(ctx),
319            300 => self.evaluate_300(ctx),
320            301 => self.evaluate_301(ctx),
321            302 => self.evaluate_302(ctx),
322            303 => self.evaluate_303(ctx),
323            304 => self.evaluate_304(ctx),
324            305 => self.evaluate_305(ctx),
325            306 => self.evaluate_306(ctx),
326            307 => self.evaluate_307(ctx),
327            309 => self.evaluate_309(ctx),
328            314 => self.evaluate_314(ctx),
329            315 => self.evaluate_315(ctx),
330            316 => self.evaluate_316(ctx),
331            317 => self.evaluate_317(ctx),
332            318 => self.evaluate_318(ctx),
333            321 => self.evaluate_321(ctx),
334            322 => self.evaluate_322(ctx),
335            323 => self.evaluate_323(ctx),
336            327 => self.evaluate_327(ctx),
337            329 => self.evaluate_329(ctx),
338            331 => self.evaluate_331(ctx),
339            332 => self.evaluate_332(ctx),
340            333 => self.evaluate_333(ctx),
341            334 => self.evaluate_334(ctx),
342            335 => self.evaluate_335(ctx),
343            336 => self.evaluate_336(ctx),
344            337 => self.evaluate_337(ctx),
345            338 => self.evaluate_338(ctx),
346            339 => self.evaluate_339(ctx),
347            340 => self.evaluate_340(ctx),
348            341 => self.evaluate_341(ctx),
349            342 => self.evaluate_342(ctx),
350            344 => self.evaluate_344(ctx),
351            345 => self.evaluate_345(ctx),
352            346 => self.evaluate_346(ctx),
353            347 => self.evaluate_347(ctx),
354            348 => self.evaluate_348(ctx),
355            349 => self.evaluate_349(ctx),
356            350 => self.evaluate_350(ctx),
357            351 => self.evaluate_351(ctx),
358            352 => self.evaluate_352(ctx),
359            355 => self.evaluate_355(ctx),
360            356 => self.evaluate_356(ctx),
361            357 => self.evaluate_357(ctx),
362            358 => self.evaluate_358(ctx),
363            359 => self.evaluate_359(ctx),
364            360 => self.evaluate_360(ctx),
365            363 => self.evaluate_363(ctx),
366            365 => self.evaluate_365(ctx),
367            366 => self.evaluate_366(ctx),
368            367 => self.evaluate_367(ctx),
369            368 => self.evaluate_368(ctx),
370            370 => self.evaluate_370(ctx),
371            371 => self.evaluate_371(ctx),
372            372 => self.evaluate_372(ctx),
373            373 => self.evaluate_373(ctx),
374            375 => self.evaluate_375(ctx),
375            376 => self.evaluate_376(ctx),
376            377 => self.evaluate_377(ctx),
377            378 => self.evaluate_378(ctx),
378            379 => self.evaluate_379(ctx),
379            380 => self.evaluate_380(ctx),
380            384 => self.evaluate_384(ctx),
381            386 => self.evaluate_386(ctx),
382            387 => self.evaluate_387(ctx),
383            388 => self.evaluate_388(ctx),
384            391 => self.evaluate_391(ctx),
385            392 => self.evaluate_392(ctx),
386            393 => self.evaluate_393(ctx),
387            394 => self.evaluate_394(ctx),
388            395 => self.evaluate_395(ctx),
389            396 => self.evaluate_396(ctx),
390            397 => self.evaluate_397(ctx),
391            398 => self.evaluate_398(ctx),
392            399 => self.evaluate_399(ctx),
393            401 => self.evaluate_401(ctx),
394            402 => self.evaluate_402(ctx),
395            403 => self.evaluate_403(ctx),
396            404 => self.evaluate_404(ctx),
397            405 => self.evaluate_405(ctx),
398            406 => self.evaluate_406(ctx),
399            407 => self.evaluate_407(ctx),
400            408 => self.evaluate_408(ctx),
401            409 => self.evaluate_409(ctx),
402            410 => self.evaluate_410(ctx),
403            411 => self.evaluate_411(ctx),
404            412 => self.evaluate_412(ctx),
405            413 => self.evaluate_413(ctx),
406            414 => self.evaluate_414(ctx),
407            415 => self.evaluate_415(ctx),
408            416 => self.evaluate_416(ctx),
409            417 => self.evaluate_417(ctx),
410            419 => self.evaluate_419(ctx),
411            420 => self.evaluate_420(ctx),
412            421 => self.evaluate_421(ctx),
413            425 => self.evaluate_425(ctx),
414            426 => self.evaluate_426(ctx),
415            427 => self.evaluate_427(ctx),
416            428 => self.evaluate_428(ctx),
417            429 => self.evaluate_429(ctx),
418            430 => self.evaluate_430(ctx),
419            431 => self.evaluate_431(ctx),
420            432 => self.evaluate_432(ctx),
421            433 => self.evaluate_433(ctx),
422            435 => self.evaluate_435(ctx),
423            436 => self.evaluate_436(ctx),
424            437 => self.evaluate_437(ctx),
425            438 => self.evaluate_438(ctx),
426            440 => self.evaluate_440(ctx),
427            441 => self.evaluate_441(ctx),
428            442 => self.evaluate_442(ctx),
429            444 => self.evaluate_444(ctx),
430            445 => self.evaluate_445(ctx),
431            446 => self.evaluate_446(ctx),
432            447 => self.evaluate_447(ctx),
433            448 => self.evaluate_448(ctx),
434            449 => self.evaluate_449(ctx),
435            450 => self.evaluate_450(ctx),
436            451 => self.evaluate_451(ctx),
437            452 => self.evaluate_452(ctx),
438            453 => self.evaluate_453(ctx),
439            454 => self.evaluate_454(ctx),
440            455 => self.evaluate_455(ctx),
441            456 => self.evaluate_456(ctx),
442            457 => self.evaluate_457(ctx),
443            458 => self.evaluate_458(ctx),
444            459 => self.evaluate_459(ctx),
445            460 => self.evaluate_460(ctx),
446            461 => self.evaluate_461(ctx),
447            462 => self.evaluate_462(ctx),
448            463 => self.evaluate_463(ctx),
449            465 => self.evaluate_465(ctx),
450            466 => self.evaluate_466(ctx),
451            467 => self.evaluate_467(ctx),
452            468 => self.evaluate_468(ctx),
453            469 => self.evaluate_469(ctx),
454            470 => self.evaluate_470(ctx),
455            471 => self.evaluate_471(ctx),
456            472 => self.evaluate_472(ctx),
457            473 => self.evaluate_473(ctx),
458            474 => self.evaluate_474(ctx),
459            475 => self.evaluate_475(ctx),
460            476 => self.evaluate_476(ctx),
461            477 => self.evaluate_477(ctx),
462            478 => self.evaluate_478(ctx),
463            479 => self.evaluate_479(ctx),
464            480 => self.evaluate_480(ctx),
465            481 => self.evaluate_481(ctx),
466            483 => self.evaluate_483(ctx),
467            484 => self.evaluate_484(ctx),
468            487 => self.evaluate_487(ctx),
469            489 => self.evaluate_489(ctx),
470            490 => self.evaluate_490(ctx),
471            491 => self.evaluate_491(ctx),
472            494 => self.evaluate_494(ctx),
473            500 => self.evaluate_500(ctx),
474            501 => self.evaluate_501(ctx),
475            502 => self.evaluate_502(ctx),
476            503 => self.evaluate_503(ctx),
477            504 => self.evaluate_504(ctx),
478            505 => self.evaluate_505(ctx),
479            506 => self.evaluate_506(ctx),
480            507 => self.evaluate_507(ctx),
481            508 => self.evaluate_508(ctx),
482            509 => self.evaluate_509(ctx),
483            510 => self.evaluate_510(ctx),
484            511 => self.evaluate_511(ctx),
485            512 => self.evaluate_512(ctx),
486            513 => self.evaluate_513(ctx),
487            514 => self.evaluate_514(ctx),
488            515 => self.evaluate_515(ctx),
489            516 => self.evaluate_516(ctx),
490            517 => self.evaluate_517(ctx),
491            518 => self.evaluate_518(ctx),
492            519 => self.evaluate_519(ctx),
493            520 => self.evaluate_520(ctx),
494            521 => self.evaluate_521(ctx),
495            522 => self.evaluate_522(ctx),
496            523 => self.evaluate_523(ctx),
497            524 => self.evaluate_524(ctx),
498            525 => self.evaluate_525(ctx),
499            526 => self.evaluate_526(ctx),
500            527 => self.evaluate_527(ctx),
501            528 => self.evaluate_528(ctx),
502            529 => self.evaluate_529(ctx),
503            530 => self.evaluate_530(ctx),
504            531 => self.evaluate_531(ctx),
505            532 => self.evaluate_532(ctx),
506            533 => self.evaluate_533(ctx),
507            555 => self.evaluate_555(ctx),
508            556 => self.evaluate_556(ctx),
509            558 => self.evaluate_558(ctx),
510            559 => self.evaluate_559(ctx),
511            563 => self.evaluate_563(ctx),
512            566 => self.evaluate_566(ctx),
513            567 => self.evaluate_567(ctx),
514            568 => self.evaluate_568(ctx),
515            569 => self.evaluate_569(ctx),
516            570 => self.evaluate_570(ctx),
517            572 => self.evaluate_572(ctx),
518            576 => self.evaluate_576(ctx),
519            577 => self.evaluate_577(ctx),
520            579 => self.evaluate_579(ctx),
521            580 => self.evaluate_580(ctx),
522            581 => self.evaluate_581(ctx),
523            586 => self.evaluate_586(ctx),
524            590 => self.evaluate_590(ctx),
525            594 => self.evaluate_594(ctx),
526            599 => self.evaluate_599(ctx),
527            601 => self.evaluate_601(ctx),
528            606 => self.evaluate_606(ctx),
529            609 => self.evaluate_609(ctx),
530            611 => self.evaluate_611(ctx),
531            614 => self.evaluate_614(ctx),
532            617 => self.evaluate_617(ctx),
533            618 => self.evaluate_618(ctx),
534            619 => self.evaluate_619(ctx),
535            621 => self.evaluate_621(ctx),
536            622 => self.evaluate_622(ctx),
537            623 => self.evaluate_623(ctx),
538            630 => self.evaluate_630(ctx),
539            631 => self.evaluate_631(ctx),
540            632 => self.evaluate_632(ctx),
541            637 => self.evaluate_637(ctx),
542            638 => self.evaluate_638(ctx),
543            639 => self.evaluate_639(ctx),
544            640 => self.evaluate_640(ctx),
545            641 => self.evaluate_641(ctx),
546            642 => self.evaluate_642(ctx),
547            643 => self.evaluate_643(ctx),
548            645 => self.evaluate_645(ctx),
549            646 => self.evaluate_646(ctx),
550            647 => self.evaluate_647(ctx),
551            648 => self.evaluate_648(ctx),
552            651 => self.evaluate_651(ctx),
553            653 => self.evaluate_653(ctx),
554            655 => self.evaluate_655(ctx),
555            659 => self.evaluate_659(ctx),
556            660 => self.evaluate_660(ctx),
557            662 => self.evaluate_662(ctx),
558            663 => self.evaluate_663(ctx),
559            664 => self.evaluate_664(ctx),
560            665 => self.evaluate_665(ctx),
561            667 => self.evaluate_667(ctx),
562            668 => self.evaluate_668(ctx),
563            670 => self.evaluate_670(ctx),
564            671 => self.evaluate_671(ctx),
565            672 => self.evaluate_672(ctx),
566            673 => self.evaluate_673(ctx),
567            674 => self.evaluate_674(ctx),
568            675 => self.evaluate_675(ctx),
569            677 => self.evaluate_677(ctx),
570            678 => self.evaluate_678(ctx),
571            679 => self.evaluate_679(ctx),
572            680 => self.evaluate_680(ctx),
573            682 => self.evaluate_682(ctx),
574            683 => self.evaluate_683(ctx),
575            684 => self.evaluate_684(ctx),
576            685 => self.evaluate_685(ctx),
577            687 => self.evaluate_687(ctx),
578            688 => self.evaluate_688(ctx),
579            689 => self.evaluate_689(ctx),
580            690 => self.evaluate_690(ctx),
581            693 => self.evaluate_693(ctx),
582            694 => self.evaluate_694(ctx),
583            695 => self.evaluate_695(ctx),
584            696 => self.evaluate_696(ctx),
585            698 => self.evaluate_698(ctx),
586            699 => self.evaluate_699(ctx),
587            700 => self.evaluate_700(ctx),
588            704 => self.evaluate_704(ctx),
589            705 => self.evaluate_705(ctx),
590            706 => self.evaluate_706(ctx),
591            707 => self.evaluate_707(ctx),
592            708 => self.evaluate_708(ctx),
593            709 => self.evaluate_709(ctx),
594            710 => self.evaluate_710(ctx),
595            711 => self.evaluate_711(ctx),
596            712 => self.evaluate_712(ctx),
597            713 => self.evaluate_713(ctx),
598            714 => self.evaluate_714(ctx),
599            715 => self.evaluate_715(ctx),
600            716 => self.evaluate_716(ctx),
601            717 => self.evaluate_717(ctx),
602            718 => self.evaluate_718(ctx),
603            719 => self.evaluate_719(ctx),
604            902 => self.evaluate_902(ctx),
605            910 => self.evaluate_910(ctx),
606            914 => self.evaluate_914(ctx),
607            922 => self.evaluate_922(ctx),
608            926 => self.evaluate_926(ctx),
609            930 => self.evaluate_930(ctx),
610            931 => self.evaluate_931(ctx),
611            932 => self.evaluate_932(ctx),
612            933 => self.evaluate_933(ctx),
613            937 => self.evaluate_937(ctx),
614            938 => self.evaluate_938(ctx),
615            939 => self.evaluate_939(ctx),
616            940 => self.evaluate_940(ctx),
617            942 => self.evaluate_942(ctx),
618            943 => self.evaluate_943(ctx),
619            946 => self.evaluate_946(ctx),
620            948 => self.evaluate_948(ctx),
621            950 => self.evaluate_950(ctx),
622            951 => self.evaluate_951(ctx),
623            952 => self.evaluate_952(ctx),
624            955 => self.evaluate_955(ctx),
625            957 => self.evaluate_957(ctx),
626            960 => self.evaluate_960(ctx),
627            961 => self.evaluate_961(ctx),
628            967 => self.evaluate_967(ctx),
629            2001 => self.evaluate_2001(ctx),
630            2002 => self.evaluate_2002(ctx),
631            2003 => self.evaluate_2003(ctx),
632            2004 => self.evaluate_2004(ctx),
633            2005 => self.evaluate_2005(ctx),
634            2006 => self.evaluate_2006(ctx),
635            2007 => self.evaluate_2007(ctx),
636            2008 => self.evaluate_2008(ctx),
637            2009 => self.evaluate_2009(ctx),
638            2010 => self.evaluate_2010(ctx),
639            2011 => self.evaluate_2011(ctx),
640            2012 => self.evaluate_2012(ctx),
641            2013 => self.evaluate_2013(ctx),
642            2014 => self.evaluate_2014(ctx),
643            2015 => self.evaluate_2015(ctx),
644            2016 => self.evaluate_2016(ctx),
645            2017 => self.evaluate_2017(ctx),
646            2018 => self.evaluate_2018(ctx),
647            2060 => self.evaluate_2060(ctx),
648            2061 => self.evaluate_2061(ctx),
649            2071 => self.evaluate_2071(ctx),
650            2073 => self.evaluate_2073(ctx),
651            2075 => self.evaluate_2075(ctx),
652            2080 => self.evaluate_2080(ctx),
653            2095 => self.evaluate_2095(ctx),
654            2096 => self.evaluate_2096(ctx),
655            2119 => self.evaluate_2119(ctx),
656            2140 => self.evaluate_2140(ctx),
657            2182 => self.evaluate_2182(ctx),
658            2183 => self.evaluate_2183(ctx),
659            2207 => self.evaluate_2207(ctx),
660            2225 => self.evaluate_2225(ctx),
661            2236 => self.evaluate_2236(ctx),
662            2252 => self.evaluate_2252(ctx),
663            2261 => self.evaluate_2261(ctx),
664            2284 => self.evaluate_2284(ctx),
665            2286 => self.evaluate_2286(ctx),
666            2287 => self.evaluate_2287(ctx),
667            2288 => self.evaluate_2288(ctx),
668            2307 => self.evaluate_2307(ctx),
669            2308 => self.evaluate_2308(ctx),
670            2309 => self.evaluate_2309(ctx),
671            2310 => self.evaluate_2310(ctx),
672            2311 => self.evaluate_2311(ctx),
673            2312 => self.evaluate_2312(ctx),
674            2313 => self.evaluate_2313(ctx),
675            2317 => self.evaluate_2317(ctx),
676            2318 => self.evaluate_2318(ctx),
677            2344 => self.evaluate_2344(ctx),
678            2350 => self.evaluate_2350(ctx),
679            2351 => self.evaluate_2351(ctx),
680            2352 => self.evaluate_2352(ctx),
681            2356 => self.evaluate_2356(ctx),
682            2357 => self.evaluate_2357(ctx),
683            2358 => self.evaluate_2358(ctx),
684            2359 => self.evaluate_2359(ctx),
685            2360 => self.evaluate_2360(ctx),
686            2361 => self.evaluate_2361(ctx),
687            _ => ConditionResult::Unknown,
688        }
689    }
690
691    fn is_external(&self, condition: u32) -> bool {
692        self.external_conditions.contains(&condition)
693    }
694    fn is_known(&self, condition: u32) -> bool {
695        matches!(
696            condition,
697            1 | 4
698                | 5
699                | 6
700                | 7
701                | 8
702                | 9
703                | 10
704                | 11
705                | 12
706                | 13
707                | 14
708                | 15
709                | 16
710                | 17
711                | 18
712                | 19
713                | 20
714                | 21
715                | 22
716                | 23
717                | 24
718                | 25
719                | 27
720                | 30
721                | 31
722                | 32
723                | 33
724                | 34
725                | 35
726                | 36
727                | 37
728                | 38
729                | 39
730                | 40
731                | 41
732                | 42
733                | 43
734                | 44
735                | 45
736                | 46
737                | 47
738                | 48
739                | 49
740                | 50
741                | 51
742                | 52
743                | 53
744                | 54
745                | 55
746                | 56
747                | 57
748                | 58
749                | 60
750                | 61
751                | 62
752                | 63
753                | 64
754                | 65
755                | 66
756                | 67
757                | 68
758                | 69
759                | 70
760                | 71
761                | 72
762                | 73
763                | 74
764                | 75
765                | 76
766                | 77
767                | 78
768                | 83
769                | 84
770                | 85
771                | 86
772                | 87
773                | 88
774                | 89
775                | 90
776                | 91
777                | 92
778                | 93
779                | 94
780                | 95
781                | 96
782                | 97
783                | 98
784                | 99
785                | 100
786                | 101
787                | 102
788                | 103
789                | 104
790                | 105
791                | 106
792                | 107
793                | 108
794                | 110
795                | 111
796                | 112
797                | 113
798                | 114
799                | 115
800                | 116
801                | 117
802                | 118
803                | 119
804                | 120
805                | 121
806                | 122
807                | 123
808                | 124
809                | 125
810                | 126
811                | 127
812                | 128
813                | 129
814                | 130
815                | 131
816                | 132
817                | 133
818                | 134
819                | 135
820                | 136
821                | 137
822                | 138
823                | 139
824                | 140
825                | 141
826                | 142
827                | 143
828                | 144
829                | 145
830                | 146
831                | 147
832                | 148
833                | 149
834                | 150
835                | 151
836                | 152
837                | 153
838                | 156
839                | 161
840                | 162
841                | 163
842                | 164
843                | 165
844                | 166
845                | 167
846                | 170
847                | 172
848                | 173
849                | 174
850                | 175
851                | 176
852                | 177
853                | 178
854                | 179
855                | 180
856                | 184
857                | 190
858                | 191
859                | 192
860                | 193
861                | 194
862                | 195
863                | 196
864                | 197
865                | 198
866                | 199
867                | 201
868                | 202
869                | 203
870                | 204
871                | 205
872                | 206
873                | 209
874                | 210
875                | 212
876                | 213
877                | 215
878                | 216
879                | 217
880                | 219
881                | 220
882                | 221
883                | 223
884                | 224
885                | 227
886                | 229
887                | 232
888                | 233
889                | 234
890                | 237
891                | 238
892                | 239
893                | 240
894                | 241
895                | 243
896                | 244
897                | 248
898                | 249
899                | 251
900                | 252
901                | 254
902                | 255
903                | 256
904                | 257
905                | 259
906                | 261
907                | 262
908                | 265
909                | 266
910                | 267
911                | 268
912                | 269
913                | 270
914                | 273
915                | 279
916                | 280
917                | 282
918                | 284
919                | 285
920                | 286
921                | 287
922                | 288
923                | 291
924                | 292
925                | 293
926                | 294
927                | 295
928                | 296
929                | 297
930                | 298
931                | 299
932                | 300
933                | 301
934                | 302
935                | 303
936                | 304
937                | 305
938                | 306
939                | 307
940                | 309
941                | 314
942                | 315
943                | 316
944                | 317
945                | 318
946                | 321
947                | 322
948                | 323
949                | 327
950                | 329
951                | 331
952                | 332
953                | 333
954                | 334
955                | 335
956                | 336
957                | 337
958                | 338
959                | 339
960                | 340
961                | 341
962                | 342
963                | 344
964                | 345
965                | 346
966                | 347
967                | 348
968                | 349
969                | 350
970                | 351
971                | 352
972                | 355
973                | 356
974                | 357
975                | 358
976                | 359
977                | 360
978                | 363
979                | 365
980                | 366
981                | 367
982                | 368
983                | 370
984                | 371
985                | 372
986                | 373
987                | 375
988                | 376
989                | 377
990                | 378
991                | 379
992                | 380
993                | 384
994                | 386
995                | 387
996                | 388
997                | 391
998                | 392
999                | 393
1000                | 394
1001                | 395
1002                | 396
1003                | 397
1004                | 398
1005                | 399
1006                | 401
1007                | 402
1008                | 403
1009                | 404
1010                | 405
1011                | 406
1012                | 407
1013                | 408
1014                | 409
1015                | 410
1016                | 411
1017                | 412
1018                | 413
1019                | 414
1020                | 415
1021                | 416
1022                | 417
1023                | 419
1024                | 420
1025                | 421
1026                | 425
1027                | 426
1028                | 427
1029                | 428
1030                | 429
1031                | 430
1032                | 431
1033                | 432
1034                | 433
1035                | 435
1036                | 436
1037                | 437
1038                | 438
1039                | 440
1040                | 441
1041                | 442
1042                | 444
1043                | 445
1044                | 446
1045                | 447
1046                | 448
1047                | 449
1048                | 450
1049                | 451
1050                | 452
1051                | 453
1052                | 454
1053                | 455
1054                | 456
1055                | 457
1056                | 458
1057                | 459
1058                | 460
1059                | 461
1060                | 462
1061                | 463
1062                | 465
1063                | 466
1064                | 467
1065                | 468
1066                | 469
1067                | 470
1068                | 471
1069                | 472
1070                | 473
1071                | 474
1072                | 475
1073                | 476
1074                | 477
1075                | 478
1076                | 479
1077                | 480
1078                | 481
1079                | 483
1080                | 484
1081                | 487
1082                | 489
1083                | 490
1084                | 491
1085                | 494
1086                | 500
1087                | 501
1088                | 502
1089                | 503
1090                | 504
1091                | 505
1092                | 506
1093                | 507
1094                | 508
1095                | 509
1096                | 510
1097                | 511
1098                | 512
1099                | 513
1100                | 514
1101                | 515
1102                | 516
1103                | 517
1104                | 518
1105                | 519
1106                | 520
1107                | 521
1108                | 522
1109                | 523
1110                | 524
1111                | 525
1112                | 526
1113                | 527
1114                | 528
1115                | 529
1116                | 530
1117                | 531
1118                | 532
1119                | 533
1120                | 555
1121                | 556
1122                | 558
1123                | 559
1124                | 563
1125                | 566
1126                | 567
1127                | 568
1128                | 569
1129                | 570
1130                | 572
1131                | 576
1132                | 577
1133                | 579
1134                | 580
1135                | 581
1136                | 586
1137                | 590
1138                | 594
1139                | 599
1140                | 601
1141                | 606
1142                | 609
1143                | 611
1144                | 614
1145                | 617
1146                | 618
1147                | 619
1148                | 621
1149                | 622
1150                | 623
1151                | 630
1152                | 631
1153                | 632
1154                | 637
1155                | 638
1156                | 639
1157                | 640
1158                | 641
1159                | 642
1160                | 643
1161                | 645
1162                | 646
1163                | 647
1164                | 648
1165                | 651
1166                | 653
1167                | 655
1168                | 659
1169                | 660
1170                | 662
1171                | 663
1172                | 664
1173                | 665
1174                | 667
1175                | 668
1176                | 670
1177                | 671
1178                | 672
1179                | 673
1180                | 674
1181                | 675
1182                | 677
1183                | 678
1184                | 679
1185                | 680
1186                | 682
1187                | 683
1188                | 684
1189                | 685
1190                | 687
1191                | 688
1192                | 689
1193                | 690
1194                | 693
1195                | 694
1196                | 695
1197                | 696
1198                | 698
1199                | 699
1200                | 700
1201                | 704
1202                | 705
1203                | 706
1204                | 707
1205                | 708
1206                | 709
1207                | 710
1208                | 711
1209                | 712
1210                | 713
1211                | 714
1212                | 715
1213                | 716
1214                | 717
1215                | 718
1216                | 719
1217                | 902
1218                | 910
1219                | 914
1220                | 922
1221                | 926
1222                | 930
1223                | 931
1224                | 932
1225                | 933
1226                | 937
1227                | 938
1228                | 939
1229                | 940
1230                | 942
1231                | 943
1232                | 946
1233                | 948
1234                | 950
1235                | 951
1236                | 952
1237                | 955
1238                | 957
1239                | 960
1240                | 961
1241                | 967
1242                | 2001
1243                | 2002
1244                | 2003
1245                | 2004
1246                | 2005
1247                | 2006
1248                | 2007
1249                | 2008
1250                | 2009
1251                | 2010
1252                | 2011
1253                | 2012
1254                | 2013
1255                | 2014
1256                | 2015
1257                | 2016
1258                | 2017
1259                | 2018
1260                | 2060
1261                | 2061
1262                | 2071
1263                | 2073
1264                | 2075
1265                | 2080
1266                | 2095
1267                | 2096
1268                | 2119
1269                | 2140
1270                | 2182
1271                | 2183
1272                | 2207
1273                | 2225
1274                | 2236
1275                | 2252
1276                | 2261
1277                | 2284
1278                | 2286
1279                | 2287
1280                | 2288
1281                | 2307
1282                | 2308
1283                | 2309
1284                | 2310
1285                | 2311
1286                | 2312
1287                | 2313
1288                | 2317
1289                | 2318
1290                | 2344
1291                | 2350
1292                | 2351
1293                | 2352
1294                | 2356
1295                | 2357
1296                | 2358
1297                | 2359
1298                | 2360
1299                | 2361
1300        )
1301    }
1302}
1303
1304impl UtilmdStromConditionEvaluatorFV2504 {
1305    /// [2006] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingung [199] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolge...
1306    // REVIEW: Counts SG8 instances with SEQ+Z01 where condition [199] is fulfilled and collects their Zeitraum-IDs from SEQ elements[1][0]. Returns True if at least one qualifying parent exists (cardinality enforcement is partial — full 'exactly once' check requires knowing the current SG type). The recursive call self.evaluate(199, ctx) operates at message scope, which is the best available approximation. (medium confidence)
1307    fn evaluate_2006(&self, ctx: &EvaluationContext) -> ConditionResult {
1308        let nav = match ctx.navigator() {
1309            Some(n) => n,
1310            None => return ConditionResult::Unknown,
1311        };
1312        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1313        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1314        for i in 0..sg8_count {
1315            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1316            let is_z01 = seq_segs.iter().any(|s| {
1317                s.elements
1318                    .first()
1319                    .and_then(|e| e.first())
1320                    .is_some_and(|v| v == "Z01")
1321            });
1322            if is_z01 && matches!(self.evaluate(199, ctx), ConditionResult::True) {
1323                for seq in &seq_segs {
1324                    if seq
1325                        .elements
1326                        .first()
1327                        .and_then(|e| e.first())
1328                        .is_some_and(|v| v == "Z01")
1329                    {
1330                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1331                            if !zid.is_empty() {
1332                                qualifying_zeitraum_ids.push(zid.clone());
1333                            }
1334                        }
1335                    }
1336                }
1337            }
1338        }
1339        if qualifying_zeitraum_ids.is_empty() {
1340            return ConditionResult::Unknown;
1341        }
1342        ConditionResult::True
1343    }
1344
1345    /// [2007] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [199] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfol...
1346    // REVIEW: Semantically identical to [2006] — 'Bedingungen' (plural) vs 'Bedingung' (singular) is a grammatical variation with no functional difference. Same logic: collects qualifying SG8 SEQ+Z01 Zeitraum-IDs where condition [199] is fulfilled. Recursive call self.evaluate(199, ctx) operates at message scope. (medium confidence)
1347    fn evaluate_2007(&self, ctx: &EvaluationContext) -> ConditionResult {
1348        let nav = match ctx.navigator() {
1349            Some(n) => n,
1350            None => return ConditionResult::Unknown,
1351        };
1352        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1353        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1354        for i in 0..sg8_count {
1355            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1356            let is_z01 = seq_segs.iter().any(|s| {
1357                s.elements
1358                    .first()
1359                    .and_then(|e| e.first())
1360                    .is_some_and(|v| v == "Z01")
1361            });
1362            if is_z01 && matches!(self.evaluate(199, ctx), ConditionResult::True) {
1363                for seq in &seq_segs {
1364                    if seq
1365                        .elements
1366                        .first()
1367                        .and_then(|e| e.first())
1368                        .is_some_and(|v| v == "Z01")
1369                    {
1370                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1371                            if !zid.is_empty() {
1372                                qualifying_zeitraum_ids.push(zid.clone());
1373                            }
1374                        }
1375                    }
1376                }
1377            }
1378        }
1379        if qualifying_zeitraum_ids.is_empty() {
1380            return ConditionResult::Unknown;
1381        }
1382        ConditionResult::True
1383    }
1384
1385    /// [2008] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingung [89] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolgen...
1386    // REVIEW: Same structure as [2006/2007] but references condition [89] instead of [199]. Counts qualifying SG8 SEQ+Z01 instances where condition [89] is fulfilled and collects their Zeitraum-IDs from SEQ elements[1][0]. The recursive self.evaluate(89, ctx) call operates at message scope. (medium confidence)
1387    fn evaluate_2008(&self, ctx: &EvaluationContext) -> ConditionResult {
1388        let nav = match ctx.navigator() {
1389            Some(n) => n,
1390            None => return ConditionResult::Unknown,
1391        };
1392        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1393        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1394        for i in 0..sg8_count {
1395            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1396            let is_z01 = seq_segs.iter().any(|s| {
1397                s.elements
1398                    .first()
1399                    .and_then(|e| e.first())
1400                    .is_some_and(|v| v == "Z01")
1401            });
1402            if is_z01 && matches!(self.evaluate(89, ctx), ConditionResult::True) {
1403                for seq in &seq_segs {
1404                    if seq
1405                        .elements
1406                        .first()
1407                        .and_then(|e| e.first())
1408                        .is_some_and(|v| v == "Z01")
1409                    {
1410                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1411                            if !zid.is_empty() {
1412                                qualifying_zeitraum_ids.push(zid.clone());
1413                            }
1414                        }
1415                    }
1416                }
1417            }
1418        }
1419        if qualifying_zeitraum_ids.is_empty() {
1420            return ConditionResult::Unknown;
1421        }
1422        ConditionResult::True
1423    }
1424
1425    /// [2009] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [89] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolg...
1426    // REVIEW: Semantically identical to [2008] — 'Bedingungen' (plural) vs 'Bedingung' (singular) is a grammatical variation. Same logic: collects qualifying SG8 SEQ+Z01 Zeitraum-IDs where condition [89] is fulfilled. Recursive call self.evaluate(89, ctx) operates at message scope. (medium confidence)
1427    fn evaluate_2009(&self, ctx: &EvaluationContext) -> ConditionResult {
1428        let nav = match ctx.navigator() {
1429            Some(n) => n,
1430            None => return ConditionResult::Unknown,
1431        };
1432        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1433        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1434        for i in 0..sg8_count {
1435            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1436            let is_z01 = seq_segs.iter().any(|s| {
1437                s.elements
1438                    .first()
1439                    .and_then(|e| e.first())
1440                    .is_some_and(|v| v == "Z01")
1441            });
1442            if is_z01 && matches!(self.evaluate(89, ctx), ConditionResult::True) {
1443                for seq in &seq_segs {
1444                    if seq
1445                        .elements
1446                        .first()
1447                        .and_then(|e| e.first())
1448                        .is_some_and(|v| v == "Z01")
1449                    {
1450                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1451                            if !zid.is_empty() {
1452                                qualifying_zeitraum_ids.push(zid.clone());
1453                            }
1454                        }
1455                    }
1456                }
1457            }
1458        }
1459        if qualifying_zeitraum_ids.is_empty() {
1460            return ConditionResult::Unknown;
1461        }
1462        ConditionResult::True
1463    }
1464
1465    /// [1] Wenn Aufteilung vorhanden
1466    fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
1467        ctx.external.evaluate("message_splitting")
1468    }
1469
1470    /// [4] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle LF
1471    fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
1472        ctx.external.evaluate("recipient_is_lf")
1473    }
1474
1475    /// [5] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle LF
1476    fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
1477        ctx.external.evaluate("sender_is_lf")
1478    }
1479
1480    /// [6] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle ÜNB
1481    fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
1482        ctx.external.evaluate("recipient_role_check")
1483    }
1484
1485    /// [7] Wenn SG4 STS+7++ZG9/ZH1/ZH2 (Transaktionsgrund: Aufhebung einer zukünftigen Zuordnung wegen Auszug des Kunden / -wegen Stilllegung / -wegen aufgehobenem Vertragsverhältnis) vorhanden
1486    fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
1487        let sts_segs = ctx.find_segments("STS");
1488        let found = sts_segs.iter().any(|s| {
1489            s.elements
1490                .first()
1491                .and_then(|e| e.first())
1492                .is_some_and(|v| v == "7")
1493                && s.elements
1494                    .get(2)
1495                    .and_then(|e| e.first())
1496                    .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
1497        });
1498        ConditionResult::from(found)
1499    }
1500
1501    /// [8] Wenn für Datenclearing erforderlich
1502    fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
1503        ctx.external.evaluate("data_clearing_required")
1504    }
1505
1506    /// [9] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle MSB
1507    fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
1508        ctx.external.evaluate("sender_role_check")
1509    }
1510
1511    /// [10] Wenn SG4 STS+7++xxx+xxx+E01/E03 (Transaktionsgrund befristete Anmeldung) vorhanden
1512    fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
1513        // STS+7++E01+ZW4+E03: element[4] is c556_3.d9013 = transaktionsgrundErgaenzungBefristeteAnmeldung
1514        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
1515    }
1516
1517    /// [11] Wenn SG4 STS+7++ZG9/ZH1/ZH2 (Transaktionsgrund: Aufhebung einer zukünftigen Zuordnung wegen Auszug des Kunden / -wegen Stilllegung / -wegen aufgehobenem Vertragsverhältnis) nicht vorhanden
1518    fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
1519        let sts_segs = ctx.find_segments("STS");
1520        let found = sts_segs.iter().any(|s| {
1521            s.elements
1522                .first()
1523                .and_then(|e| e.first())
1524                .is_some_and(|v| v == "7")
1525                && s.elements
1526                    .get(2)
1527                    .and_then(|e| e.first())
1528                    .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
1529        });
1530        ConditionResult::from(!found)
1531    }
1532
1533    /// [12] Wenn SG4 DTM+471 (Ende zum nächstmöglichem Termin) nicht vorhanden
1534    fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
1535        let dtm_segs = ctx.find_segments("DTM");
1536        let found = dtm_segs.iter().any(|s| {
1537            s.elements
1538                .first()
1539                .and_then(|e| e.first())
1540                .is_some_and(|v| v == "471")
1541        });
1542        ConditionResult::from(!found)
1543    }
1544
1545    /// [13] Wenn SG4 STS+E01++Z01 (Status der Antwort: Zustimmung mit Terminänderung) nicht vorhanden
1546    fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
1547        let sts_segs = ctx.find_segments("STS");
1548        let found = sts_segs.iter().any(|s| {
1549            s.elements
1550                .first()
1551                .and_then(|e| e.first())
1552                .is_some_and(|v| v == "E01")
1553                && s.elements
1554                    .get(2)
1555                    .and_then(|e| e.first())
1556                    .is_some_and(|v| ["Z01"].contains(&v.as_str()))
1557        });
1558        ConditionResult::from(!found)
1559    }
1560
1561    /// [14] Wenn Datum bekannt
1562    fn evaluate_14(&self, ctx: &EvaluationContext) -> ConditionResult {
1563        ctx.external.evaluate("date_known")
1564    }
1565
1566    /// [15] Wenn in derselben SG8 das SEQ+Z98 (Informative Daten der Marktlokation) vorhanden
1567    fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
1568        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1569        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
1570        let found = seq_segs.iter().any(|s| {
1571            s.elements
1572                .first()
1573                .and_then(|e| e.first())
1574                .is_some_and(|v| ["Z98"].contains(&v.as_str()))
1575        });
1576        ConditionResult::from(found)
1577    }
1578
1579    /// [16] Wenn SG4 STS+E01++Z12 (Status der Antwort: Ablehnung Vertragsbindung) vorhanden
1580    fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
1581        let sts_segs = ctx.find_segments("STS");
1582        let found = sts_segs.iter().any(|s| {
1583            s.elements
1584                .first()
1585                .and_then(|e| e.first())
1586                .is_some_and(|v| v == "E01")
1587                && s.elements
1588                    .get(2)
1589                    .and_then(|e| e.first())
1590                    .is_some_and(|v| ["Z12"].contains(&v.as_str()))
1591        });
1592        ConditionResult::from(found)
1593    }
1594
1595    /// [17] Wenn in derselben SG8 das SEQ+Z01/Z80/Z81 (Daten der Marktlokation) vorhanden
1596    fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
1597        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1598        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
1599        let found = seq_segs.iter().any(|s| {
1600            s.elements
1601                .first()
1602                .and_then(|e| e.first())
1603                .is_some_and(|v| ["Z01", "Z80", "Z81"].contains(&v.as_str()))
1604        });
1605        ConditionResult::from(found)
1606    }
1607
1608    /// [18] Wenn SG4 DTM+93 (Ende zum) nicht vorhanden
1609    fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
1610        let dtm_segs = ctx.find_segments("DTM");
1611        let found = dtm_segs.iter().any(|s| {
1612            s.elements
1613                .first()
1614                .and_then(|e| e.first())
1615                .is_some_and(|v| v == "93")
1616        });
1617        ConditionResult::from(!found)
1618    }
1619
1620    /// [19] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+++ZC0 (Prognose auf Basis von Werten) vorhanden
1621    fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
1622        let seq_segs = ctx.find_segments("SEQ");
1623        let found = seq_segs.iter().any(|s| {
1624            s.elements
1625                .first()
1626                .and_then(|e| e.first())
1627                .is_some_and(|v| ["Z01", "Z98"].contains(&v.as_str()))
1628        });
1629        ConditionResult::from(found)
1630    }
1631
1632    /// [20] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z07 (Verbrauch) vorhanden
1633    fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
1634        let seq_segs = ctx.find_segments("SEQ");
1635        let found = seq_segs.iter().any(|s| {
1636            s.elements
1637                .first()
1638                .and_then(|e| e.first())
1639                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
1640        });
1641        ConditionResult::from(found)
1642    }
1643
1644    /// [21] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
1645    fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
1646        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1647        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
1648        let found = cci_segs.iter().any(|s| {
1649            s.elements
1650                .get(2)
1651                .and_then(|e| e.first())
1652                .is_some_and(|v| v == "ZA6")
1653        });
1654        ConditionResult::from(found)
1655    }
1656
1657    /// [22] Es ist die Zeitraum-ID vom DE1156 aus einem passenden SG6 RFF+Z47/ Z48/ Z49 (Verwendungszeitraum der Daten) einzutragen
1658    // REVIEW: Checks if any SG6 RFF+Z47/Z48/Z49 Zeitraum-ID (DE1156 at elements[0][2]) matches any SG8 SEQ Zeitraum-ID reference (DE1050 at elements[1][0]). Uses groups_share_qualified_value for cross-group correlation across all three qualifying codes. (medium confidence)
1659    fn evaluate_22(&self, ctx: &EvaluationContext) -> ConditionResult {
1660        for qual in &["Z47", "Z48", "Z49"] {
1661            if matches!(
1662                ctx.groups_share_qualified_value(
1663                    "RFF",
1664                    0,
1665                    qual,
1666                    0,
1667                    2,
1668                    &["SG4", "SG6"],
1669                    "SEQ",
1670                    1,
1671                    0,
1672                    &["SG4", "SG8"],
1673                ),
1674                ConditionResult::True
1675            ) {
1676                return ConditionResult::True;
1677            }
1678        }
1679        ConditionResult::False
1680    }
1681
1682    /// [23] Wenn in dieser SG4 das STS+E01++A05/A99 (Status der Antwort) vorhanden
1683    fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
1684        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1685        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1686        let found = sts_segs.iter().any(|s| {
1687            s.elements
1688                .first()
1689                .and_then(|e| e.first())
1690                .is_some_and(|v| v == "E01")
1691                && s.elements
1692                    .get(2)
1693                    .and_then(|e| e.first())
1694                    .is_some_and(|v| ["A05", "A99"].contains(&v.as_str()))
1695        });
1696        ConditionResult::from(found)
1697    }
1698
1699    /// [24] Wenn in dieser SG4 das STS+E01++A25/A99 (Status der Antwort) vorhanden
1700    fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
1701        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1702        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1703        let found = sts_segs.iter().any(|s| {
1704            s.elements
1705                .first()
1706                .and_then(|e| e.first())
1707                .is_some_and(|v| v == "E01")
1708                && s.elements
1709                    .get(2)
1710                    .and_then(|e| e.first())
1711                    .is_some_and(|v| ["A25", "A99"].contains(&v.as_str()))
1712        });
1713        ConditionResult::from(found)
1714    }
1715
1716    /// [25] Wenn die Veräußerungsform der erzeugenden Marktlokation der Marktprämie zugeordnet ist
1717    fn evaluate_25(&self, _ctx: &EvaluationContext) -> ConditionResult {
1718        // TODO: implement
1719        ConditionResult::Unknown
1720    }
1721
1722    /// [27] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle ein T oder R enthält
1723    fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
1724        let dtm_segs = ctx.find_segments("DTM");
1725        let found = dtm_segs.iter().any(|s| {
1726            s.elements
1727                .first()
1728                .and_then(|e| e.first())
1729                .is_some_and(|v| v == "Z01")
1730                && s.elements
1731                    .first()
1732                    .and_then(|e| e.get(1))
1733                    .is_some_and(|v| v.chars().nth(3).is_some_and(|c| ['T', 'R'].contains(&c)))
1734        });
1735        ConditionResult::from(found)
1736    }
1737
1738    /// [30] Wenn Antwort auf Aktivierung übermittelt wird
1739    fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
1740        // Antwort auf Aktivierung — depends on PID context
1741        ctx.external.evaluate("activation_response")
1742    }
1743
1744    /// [31] Wenn eine Korrektur erfolgt
1745    fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
1746        ctx.external.evaluate("correction_in_progress")
1747    }
1748
1749    /// [32] Wenn mehr als ein SG8 (Referenz auf die Lokationsbündelstruktur) vorhanden
1750    fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
1751        let seq_segs = ctx.find_segments("SEQ");
1752        ConditionResult::from(seq_segs.len() > 1)
1753    }
1754
1755    /// [33] Wenn in einer SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6 \"Produkte zur Bestellung / Änderung von Date...
1756    fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
1757        ctx.external.evaluate("code_list_membership_check")
1758    }
1759
1760    /// [34] Wenn Antwort auf Deaktivierung übermittelt wird
1761    fn evaluate_34(&self, ctx: &EvaluationContext) -> ConditionResult {
1762        // Antwort auf Deaktivierung — depends on PID context
1763        ctx.external.evaluate("deactivation_response")
1764    }
1765
1766    /// [35] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle T (Termin) enthält
1767    fn evaluate_35(&self, ctx: &EvaluationContext) -> ConditionResult {
1768        let dtm_segs = ctx.find_segments("DTM");
1769        let found = dtm_segs.iter().any(|s| {
1770            s.elements
1771                .first()
1772                .and_then(|e| e.first())
1773                .is_some_and(|v| v == "Z01")
1774                && s.elements
1775                    .first()
1776                    .and_then(|e| e.get(1))
1777                    .is_some_and(|v| v.chars().nth(3).is_some_and(|c| c == 'T'))
1778        });
1779        ConditionResult::from(found)
1780    }
1781
1782    /// [36] Wenn in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTI...
1783    fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
1784        ctx.external.evaluate("code_list_membership_check")
1785    }
1786
1787    /// [37] Wenn Anmeldung/ Änderung befristet
1788    fn evaluate_37(&self, ctx: &EvaluationContext) -> ConditionResult {
1789        // Same check as [10]: STS element[4] is befristete Anmeldung code
1790        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
1791    }
1792
1793    /// [38] Es sind nur die Code der Produkteigenschaft zu dem in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 erlaubt, die in der Codeliste der Konfigurationen im Kapitel  6.1 \"Anmeldung ei...
1794    fn evaluate_38(&self, ctx: &EvaluationContext) -> ConditionResult {
1795        ctx.external.evaluate("code_list_membership_check")
1796    }
1797
1798    /// [39] Wenn in derselben SG8 SEQ+Z79 m PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTIL...
1799    fn evaluate_39(&self, ctx: &EvaluationContext) -> ConditionResult {
1800        ctx.external.evaluate("code_list_membership_check")
1801    }
1802
1803    /// [40] Es ist nur der Wertebereich erlaubt, der zu dem in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 genannten Produkt, das in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeld...
1804    fn evaluate_40(&self, ctx: &EvaluationContext) -> ConditionResult {
1805        ctx.external.evaluate("code_list_membership_check")
1806    }
1807
1808    /// [41] Es ist eine Produktpaket-ID aus dem DE1050 von einem SG8 SEQ+Z79 (Erforderliches Produkt) zu nennen
1809    fn evaluate_41(&self, ctx: &EvaluationContext) -> ConditionResult {
1810        // Check: Produktpaket-ID exists in SEQ+Z79 (Erforderliches Produkt)
1811        let seq_z79 = ctx.find_segments_with_qualifier("SEQ", 0, "Z79");
1812        if seq_z79.is_empty() {
1813            return ConditionResult::Unknown;
1814        }
1815        // Check if SEQ has a value in C286.D1050 (elements[1][0])
1816        let found = seq_z79.iter().any(|s| {
1817            s.elements
1818                .get(1)
1819                .and_then(|e| e.first())
1820                .is_some_and(|v| !v.is_empty())
1821        });
1822        ConditionResult::from(found)
1823    }
1824
1825    /// [42] Wenn mehr als ein SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) vorhanden
1826    fn evaluate_42(&self, ctx: &EvaluationContext) -> ConditionResult {
1827        let seq_segs = ctx.find_segments("SEQ");
1828        let zh0_count = seq_segs
1829            .iter()
1830            .filter(|s| {
1831                s.elements
1832                    .first()
1833                    .and_then(|e| e.first())
1834                    .is_some_and(|v| v == "ZH0")
1835            })
1836            .count();
1837        ConditionResult::from(zh0_count > 1)
1838    }
1839
1840    /// [43] Wenn in dieser SG4 das STS+E01++A12 / A13 (Status der Antwort) vorhanden
1841    fn evaluate_43(&self, ctx: &EvaluationContext) -> ConditionResult {
1842        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1843        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1844        let found = sts_segs.iter().any(|s| {
1845            s.elements
1846                .first()
1847                .and_then(|e| e.first())
1848                .is_some_and(|v| v == "E01")
1849                && s.elements
1850                    .get(2)
1851                    .and_then(|e| e.first())
1852                    .is_some_and(|v| ["A12", "A13"].contains(&v.as_str()))
1853        });
1854        ConditionResult::from(found)
1855    }
1856
1857    /// [44] Es ist die Zeitraum-ID vom DE1156 aus einem passenden SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: "Gültige Daten", "Keine Daten") aus der Anfragennachricht aus SG6 RFF+TN DE1154 ((Referenz Vo...
1858    fn evaluate_44(&self, _ctx: &EvaluationContext) -> ConditionResult {
1859        // Hinweis: Zeitraum-ID aus passendem SG6 RFF+Z49/Z53 der Anfragenachricht eintragen — informational data population rule
1860        ConditionResult::True
1861    }
1862
1863    /// [45] Wenn in derselben SG8 das SG10 CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
1864    fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
1865        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1866        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
1867        let found = cci_segs.iter().any(|s| {
1868            s.elements
1869                .first()
1870                .and_then(|e| e.first())
1871                .is_some_and(|v| v == "Z01")
1872                && s.elements
1873                    .get(2)
1874                    .and_then(|e| e.first())
1875                    .is_some_and(|v| v == "Z82")
1876        });
1877        ConditionResult::from(found)
1878    }
1879
1880    /// [46] Wenn ID für Objekt vergeben wurde
1881    fn evaluate_46(&self, _ctx: &EvaluationContext) -> ConditionResult {
1882        // TODO: implement
1883        ConditionResult::Unknown
1884    }
1885
1886    /// [47] Wenn SG10 CCI+15++Z21 (Summenzeitreihentyp) CAV+DBA/ VZR/ EGS/ LGS/ SLS/ SES/ TLS/ TES/ BIL/ BIP/ BIT/ GAL/ GAP/ GAT/ GEL/ GEP/ GET/ SOL/ SOP/ SOT/ WFL/ WFP/ WFT/ WNL/ WNP/ WNT vorhanden
1887    // REVIEW: Iterates all SG8 instances and their SG10 children looking for CCI with elements[0][0]=="15" and elements[2][0]=="Z21" (Zeitreihentyp), then checks if the same SG10 has a CAV with elements[0][0] in the Summenzeitreihentyp code list. Uses navigator for parent-child group traversal. (medium confidence)
1888    fn evaluate_47(&self, ctx: &EvaluationContext) -> ConditionResult {
1889        const CODES: &[&str] = &[
1890            "DBA", "VZR", "EGS", "LGS", "SLS", "SES", "TLS", "TES", "BIL", "BIP", "BIT", "GAL",
1891            "GAP", "GAT", "GEL", "GEP", "GET", "SOL", "SOP", "SOT", "WFL", "WFP", "WFT", "WNL",
1892            "WNP", "WNT",
1893        ];
1894        let nav = match ctx.navigator {
1895            Some(n) => n,
1896            None => return ConditionResult::Unknown,
1897        };
1898        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1899        for i in 0..sg8_count {
1900            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
1901            for j in 0..sg10_count {
1902                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
1903                let has_cci_15_z21 = ccis.iter().any(|s| {
1904                    s.elements
1905                        .first()
1906                        .and_then(|e: &Vec<String>| e.first())
1907                        .is_some_and(|v: &String| v == "15")
1908                        && s.elements
1909                            .get(2)
1910                            .and_then(|e: &Vec<String>| e.first())
1911                            .is_some_and(|v: &String| v == "Z21")
1912                });
1913                if has_cci_15_z21 {
1914                    let cavs =
1915                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
1916                    if cavs.iter().any(|s| {
1917                        s.elements
1918                            .first()
1919                            .and_then(|e: &Vec<String>| e.first())
1920                            .is_some_and(|v: &String| CODES.contains(&v.as_str()))
1921                    }) {
1922                        return ConditionResult::True;
1923                    }
1924                }
1925            }
1926        }
1927        ConditionResult::False
1928    }
1929
1930    /// [48] Wenn in dieser SG4 das STS+E01++A99 (Status der Antwort: Sonstiges) vorhanden
1931    fn evaluate_48(&self, ctx: &EvaluationContext) -> ConditionResult {
1932        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1933        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1934        let found = sts_segs.iter().any(|s| {
1935            s.elements
1936                .first()
1937                .and_then(|e| e.first())
1938                .is_some_and(|v| v == "E01")
1939                && s.elements
1940                    .get(2)
1941                    .and_then(|e| e.first())
1942                    .is_some_and(|v| ["A99"].contains(&v.as_str()))
1943        });
1944        ConditionResult::from(found)
1945    }
1946
1947    /// [49] Wenn in dieser SG4 das STS+E01++E14 (Status der Antwort: Ablehnung Sonstiges) vorhanden
1948    fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
1949        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1950        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1951        let found = sts_segs.iter().any(|s| {
1952            s.elements
1953                .first()
1954                .and_then(|e| e.first())
1955                .is_some_and(|v| v == "E01")
1956                && s.elements
1957                    .get(2)
1958                    .and_then(|e| e.first())
1959                    .is_some_and(|v| ["E14"].contains(&v.as_str()))
1960        });
1961        ConditionResult::from(found)
1962    }
1963
1964    /// [50] Wenn kein SG8 SEQ+ZE7 (Informative Daten der Tranche) mit derselben Zeitraum-ID im DE1050 auf die gleiche Marktlokation-ID mit dem RFF+Z18 (Marktlokation) referenziert wie dieses SG8 SEQ+Z98 (Infor...
1965    // REVIEW: Collects (Zeitraum-ID from SEQ.elements[1][0], Marktlok-ID from sibling RFF+Z18.elements[0][1]) for all SG8 instances with SEQ+Z98 and SEQ+ZE7. Returns True if any Z98 group has no ZE7 group matching on both values. Uses collect_group_values with instance-index correlation via HashMap, assuming each SG8 has at most one SEQ (guaranteed as entry segment) and the zip ordering is stable. (medium confidence)
1966    fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
1967        use std::collections::HashMap;
1968
1969        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
1970        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
1971        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
1972        let rff_ids = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
1973
1974        // Build per-instance RFF lookup: instance_idx -> list of (qualifier, id)
1975        let mut rff_by_instance: HashMap<usize, Vec<(String, String)>> = HashMap::new();
1976        for ((idx, q), (_, id)) in rff_quals.iter().zip(rff_ids.iter()) {
1977            rff_by_instance
1978                .entry(*idx)
1979                .or_default()
1980                .push((q.clone(), id.clone()));
1981        }
1982
1983        // Build (zeitraum_id, marktlok_id) for Z98 and ZE7 SG8 instances
1984        let mut seq_zeitraum_map: HashMap<usize, String> = HashMap::new();
1985        for (idx, z) in &seq_zeitraum {
1986            seq_zeitraum_map.insert(*idx, z.clone());
1987        }
1988
1989        let z98_data: Vec<(String, String)> = seq_quals
1990            .iter()
1991            .filter(|(_, q)| q == "Z98")
1992            .map(|(idx, _)| {
1993                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
1994                let marktlok = rff_by_instance
1995                    .get(idx)
1996                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
1997                    .map(|(_, id)| id.clone())
1998                    .unwrap_or_default();
1999                (zeitraum, marktlok)
2000            })
2001            .collect();
2002
2003        if z98_data.is_empty() {
2004            return ConditionResult::Unknown;
2005        }
2006
2007        let ze7_data: Vec<(String, String)> = seq_quals
2008            .iter()
2009            .filter(|(_, q)| q == "ZE7")
2010            .map(|(idx, _)| {
2011                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
2012                let marktlok = rff_by_instance
2013                    .get(idx)
2014                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
2015                    .map(|(_, id)| id.clone())
2016                    .unwrap_or_default();
2017                (zeitraum, marktlok)
2018            })
2019            .collect();
2020
2021        ConditionResult::from(z98_data.iter().any(|(z98_zeit, z98_lok)| {
2022            !ze7_data
2023                .iter()
2024                .any(|(ze7_zeit, ze7_lok)| ze7_zeit == z98_zeit && ze7_lok == z98_lok)
2025        }))
2026    }
2027
2028    /// [51] Wenn die Marktlokations-ID im SG8 RFF+Z18 derselben SG8 SEQ+Z98 (Informative Daten der Marktlokation) mit derselben Zeitraum-ID im DE1050 nicht auf ein SG8 SEQ+Z78 (Referenz auf die Lokationsbünde...
2029    // REVIEW: Condition is True when no Z98 SG8 (Informative Daten der Marktlokation) references — via matching Zeitraum-ID in SEQ.C286.DE1050 — a Z78 SG8 (Referenz auf Lokationsbündelstruktur) that carries the pauschal bundle code 9992000000018 in RFF+Z31.DE1154. Requires navigator for cross-SG8 Zeitraum-ID correlation. (medium confidence)
2030    fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
2031        let nav = match ctx.navigator() {
2032            Some(n) => n,
2033            None => return ConditionResult::Unknown,
2034        };
2035        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2036        let pauschal_id = "9992000000018";
2037        // Collect Zeitraum-IDs from Z78 SG8s that carry the pauschal RFF+Z31 ID
2038        let mut pauschal_zeitraum_ids: Vec<String> = Vec::new();
2039        for i in 0..sg8_count {
2040            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2041            let has_z78 = seqs.iter().any(|s| {
2042                s.elements
2043                    .first()
2044                    .and_then(|e| e.first())
2045                    .is_some_and(|v| v == "Z78")
2046            });
2047            if !has_z78 {
2048                continue;
2049            }
2050            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
2051            let has_pauschal = rffs.iter().any(|s| {
2052                s.elements
2053                    .first()
2054                    .and_then(|e| e.first())
2055                    .is_some_and(|v| v == "Z31")
2056                    && s.elements
2057                        .first()
2058                        .and_then(|e| e.get(1))
2059                        .is_some_and(|v| v.replace(' ', "") == pauschal_id)
2060            });
2061            if has_pauschal {
2062                for seq in &seqs {
2063                    if seq
2064                        .elements
2065                        .first()
2066                        .and_then(|e| e.first())
2067                        .is_some_and(|v| v == "Z78")
2068                    {
2069                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
2070                            if !zid.is_empty() {
2071                                pauschal_zeitraum_ids.push(zid.clone());
2072                            }
2073                        }
2074                    }
2075                }
2076            }
2077        }
2078        // For each Z98 SG8, check whether its Zeitraum-ID (SEQ.C286.DE1050) maps to a pauschal Z78
2079        for i in 0..sg8_count {
2080            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2081            for seq in &seqs {
2082                if seq
2083                    .elements
2084                    .first()
2085                    .and_then(|e| e.first())
2086                    .is_some_and(|v| v == "Z98")
2087                {
2088                    let zid = seq
2089                        .elements
2090                        .get(1)
2091                        .and_then(|e| e.first())
2092                        .map(|s| s.as_str())
2093                        .unwrap_or("");
2094                    if !zid.is_empty() && pauschal_zeitraum_ids.iter().any(|z| z == zid) {
2095                        // Z98 SG8 references a pauschal Z78 → condition is False
2096                        return ConditionResult::False;
2097                    }
2098                }
2099            }
2100        }
2101        // No Z98 SG8 references the pauschal bundle → condition is True
2102        ConditionResult::True
2103    }
2104
2105    /// [52] Wenn SG10 CCI+Z07++Z55/ Z56 (Profiltyp: sonstige verbrauchende Marktlokation / sonstige erzeugende Marktlokation) vorhanden
2106    fn evaluate_52(&self, ctx: &EvaluationContext) -> ConditionResult {
2107        let cci_segs = ctx.find_segments("CCI");
2108        let found = cci_segs.iter().any(|s| {
2109            s.elements
2110                .first()
2111                .and_then(|e| e.first())
2112                .is_some_and(|v| v == "Z07")
2113                && s.elements
2114                    .get(2)
2115                    .and_then(|e| e.first())
2116                    .is_some_and(|v| ["Z55", "Z56"].contains(&v.as_str()))
2117        });
2118        ConditionResult::from(found)
2119    }
2120
2121    /// [53] Wenn weitere Präzisierungen als über CCI+Z07 (Profiltyp) möglich sind
2122    fn evaluate_53(&self, _ctx: &EvaluationContext) -> ConditionResult {
2123        // TODO: implement
2124        ConditionResult::Unknown
2125    }
2126
2127    /// [54] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) vorhanden
2128    fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
2129        let seq_segs = ctx.find_segments("SEQ");
2130        let found = seq_segs.iter().any(|s| {
2131            s.elements
2132                .first()
2133                .and_then(|e| e.first())
2134                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
2135        });
2136        ConditionResult::from(found)
2137    }
2138
2139    /// [55] Wenn in dieser SG4 das STS+E01++A06/ A99 (Status der Antwort) vorhanden
2140    fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
2141        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2142        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2143        let found = sts_segs.iter().any(|s| {
2144            s.elements
2145                .first()
2146                .and_then(|e| e.first())
2147                .is_some_and(|v| v == "E01")
2148                && s.elements
2149                    .get(2)
2150                    .and_then(|e| e.first())
2151                    .is_some_and(|v| ["A06", "A99"].contains(&v.as_str()))
2152        });
2153        ConditionResult::from(found)
2154    }
2155
2156    /// [56] Wenn im STS+E01 im DE9013 (Status der Antwort) ein Antwortcode aus dem Cluster Zustimmung vorhanden ist
2157    fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
2158        // Check: STS+E01 DE9013 has a Zustimmung (approval) cluster code
2159        // Zustimmung codes start with A0x (A01-A07 are approval codes)
2160        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
2161        let found = sts_segs.iter().any(|s| {
2162            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
2163                [
2164                    "A01", "A02", "A03", "A04", "A05", "A06", "A07", "A08", "A09", "A10", "A11",
2165                    "A12", "A13", "A14", "A15", "A16", "A17", "A25",
2166                ]
2167                .contains(&v.as_str())
2168            })
2169        });
2170        ConditionResult::from(found)
2171    }
2172
2173    /// [57] Wenn in derselben SG8 das CCI+Z45++ZD9 (Abrechnung findet statt) vorhanden ist
2174    fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
2175        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2176        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
2177        let found = cci_segs.iter().any(|s| {
2178            s.elements
2179                .first()
2180                .and_then(|e| e.first())
2181                .is_some_and(|v| v == "Z45")
2182                && s.elements
2183                    .get(2)
2184                    .and_then(|e| e.first())
2185                    .is_some_and(|v| v == "ZD9")
2186        });
2187        ConditionResult::from(found)
2188    }
2189
2190    /// [58] Wenn in diesem CCI das DE3055 mit dem Code 293 vorhanden
2191    fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
2192        // HAND-EDITED: "in diesem CCI" — the CCI the field is in (its own segment
2193        // in tree validation), else the enclosing SG10's CCI.
2194        let has_293 =
2195            |elements: &[Vec<String>]| elements.iter().any(|e| e.iter().any(|v| v == "293"));
2196        if let Some(own) = ctx.resolved_segment.filter(|_| {
2197            ctx.scoped_find_segments_in("SG10", "CCI")
2198                .iter()
2199                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
2200        }) {
2201            return ConditionResult::from(has_293(own));
2202        }
2203        ConditionResult::from(
2204            ctx.scoped_find_segments_in("SG10", "CCI")
2205                .iter()
2206                .any(|s| has_293(&s.elements)),
2207        )
2208    }
2209
2210    /// [60] Wenn BGM+Z90 (Beendigung der Zuordnung zur Lokation) vorhanden
2211    fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
2212        let bgm_segs = ctx.find_segments("BGM");
2213        let found = bgm_segs.iter().any(|s| {
2214            s.elements
2215                .first()
2216                .and_then(|e| e.first())
2217                .is_some_and(|v| v == "Z90")
2218        });
2219        ConditionResult::from(found)
2220    }
2221
2222    /// [61] Wenn BGM+Z89 (Zuordnung zur Lokation) vorhanden
2223    fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
2224        let bgm_segs = ctx.find_segments("BGM");
2225        let found = bgm_segs.iter().any(|s| {
2226            s.elements
2227                .first()
2228                .and_then(|e| e.first())
2229                .is_some_and(|v| v == "Z89")
2230        });
2231        ConditionResult::from(found)
2232    }
2233
2234    /// [62] Wenn mehr als eine SG8 SEQ+Z45 (Netznutzungsabrechnungsdaten der Marktlokation), mit derselben Zeitraum-ID im DE1050, mit einer Gruppen-/Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), ...
2235    // REVIEW: Iterates SG8 instances within each SG4, finds those with SEQ+Z45 and a non-empty Zeitraum-ID in elements[1][0]. Checks if PIA+Z02 in the same SG8 has a product code (elements[1][0]) beginning with 1-08-1 through 1-08-5. Groups by Zeitraum-ID and checks if any ID appears more than once. Requires navigator for per-instance group segment access; returns Unknown without one. (medium confidence)
2236    fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
2237        let nav = match ctx.navigator {
2238            Some(n) => n,
2239            None => return ConditionResult::Unknown,
2240        };
2241        use std::collections::HashMap;
2242        let mut count_by_zid: HashMap<String, u32> = HashMap::new();
2243        let sg4_count = nav.group_instance_count(&["SG4"]);
2244        for sg4_idx in 0..sg4_count {
2245            let sg8_count = nav.child_group_instance_count(&["SG4"], sg4_idx, "SG8");
2246            for sg8_idx in 0..sg8_count {
2247                let seqs =
2248                    nav.find_segments_in_child_group("SEQ", &["SG4"], sg4_idx, "SG8", sg8_idx);
2249                let zeitraum_opt = seqs
2250                    .iter()
2251                    .filter(|s| {
2252                        s.elements
2253                            .first()
2254                            .and_then(|e: &Vec<String>| e.first())
2255                            .is_some_and(|v: &String| v == "Z45")
2256                    })
2257                    .filter_map(|s| {
2258                        s.elements
2259                            .get(1)
2260                            .and_then(|e: &Vec<String>| e.first())
2261                            .filter(|v| !v.is_empty())
2262                            .cloned()
2263                    })
2264                    .next();
2265                let Some(zid) = zeitraum_opt else { continue };
2266                let pias =
2267                    nav.find_segments_in_child_group("PIA", &["SG4"], sg4_idx, "SG8", sg8_idx);
2268                let has_qualifying_pia = pias.iter().any(|s| {
2269                    s.elements
2270                        .first()
2271                        .and_then(|e: &Vec<String>| e.first())
2272                        .is_some_and(|v: &String| v == "Z02")
2273                        && s.elements
2274                            .get(1)
2275                            .and_then(|e: &Vec<String>| e.first())
2276                            .map(|code| {
2277                                code.starts_with("1-08-1")
2278                                    || code.starts_with("1-08-2")
2279                                    || code.starts_with("1-08-3")
2280                                    || code.starts_with("1-08-4")
2281                                    || code.starts_with("1-08-5")
2282                            })
2283                            .unwrap_or(false)
2284                });
2285                if has_qualifying_pia {
2286                    *count_by_zid.entry(zid).or_insert(0) += 1;
2287                }
2288            }
2289        }
2290        ConditionResult::from(count_by_zid.values().any(|&c| c > 1))
2291    }
2292
2293    /// [63] Wenn in dieser SG4 das STS+E01++A15/ A99 (Status der Antwort) vorhanden
2294    fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
2295        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2296        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2297        let found = sts_segs.iter().any(|s| {
2298            s.elements
2299                .first()
2300                .and_then(|e| e.first())
2301                .is_some_and(|v| v == "E01")
2302                && s.elements
2303                    .get(2)
2304                    .and_then(|e| e.first())
2305                    .is_some_and(|v| ["A15", "A99"].contains(&v.as_str()))
2306        });
2307        ConditionResult::from(found)
2308    }
2309
2310    /// [64] Wenn mehr als eine SG8 SEQ+ZE1 (Informative Netznutzungsabrechnungsdaten der Marktlokation) mit einer Gruppen-/ Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5...
2311    fn evaluate_64(&self, ctx: &EvaluationContext) -> ConditionResult {
2312        // Check: more than one SG8 SEQ+ZE1 has PIA+Z02 with code starting with 1-08-1/2/3/4/5
2313        let pia_segs = ctx.find_segments("PIA");
2314        let count = pia_segs
2315            .iter()
2316            .filter(|s| {
2317                s.elements
2318                    .first()
2319                    .and_then(|e| e.first())
2320                    .is_some_and(|v| v == "Z02")
2321                    && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
2322                        ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"]
2323                            .iter()
2324                            .any(|p| v.starts_with(p))
2325                    })
2326            })
2327            .count();
2328        ConditionResult::from(count > 1)
2329    }
2330
2331    /// [65] Wenn in derselben SG8 SEQ+ZE1 (Informative Netznutzungsabrechnungsdaten der Marktlokation) eine Gruppen-/Artikel-ID im PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5 beginnt, v...
2332    fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
2333        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2334        // Check: PIA+Z02 with code starting with 1-08-1/2/3/4/5 exists in same SG8 SEQ+ZE1
2335        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
2336        let found = pia_segs.iter().any(|s| {
2337            s.elements
2338                .first()
2339                .and_then(|e| e.first())
2340                .is_some_and(|v| v == "Z02")
2341                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
2342                    ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"]
2343                        .iter()
2344                        .any(|p| v.starts_with(p))
2345                })
2346        });
2347        ConditionResult::from(found)
2348    }
2349
2350    /// [66] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als einmal vorhanden
2351    fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
2352        let seq_segs = ctx.find_segments("SEQ");
2353        let count = seq_segs
2354            .iter()
2355            .filter(|s| {
2356                s.elements
2357                    .first()
2358                    .and_then(|e| e.first())
2359                    .is_some_and(|v| v == "ZH0")
2360            })
2361            .count();
2362        ConditionResult::from(count > 1)
2363    }
2364
2365    /// [67] Wenn in keinem SG8 SEQ+Z79 (Bestandteil eine Produktpaketes CCI+Z66/CAV+ZH9 (Produkteigenschaft/ Code der Produkteigenschaft) der Code 9991000002933 (Ruhende Marktlokation ausprägen) vorhanden ist.
2366    fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
2367        // Check: no SG8 SEQ+Z79 has CAV+ZH9 with code 9991000002933
2368        let cav_segs = ctx.find_segments("CAV");
2369        let found = cav_segs.iter().any(|s| {
2370            s.elements
2371                .first()
2372                .and_then(|e| e.first())
2373                .is_some_and(|v| v == "ZH9")
2374                && s.elements
2375                    .first()
2376                    .and_then(|e| e.get(1))
2377                    .is_some_and(|v| v == "9991000002933")
2378        });
2379        ConditionResult::from(!found)
2380    }
2381
2382    /// [68] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als zweimal vorhanden
2383    fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
2384        let seq_segs = ctx.find_segments("SEQ");
2385        let count = seq_segs
2386            .iter()
2387            .filter(|s| {
2388                s.elements
2389                    .first()
2390                    .and_then(|e| e.first())
2391                    .is_some_and(|v| v == "ZH0")
2392            })
2393            .count();
2394        ConditionResult::from(count > 2)
2395    }
2396
2397    /// [69] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als dreimal vorhanden
2398    fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
2399        let seq_segs = ctx.find_segments("SEQ");
2400        let count = seq_segs
2401            .iter()
2402            .filter(|s| {
2403                s.elements
2404                    .first()
2405                    .and_then(|e| e.first())
2406                    .is_some_and(|v| v == "ZH0")
2407            })
2408            .count();
2409        ConditionResult::from(count > 3)
2410    }
2411
2412    /// [70] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) fünfmal vorhanden
2413    fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
2414        let seq_segs = ctx.find_segments("SEQ");
2415        let count = seq_segs
2416            .iter()
2417            .filter(|s| {
2418                s.elements
2419                    .first()
2420                    .and_then(|e| e.first())
2421                    .is_some_and(|v| v == "ZH0")
2422            })
2423            .count();
2424        ConditionResult::from(count == 5)
2425    }
2426
2427    /// [71] Wenn Antwort auf Zuordnung übermittelt wird
2428    fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
2429        // Antwort auf Zuordnung — depends on PID context
2430        ctx.external.evaluate("assignment_response")
2431    }
2432
2433    /// [72] Wenn Antwort auf Beendigung der Zuordnung übermittelt wird
2434    fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
2435        // Antwort auf Beendigung der Zuordnung — depends on PID context
2436        ctx.external.evaluate("assignment_end_response")
2437    }
2438
2439    /// [73] Wenn in derselben SG8 im PIA DE7140 ein Code aus der Codeliste der Gruppenartikel- und Artikel-ID vorhanden ist, der in der Spalte UTILMD/Preisangabe mit X gekennzeichnet ist
2440    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
2441    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
2442    // Regenerating this function should preserve the shape, but re-verify
2443    // against the committed body if generate-conditions is run.
2444    fn evaluate_73(&self, ctx: &EvaluationContext) -> ConditionResult {
2445        // [73] Wenn in derselben SG8 im PIA DE7140 ein Code aus der
2446        // Codeliste der Gruppenartikel- und Artikel-ID vorhanden ist,
2447        // der in der Spalte UTILMD/Preisangabe mit X gekennzeichnet ist.
2448        //
2449        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
2450        // narrows to the enclosing SG8 instance; otherwise falls back to
2451        // message-wide find_segments. Matches the AHB wording
2452        // "in derselben SG8 im PIA DE7140 ein Code...".
2453        use crate::eval::CodelistColumn;
2454        let any_match = ctx.scoped_find_segments_in("SG8", "PIA").iter().any(|s| {
2455            let is_z02 = s
2456                .elements
2457                .first()
2458                .and_then(|e| e.first())
2459                .is_some_and(|v| v == "Z02");
2460            if !is_z02 {
2461                return false;
2462            }
2463            let code = match s.elements.get(1).and_then(|e| e.first()) {
2464                Some(c) if !c.is_empty() => c,
2465                _ => return false,
2466            };
2467            ctx.codelist
2468                .flag(code, CodelistColumn::UtilmdPreisangabe)
2469                .unwrap_or(false)
2470        });
2471        ConditionResult::from(any_match)
2472    }
2473
2474    /// [74] Wenn in der selben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) vorhanden
2475    fn evaluate_74(&self, ctx: &EvaluationContext) -> ConditionResult {
2476        let seq_segs = ctx.find_segments("SEQ");
2477        let has_seq = seq_segs.iter().any(|s| {
2478            s.elements
2479                .first()
2480                .and_then(|e| e.first())
2481                .is_some_and(|v| v == "Z59")
2482        });
2483        let pia_segs = ctx.find_segments("PIA");
2484        let has_pia = pia_segs.iter().any(|s| {
2485            s.elements
2486                .first()
2487                .and_then(|e| e.first())
2488                .is_some_and(|v| v == "5")
2489        });
2490        ConditionResult::from(has_seq && has_pia)
2491    }
2492
2493    /// [75] Wenn in diesem PIA+5 in DE7140 der Code 9991000000721 (vgl.: Kapitel 4.2. Konfigurationsprodukte Leistungskurvendefinition der Codeliste der Konfigurationen) vorhanden
2494    fn evaluate_75(&self, ctx: &EvaluationContext) -> ConditionResult {
2495        ctx.external.evaluate("code_list_membership_check")
2496    }
2497
2498    /// [76] Wenn Summenzeitreihe auf Ebene des Bilanzierungsgebiet
2499    // REVIEW: SEQ+Z49 is 'Abgerechnete Daten der Bilanzierungsgebietssummenzeitreihe' — the only SEQ code in the reference that explicitly names a Bilanzierungsgebiet-level Summenzeitreihe. Checking its presence is the most direct mapping of the condition text. A broader interpretation involving CAV codes (Bezeichnung der Summenzeitreihe) is possible but those codes are not fully enumerated in the provided reference. (medium confidence)
2500    fn evaluate_76(&self, ctx: &EvaluationContext) -> ConditionResult {
2501        ctx.has_qualifier("SEQ", 0, "Z49")
2502    }
2503
2504    /// [77] Wenn SG8 SEQ+Z03 (Zähleinrichtungsdaten) CAV+Z30 (Identifikation/Nummer des Gerätes) nicht vorhanden
2505    fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
2506        let seq_segs = ctx.find_segments("SEQ");
2507        let found = seq_segs.iter().any(|s| {
2508            s.elements
2509                .first()
2510                .and_then(|e| e.first())
2511                .is_some_and(|v| ["Z03"].contains(&v.as_str()))
2512        });
2513        ConditionResult::from(!found)
2514    }
2515
2516    /// [78] Wenn SG4 STS+7++E02 (Transaktionsgrund: Einzug in Neuanlage) nicht vorhanden
2517    fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
2518        let sts_segs = ctx.find_segments("STS");
2519        let found = sts_segs.iter().any(|s| {
2520            s.elements
2521                .first()
2522                .and_then(|e| e.first())
2523                .is_some_and(|v| v == "7")
2524                && s.elements
2525                    .get(2)
2526                    .and_then(|e| e.first())
2527                    .is_some_and(|v| ["E02"].contains(&v.as_str()))
2528        });
2529        ConditionResult::from(!found)
2530    }
2531
2532    /// [83] Wenn in dieser SG4 das STS+E01++A08/ A16/ A99 (Status der Antwort) vorhanden
2533    fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
2534        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2535        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2536        let found = sts_segs.iter().any(|s| {
2537            s.elements
2538                .first()
2539                .and_then(|e| e.first())
2540                .is_some_and(|v| v == "E01")
2541                && s.elements
2542                    .get(2)
2543                    .and_then(|e| e.first())
2544                    .is_some_and(|v| ["A08", "A16", "A99"].contains(&v.as_str()))
2545        });
2546        ConditionResult::from(found)
2547    }
2548
2549    /// [84] Wenn SG4 STS+E01++A57 (Status der Antwort) vorhanden
2550    fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
2551        let sts_segs = ctx.find_segments("STS");
2552        let found = sts_segs.iter().any(|s| {
2553            s.elements
2554                .first()
2555                .and_then(|e| e.first())
2556                .is_some_and(|v| v == "E01")
2557                && s.elements
2558                    .get(2)
2559                    .and_then(|e| e.first())
2560                    .is_some_and(|v| ["A57"].contains(&v.as_str()))
2561        });
2562        ConditionResult::from(found)
2563    }
2564
2565    /// [85] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle M, Q, H oder J enthält
2566    fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
2567        let dtm_segs = ctx.find_segments("DTM");
2568        let found = dtm_segs.iter().any(|s| {
2569            s.elements
2570                .first()
2571                .and_then(|e| e.first())
2572                .is_some_and(|v| v == "Z01")
2573                && s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| {
2574                    v.chars()
2575                        .nth(3)
2576                        .is_some_and(|c| ['M', 'Q', 'H', 'J'].contains(&c))
2577                })
2578        });
2579        ConditionResult::from(found)
2580    }
2581
2582    /// [86] Wenn das RFF+Z50 (Termine der Marktlokation) aus dieser SG6 (Termine der Marktlokation) auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der...
2583    fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
2584        // Check: RFF+Z50 references same LOC as RFF+Z18 in another SG8
2585        ctx.groups_share_qualified_value(
2586            "RFF",
2587            0,
2588            "Z50",
2589            0,
2590            2,
2591            &["SG4", "SG6"],
2592            "RFF",
2593            0,
2594            2,
2595            &["SG4", "SG8"],
2596        )
2597    }
2598
2599    /// [87] Es ist ein Monatserster 0 Uhr (gem. deutscher Zeit) anzugeben
2600    fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
2601        // Check: date value is 1st of month at 00:00 (format 303: CCYYMMDDHHmm)
2602        let dtm_segs = ctx.find_segments("DTM");
2603        let found = dtm_segs.iter().any(|s| {
2604            s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| {
2605                v.len() >= 12
2606                    && &v[6..8] == "01"  // day = 01
2607                    && &v[8..12] == "0000" // time = 00:00
2608            })
2609        });
2610        if !found {
2611            // Check if any DTM has a non-matching date (definite False) vs no DTM at all (Unknown)
2612            if dtm_segs.is_empty() {
2613                return ConditionResult::Unknown;
2614            }
2615        }
2616        ConditionResult::from(found)
2617    }
2618
2619    /// [88] Wert muss identisch mit DE2380 aus dem SG4 DTM+157 (Änderung zum) sein
2620    fn evaluate_88(&self, ctx: &EvaluationContext) -> ConditionResult {
2621        // Check: value matches DTM+157 (Änderung zum)
2622        let dtm157 = ctx.find_segments_with_qualifier("DTM", 0, "157");
2623        if dtm157.is_empty() {
2624            return ConditionResult::Unknown;
2625        }
2626        // Condition is True when DTM+157 exists (the value check is structural)
2627        ConditionResult::True
2628    }
2629
2630    /// [89] Wenn im SG8 SEQ+Z01 (Daten der Marktlokation) mit identischer Zeitraum-ID im DE1050 wie in diesem SG8, das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+...
2631    // REVIEW: Checks whether any SG8 instance contains an SG10 child with CCI at elements[2][0]=ZA6 and CAV at elements[0][0]=E14. The condition text requires the Zeitraum-ID in DE1050 to match 'this SG8', but a per-group evaluation context is not available in the current API — the Zeitraum-ID cross-reference is therefore not enforced and is instead approximated as a message-wide check. The CCI qualifier position (elements[2][0]) is confirmed by the MIG reference for CCI segments in SG10. (medium confidence)
2632    fn evaluate_89(&self, ctx: &EvaluationContext) -> ConditionResult {
2633        let nav = match ctx.navigator {
2634            Some(n) => n,
2635            None => {
2636                return ctx.filtered_parent_child_has_qualifier(
2637                    &["SG4", "SG8"],
2638                    "SEQ",
2639                    0,
2640                    "Z01",
2641                    "SG10",
2642                    "CCI",
2643                    2,
2644                    "ZA6",
2645                )
2646            }
2647        };
2648        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2649        for i in 0..sg8_count {
2650            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2651            for j in 0..sg10_count {
2652                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2653                let has_cci_za6 = ccis.iter().any(|s| {
2654                    s.elements
2655                        .get(2)
2656                        .and_then(|e: &Vec<String>| e.first())
2657                        .is_some_and(|v: &String| v == "ZA6")
2658                });
2659                if !has_cci_za6 {
2660                    continue;
2661                }
2662                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2663                if cavs.iter().any(|s| {
2664                    s.elements
2665                        .first()
2666                        .and_then(|e: &Vec<String>| e.first())
2667                        .is_some_and(|v: &String| v == "E14")
2668                }) {
2669                    return ConditionResult::True;
2670                }
2671            }
2672        }
2673        ConditionResult::False
2674    }
2675
2676    /// [90] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) einmal mit CAV+E14 (TLP/TEP) und einmal mit CAV+E02 (SLP/SEP) in dieser SG8 vorhanden
2677    // REVIEW: Within each SG8, iterates all SG10 child instances. For each SG10 that has a CCI with elements[2][0]=ZA6, checks the CAV elements[0][0] value. Tracks whether E14 and E02 have both been seen within the same SG8. Returns True when both are found in a single SG8, satisfying the requirement that both Prognosegrundlage options appear 'in dieser SG8'. (medium confidence)
2678    fn evaluate_90(&self, ctx: &EvaluationContext) -> ConditionResult {
2679        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
2680        // scope reaches one; the message as before otherwise.
2681        if let Some(_scope) = ctx.enclosing_scope("SG8") {
2682            return ConditionResult::from(
2683                ctx.enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
2684                    .unwrap_or_default()
2685                    .iter()
2686                    .any(|segs| {
2687                        segs.iter()
2688                            .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
2689                            && segs
2690                                .iter()
2691                                .any(|s| s.id == "CAV" && s.get_element(0) == "E14")
2692                    })
2693                    && ctx
2694                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
2695                        .unwrap_or_default()
2696                        .iter()
2697                        .any(|segs| {
2698                            segs.iter()
2699                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
2700                                && segs
2701                                    .iter()
2702                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E02")
2703                        }),
2704            );
2705        }
2706        let nav = match ctx.navigator {
2707            Some(n) => n,
2708            None => return ConditionResult::Unknown,
2709        };
2710        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2711        for i in 0..sg8_count {
2712            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2713            let mut has_za6_e14 = false;
2714            let mut has_za6_e02 = false;
2715            for j in 0..sg10_count {
2716                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2717                let has_cci_za6 = ccis.iter().any(|s| {
2718                    s.elements
2719                        .get(2)
2720                        .and_then(|e: &Vec<String>| e.first())
2721                        .is_some_and(|v: &String| v == "ZA6")
2722                });
2723                if !has_cci_za6 {
2724                    continue;
2725                }
2726                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2727                for cav in &cavs {
2728                    if let Some(code) = cav.elements.first().and_then(|e: &Vec<String>| e.first()) {
2729                        if code == "E14" {
2730                            has_za6_e14 = true;
2731                        }
2732                        if code == "E02" {
2733                            has_za6_e02 = true;
2734                        }
2735                    }
2736                }
2737            }
2738            if has_za6_e14 && has_za6_e02 {
2739                return ConditionResult::True;
2740            }
2741        }
2742        ConditionResult::False
2743    }
2744
2745    /// [91] Wenn nicht SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) einmal mit CAV+E14 (TLP/ TEP) und einmal mit CAV+E02 (SLP/SEP) in dieser SG8 vorhanden
2746    // REVIEW: Logical negation of condition 90. Condition 91 is explicitly defined as 'Wenn NICHT (CCI+++ZA6 einmal mit CAV+E14 und einmal mit CAV+E02 in dieser SG8 vorhanden)', which is the exact complement of condition 90. Preserving Unknown propagation handles the no-navigator fallback case correctly. (medium confidence)
2747    fn evaluate_91(&self, ctx: &EvaluationContext) -> ConditionResult {
2748        match self.evaluate_90(ctx) {
2749            ConditionResult::True => ConditionResult::False,
2750            ConditionResult::False => ConditionResult::True,
2751            ConditionResult::Unknown => ConditionResult::Unknown,
2752        }
2753    }
2754
2755    /// [92] Wenn Wert innerhalb SG bzw. Segment geändert wird
2756    fn evaluate_92(&self, _ctx: &EvaluationContext) -> ConditionResult {
2757        // TODO: implement
2758        ConditionResult::Unknown
2759    }
2760
2761    /// [93] Erlaubte Codes aus der PRICAT BGM+Z32 (Preisblatt Messstellenbetrieb) des verantwortlichen MSB
2762    fn evaluate_93(&self, _ctx: &EvaluationContext) -> ConditionResult {
2763        // TODO: implement
2764        ConditionResult::Unknown
2765    }
2766
2767    /// [94] Wenn ein Segment innerhalb der SG vorhanden
2768    fn evaluate_94(&self, _ctx: &EvaluationContext) -> ConditionResult {
2769        // Structural: segment exists within SG (always true for valid messages)
2770        ConditionResult::True
2771    }
2772
2773    /// [95] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) CAV+ZE5 (E-Mobilität) vorhanden
2774    fn evaluate_95(&self, ctx: &EvaluationContext) -> ConditionResult {
2775        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
2776        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
2777        let has_cci = cci_segs.iter().any(|s| {
2778            s.elements
2779                .first()
2780                .and_then(|e| e.first())
2781                .is_some_and(|v| v == "Z17")
2782        });
2783        let cav_segs = ctx.scoped_find_segments_in("SG10", "CAV");
2784        let has_cav = cav_segs.iter().any(|s| {
2785            s.elements
2786                .first()
2787                .and_then(|e| e.first())
2788                .is_some_and(|v| ["ZE5"].contains(&v.as_str()))
2789        });
2790        ConditionResult::from(has_cci && has_cav)
2791    }
2792
2793    /// [96] Wenn SG4 STS+7++xxx+ZAP (Transaktionsgrundergänzung ruhende Marktlokation) vorhanden
2794    fn evaluate_96(&self, ctx: &EvaluationContext) -> ConditionResult {
2795        let sts_segs = ctx.find_segments("STS");
2796        let found = sts_segs.iter().any(|s| {
2797            s.elements
2798                .first()
2799                .and_then(|e| e.first())
2800                .is_some_and(|v| v == "7")
2801                && s.elements
2802                    .get(3)
2803                    .and_then(|e| e.first())
2804                    .is_some_and(|v| ["ZAP"].contains(&v.as_str()))
2805        });
2806        ConditionResult::from(found)
2807    }
2808
2809    /// [97] Wenn in einem SG10 CAV+ZH9 DE7110 der Code der Produkteigenschaft (Wertebereich) 9991000002420 (Marktprämie) vorhanden ist
2810    fn evaluate_97(&self, ctx: &EvaluationContext) -> ConditionResult {
2811        // Check: CAV+ZH9 has code 9991000002420 (Marktprämie)
2812        let cav_segs = ctx.find_segments("CAV");
2813        let found = cav_segs.iter().any(|s| {
2814            s.elements
2815                .first()
2816                .and_then(|e| e.first())
2817                .is_some_and(|v| v == "ZH9")
2818                && s.elements
2819                    .first()
2820                    .and_then(|e| e.get(1))
2821                    .is_some_and(|v| v == "9991000002420")
2822        });
2823        ConditionResult::from(found)
2824    }
2825
2826    /// [98] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle NB
2827    fn evaluate_98(&self, ctx: &EvaluationContext) -> ConditionResult {
2828        ctx.external.evaluate("sender_role_check")
2829    }
2830
2831    /// [99] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle ÜNB
2832    fn evaluate_99(&self, ctx: &EvaluationContext) -> ConditionResult {
2833        ctx.external.evaluate("sender_role_check")
2834    }
2835
2836    /// [100] Wenn SG10 CAV+TLS/ TES/ BIT/ GET/ GAT/ SOT/ WNT/ WFT/ WAT vorhanden
2837    fn evaluate_100(&self, ctx: &EvaluationContext) -> ConditionResult {
2838        let cav_segs = ctx.find_segments("CAV");
2839        let found = cav_segs.iter().any(|s| {
2840            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2841                [
2842                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
2843                ]
2844                .contains(&v.as_str())
2845            })
2846        });
2847        ConditionResult::from(found)
2848    }
2849
2850    /// [101] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E14/ Z36 (TLP/ TEP/ TEP mit Ref.messung) in dieser SG8 vorhanden
2851    fn evaluate_101(&self, ctx: &EvaluationContext) -> ConditionResult {
2852        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2853        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
2854        let found = cci_segs.iter().any(|s| {
2855            s.elements
2856                .get(2)
2857                .and_then(|e| e.first())
2858                .is_some_and(|v| v == "ZA6")
2859        });
2860        ConditionResult::from(found)
2861    }
2862
2863    /// [102] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+Z36 (TEP mit Ref.messung) in dieser SG8 vorhanden
2864    fn evaluate_102(&self, ctx: &EvaluationContext) -> ConditionResult {
2865        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2866        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
2867        let found = cci_segs.iter().any(|s| {
2868            s.elements
2869                .get(2)
2870                .and_then(|e| e.first())
2871                .is_some_and(|v| v == "ZA6")
2872        });
2873        ConditionResult::from(found)
2874    }
2875
2876    /// [103] Wenn SG9 QTY+265 (Arbeit / Leistung für tagesparameterabhängig e Marktlokation: Veranschlagte Jahresmenge Gesamt) in dieser SG8 vorhanden
2877    fn evaluate_103(&self, ctx: &EvaluationContext) -> ConditionResult {
2878        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2879        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2880        let found = qty_segs.iter().any(|s| {
2881            s.elements
2882                .first()
2883                .and_then(|e| e.first())
2884                .is_some_and(|v| v == "265")
2885        });
2886        ConditionResult::from(found)
2887    }
2888
2889    /// [104] Wenn SG9 QTY+Z08 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: angepasste elektrische Arbeit nach Anhang D) in dieser SG8 vorhanden
2890    fn evaluate_104(&self, ctx: &EvaluationContext) -> ConditionResult {
2891        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2892        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2893        let found = qty_segs.iter().any(|s| {
2894            s.elements
2895                .first()
2896                .and_then(|e| e.first())
2897                .is_some_and(|v| v == "Z08")
2898        });
2899        ConditionResult::from(found)
2900    }
2901
2902    /// [105] Wenn SG9 QTY+Z10 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: Leistung der Marktlokation) in dieser SG8 vorhanden
2903    fn evaluate_105(&self, ctx: &EvaluationContext) -> ConditionResult {
2904        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2905        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2906        let found = qty_segs.iter().any(|s| {
2907            s.elements
2908                .first()
2909                .and_then(|e| e.first())
2910                .is_some_and(|v| v == "Z10")
2911        });
2912        ConditionResult::from(found)
2913    }
2914
2915    /// [106] Wenn in dieser SG8 SEQ+Z01 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
2916    fn evaluate_106(&self, ctx: &EvaluationContext) -> ConditionResult {
2917        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2918        // Check: in SG8 with SEQ+Z01, SG10 has CCI+++ZA6 and CAV+E02
2919        ctx.this_group_parent_child_has_qualifier(
2920            &["SG4", "SG8"],
2921            "SEQ",
2922            0,
2923            "Z01",
2924            "SG10",
2925            "CAV",
2926            0,
2927            "E02",
2928        )
2929    }
2930
2931    /// [107] Wenn in derselben SG8 das SG10 CAV+SLS/ SES/ BIP/ GEP/ GAP/ SOP/ WNP/ WFP/ WAP vorhanden
2932    fn evaluate_107(&self, ctx: &EvaluationContext) -> ConditionResult {
2933        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2934        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
2935        let found = cav_segs.iter().any(|s| {
2936            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2937                [
2938                    "SLS", "SES", "BIP", "GEP", "GAP", "SOP", "WNP", "WFP", "WAP",
2939                ]
2940                .contains(&v.as_str())
2941            })
2942        });
2943        ConditionResult::from(found)
2944    }
2945
2946    /// [108] Wenn in derselben SG8 (Netznutzungsabrechnungsdaten der Marktlokation) eine Gruppen-/Artikel-ID im PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5 beginnt, vorhanden
2947    // HAND-EDITED — do not let generate-conditions regenerate this function
2948    // without first confirming prompt.rs B6 SCOPE RULES section is in the
2949    // system prompt. Regeneration without that guidance would emit
2950    // ctx.has_qualifier (message-wide) and silently reintroduce the
2951    // per-instance bug this function fixes.
2952    // See: docs/plans/2026-04-23-ahb-codelist-and-per-instance-plan.md B7.
2953    fn evaluate_108(&self, ctx: &EvaluationContext) -> ConditionResult {
2954        // [108] Wenn in derselben SG8 ein PIA+Z02 mit einem Code aus
2955        // 1-08-1/2/3/4/5 vorhanden. Scope-aware: narrows to the enclosing
2956        // SG8 instance when GroupScope is set; falls back to message-wide.
2957        const PREFIXES: &[&str] = &["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"];
2958        let found = ctx
2959            .scoped_find_segments_in("SG8", "PIA")
2960            .iter()
2961            .filter(|s| {
2962                s.elements
2963                    .first()
2964                    .and_then(|e| e.first())
2965                    .is_some_and(|q| q == "Z02")
2966            })
2967            .any(|s| {
2968                s.elements
2969                    .get(1)
2970                    .and_then(|e| e.first())
2971                    .is_some_and(|v| PREFIXES.iter().any(|p| v.starts_with(p)))
2972            });
2973        ConditionResult::from(found)
2974    }
2975
2976    /// [110] Wenn SG10 CAV+LGS/ EGS/ BIL/ GEL/ GAL/ SOL/ WNL/ WFL / WAL vorhanden
2977    fn evaluate_110(&self, ctx: &EvaluationContext) -> ConditionResult {
2978        let cav_segs = ctx.find_segments("CAV");
2979        let found = cav_segs.iter().any(|s| {
2980            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2981                [
2982                    "LGS", "EGS", "BIL", "GEL", "GAL", "SOL", "WNL", "WFL", "WAL",
2983                ]
2984                .contains(&v.as_str())
2985            })
2986        });
2987        ConditionResult::from(found)
2988    }
2989
2990    /// [111] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) nicht vorhanden
2991    fn evaluate_111(&self, ctx: &EvaluationContext) -> ConditionResult {
2992        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
2993        // scope reaches one; the message as before otherwise.
2994        if let Some(scope) = ctx.enclosing_scope("SG8") {
2995            return ConditionResult::from(
2996                ctx.find_segments_at(scope, "SEQ")
2997                    .iter()
2998                    .any(|s| matches!(s.get_element(0), "Z59"))
2999                    && !ctx
3000                        .find_segments_at(scope, "PIA")
3001                        .iter()
3002                        .any(|s| s.get_element(0) == "5"),
3003            );
3004        }
3005        ctx.any_group_has_qualifier_without("SEQ", 0, "Z59", "PIA", 0, "5", &["SG4", "SG8"])
3006    }
3007
3008    /// [112] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das SG10 CCI+11 (Details zum Produkt der Marktlokation) nicht vorhanden
3009    fn evaluate_112(&self, ctx: &EvaluationContext) -> ConditionResult {
3010        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3011        // scope reaches one; the message as before otherwise.
3012        if let Some(scope) = ctx.enclosing_scope("SG8") {
3013            return ConditionResult::from(
3014                ctx.find_segments_at(scope, "SEQ")
3015                    .iter()
3016                    .any(|s| matches!(s.get_element(0), "Z59"))
3017                    && !ctx
3018                        .find_segments_at(scope, "CCI")
3019                        .iter()
3020                        .any(|s| s.get_element(0) == "11"),
3021            );
3022        }
3023        let result = ctx.filtered_parent_child_has_qualifier(
3024            &["SG4", "SG8"],
3025            "SEQ",
3026            0,
3027            "Z59",
3028            "SG10",
3029            "CCI",
3030            0,
3031            "11",
3032        );
3033        match result {
3034            ConditionResult::True => ConditionResult::False,
3035            ConditionResult::False => ConditionResult::True,
3036            ConditionResult::Unknown => ConditionResult::Unknown,
3037        }
3038    }
3039
3040    /// [113] Wenn es sich um eine Antwort auf die Bestellung einer Zählzeit handelt
3041    fn evaluate_113(&self, ctx: &EvaluationContext) -> ConditionResult {
3042        // Antwort auf Bestellung einer Zählzeit — depends on PID context
3043        ctx.external.evaluate("counting_time_order_response")
3044    }
3045
3046    /// [114] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E06 (Niederspannung) in dieser SG vorhanden
3047    // REVIEW: Checks that CCI+E03 (Spannungsebene, elements[0][0]='E03' per AHB hint) and CAV+E06 (Niederspannung, elements[0][0]='E06') co-occur in the same SG10 child instance of some SG8. Uses navigator to iterate SG8→SG10 pairs and verify both segments within the same SG10 instance. Confidence medium because navigator availability is runtime-dependent and fallback is message-wide only. (medium confidence)
3048    fn evaluate_114(&self, ctx: &EvaluationContext) -> ConditionResult {
3049        let nav = match ctx.navigator {
3050            Some(n) => n,
3051            None => {
3052                return match (
3053                    ctx.has_qualifier("CCI", 0, "E03"),
3054                    ctx.has_qualifier("CAV", 0, "E06"),
3055                ) {
3056                    (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
3057                    (ConditionResult::False, _) | (_, ConditionResult::False) => {
3058                        ConditionResult::False
3059                    }
3060                    _ => ConditionResult::Unknown,
3061                };
3062            }
3063        };
3064        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3065        for i in 0..sg8_count {
3066            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3067            for j in 0..sg10_count {
3068                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3069                let has_cci_e03 = ccis.iter().any(|s| {
3070                    s.elements
3071                        .first()
3072                        .and_then(|e: &Vec<String>| e.first())
3073                        .is_some_and(|v: &String| v == "E03")
3074                });
3075                if has_cci_e03 {
3076                    let cavs =
3077                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3078                    if cavs.iter().any(|s| {
3079                        s.elements
3080                            .first()
3081                            .and_then(|e: &Vec<String>| e.first())
3082                            .is_some_and(|v: &String| v == "E06")
3083                    }) {
3084                        return ConditionResult::True;
3085                    }
3086                }
3087            }
3088        }
3089        ConditionResult::False
3090    }
3091
3092    /// [115] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E05 (Mittelspannung) in dieser SG vorhanden
3093    fn evaluate_115(&self, ctx: &EvaluationContext) -> ConditionResult {
3094        let cci_segs = ctx.find_segments("CCI");
3095        let has_cci = cci_segs.iter().any(|s| {
3096            s.elements
3097                .first()
3098                .and_then(|e| e.first())
3099                .is_some_and(|v| v == "E03")
3100        });
3101        let cav_segs = ctx.find_segments("CAV");
3102        let has_cav = cav_segs.iter().any(|s| {
3103            s.elements
3104                .first()
3105                .and_then(|e| e.first())
3106                .is_some_and(|v| v == "E05")
3107        });
3108        ConditionResult::from(has_cci && has_cav)
3109    }
3110
3111    /// [116] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E04 (Hochspannung) in dieser SG vorhanden
3112    fn evaluate_116(&self, ctx: &EvaluationContext) -> ConditionResult {
3113        let cci_segs = ctx.find_segments("CCI");
3114        let has_cci = cci_segs.iter().any(|s| {
3115            s.elements
3116                .first()
3117                .and_then(|e| e.first())
3118                .is_some_and(|v| v == "E03")
3119        });
3120        let cav_segs = ctx.find_segments("CAV");
3121        let has_cav = cav_segs.iter().any(|s| {
3122            s.elements
3123                .first()
3124                .and_then(|e| e.first())
3125                .is_some_and(|v| v == "E04")
3126        });
3127        ConditionResult::from(has_cci && has_cav)
3128    }
3129
3130    /// [117] Wenn das RFF+Z18 (Marktlokation) aus dieser SG8 auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der Marktlokation), in dem SG10 CCI+++ZA6 (...
3131    // REVIEW: Full condition requires: (1) current SG8 has RFF+Z18=X, (2) another SG8 with SEQ+Z01 also has RFF+Z18=X, (3) that SG8 has SG10 with CCI elements[2][0]='ZA6' AND CAV elements[0][0]='E14'. Implementation simplifies to checking whether any SG8 has SG10 with both CCI+++ZA6 and CAV+E14 co-occurring in the same SG10 instance. The cross-SG RFF+Z18 value correlation and SEQ+Z01 parent check are omitted because the navigator API only exposes child-group segments (SG10 within SG8), not SG8's own entry segments. Medium confidence due to this structural limitation. (medium confidence)
3132    fn evaluate_117(&self, ctx: &EvaluationContext) -> ConditionResult {
3133        let nav = match ctx.navigator {
3134            Some(n) => n,
3135            None => return ConditionResult::Unknown,
3136        };
3137        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3138        for i in 0..sg8_count {
3139            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3140            for j in 0..sg10_count {
3141                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3142                let has_cci_za6 = ccis.iter().any(|s| {
3143                    s.elements
3144                        .get(2)
3145                        .and_then(|e: &Vec<String>| e.first())
3146                        .is_some_and(|v: &String| v == "ZA6")
3147                });
3148                if has_cci_za6 {
3149                    let cavs =
3150                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3151                    if cavs.iter().any(|s| {
3152                        s.elements
3153                            .first()
3154                            .and_then(|e: &Vec<String>| e.first())
3155                            .is_some_and(|v: &String| v == "E14")
3156                    }) {
3157                        return ConditionResult::True;
3158                    }
3159                }
3160            }
3161        }
3162        ConditionResult::False
3163    }
3164
3165    /// [118] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/ SEP) vorhanden
3166    fn evaluate_118(&self, ctx: &EvaluationContext) -> ConditionResult {
3167        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3168        // scope reaches one; the message as before otherwise.
3169        if let Some(scope) = ctx.enclosing_scope("SG8") {
3170            return ConditionResult::from(
3171                ctx.find_segments_at(scope, "SEQ")
3172                    .iter()
3173                    .any(|s| matches!(s.get_element(0), "Z98"))
3174                    && ctx
3175                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
3176                        .unwrap_or_default()
3177                        .iter()
3178                        .any(|segs| {
3179                            segs.iter()
3180                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
3181                                && segs
3182                                    .iter()
3183                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E02")
3184                        }),
3185            );
3186        }
3187        let has_cci = ctx.filtered_parent_child_has_qualifier(
3188            &["SG4", "SG8"],
3189            "SEQ",
3190            0,
3191            "Z98",
3192            "SG10",
3193            "CCI",
3194            2,
3195            "ZA6",
3196        );
3197        if has_cci != ConditionResult::True {
3198            return has_cci;
3199        }
3200        ctx.filtered_parent_child_has_qualifier(
3201            &["SG4", "SG8"],
3202            "SEQ",
3203            0,
3204            "Z98",
3205            "SG10",
3206            "CAV",
3207            0,
3208            "E02",
3209        )
3210    }
3211
3212    /// [119] Wenn in der SG8 SEQ+Z01/ Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
3213    fn evaluate_119(&self, ctx: &EvaluationContext) -> ConditionResult {
3214        // Check: in SG8 with SEQ+Z01 or Z98, SG10 has CCI+++ZA6 and CAV+E02
3215        let r1 = ctx.filtered_parent_child_has_qualifier(
3216            &["SG4", "SG8"],
3217            "SEQ",
3218            0,
3219            "Z01",
3220            "SG10",
3221            "CAV",
3222            0,
3223            "E02",
3224        );
3225        if r1 == ConditionResult::True {
3226            return ConditionResult::True;
3227        }
3228        ctx.filtered_parent_child_has_qualifier(
3229            &["SG4", "SG8"],
3230            "SEQ",
3231            0,
3232            "Z98",
3233            "SG10",
3234            "CAV",
3235            0,
3236            "E02",
3237        )
3238    }
3239
3240    /// [120] Wenn von NB Abtretungserklärung benötigt wird
3241    fn evaluate_120(&self, _ctx: &EvaluationContext) -> ConditionResult {
3242        // TODO: implement
3243        ConditionResult::Unknown
3244    }
3245
3246    /// [121] Wenn in dem SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) das SG8 RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden
3247    fn evaluate_121(&self, ctx: &EvaluationContext) -> ConditionResult {
3248        let z03 =
3249            ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"]);
3250        if z03 == ConditionResult::True {
3251            return ConditionResult::True;
3252        }
3253        ctx.any_group_has_qualifier_without("SEQ", 0, "ZF5", "RFF", 0, "Z14", &["SG4", "SG8"])
3254    }
3255
3256    /// [122] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++E03 (Spannungsebene der Marktlokation) CAV+E06 (Niederspannung) vorhanden
3257    // REVIEW: Uses filtered_parent_child_has_qualifier to find SG8 instances where SEQ+Z98 has an SG10 child with CCI at elements[2]='E03' (Spannungsebene der Marktlokation). Then confirms CAV+E06 (Niederspannung) exists in an SG8 group. The CAV check is group-scoped rather than strictly same-SG10-instance because the navigator API does not directly expose parent-group (SG8) segment access alongside child-group (SG10) checks in one call. (medium confidence)
3258    fn evaluate_122(&self, ctx: &EvaluationContext) -> ConditionResult {
3259        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3260        // scope reaches one; the message as before otherwise.
3261        if let Some(scope) = ctx.enclosing_scope("SG8") {
3262            return ConditionResult::from(
3263                ctx.find_segments_at(scope, "SEQ")
3264                    .iter()
3265                    .any(|s| matches!(s.get_element(0), "Z98"))
3266                    && ctx
3267                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
3268                        .unwrap_or_default()
3269                        .iter()
3270                        .any(|segs| {
3271                            segs.iter()
3272                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "E03")
3273                                && segs
3274                                    .iter()
3275                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E06")
3276                        }),
3277            );
3278        }
3279        // Check: in SG8 with SEQ+Z98, SG10 child has CCI+++E03 (Spannungsebene, elements[2][0]="E03")
3280        let has_cci_e03 = ctx.filtered_parent_child_has_qualifier(
3281            &["SG4", "SG8"],
3282            "SEQ",
3283            0,
3284            "Z98",
3285            "SG10",
3286            "CCI",
3287            2,
3288            "E03",
3289        );
3290        if has_cci_e03 != ConditionResult::True {
3291            return has_cci_e03;
3292        }
3293        // Also verify CAV+E06 (Niederspannung, elements[0][0]="E06") exists in the same SG8 scope
3294        ctx.any_group_has_qualifier("CAV", 0, "E06", &["SG4", "SG8"])
3295    }
3296
3297    /// [123] Wenn noch mindestens eine weitere SG8 SEQ+Z20 (OBIS-Daten der Zähleinrichtung Smartmeter-Gateway) mit dem SG8 RFF+MG (Gerätenummer des Zählers) auf die gleiche Identifikation/Nummer des Gerätes...
3298    // REVIEW: Scans segments linearly to collect RFF+MG device IDs from each SG8 group with SEQ+Z20 (OBIS-Daten der Zähleinrichtung/Smartmeter-Gateway). A new SEQ starts a new SG8 context; RFF+MG within that context records the device number. Returns True if any device ID appears in 2+ different SG8 groups (indicating multiple OBIS datasets reference the same device). Linear scan is used because the navigator API does not expose a direct method to retrieve segments within a parent SG8 instance by absolute index. (medium confidence)
3299    fn evaluate_123(&self, ctx: &EvaluationContext) -> ConditionResult {
3300        struct Sg8Z20Entry {
3301            mg_id: Option<String>,
3302        }
3303        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3304        let mut current: Option<Sg8Z20Entry> = None;
3305        for seg in ctx.segments {
3306            match seg.id.as_str() {
3307                "SEQ" => {
3308                    if let Some(entry) = current.take() {
3309                        entries.push(entry);
3310                    }
3311                    if seg
3312                        .elements
3313                        .first()
3314                        .and_then(|e: &Vec<String>| e.first())
3315                        .is_some_and(|v: &String| v == "Z20")
3316                    {
3317                        current = Some(Sg8Z20Entry { mg_id: None });
3318                    }
3319                }
3320                "RFF" if current.is_some() => {
3321                    if seg
3322                        .elements
3323                        .first()
3324                        .and_then(|e: &Vec<String>| e.first())
3325                        .is_some_and(|v: &String| v == "MG")
3326                    {
3327                        if let Some(ref mut entry) = current {
3328                            if entry.mg_id.is_none() {
3329                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3330                            }
3331                        }
3332                    }
3333                }
3334                _ => {}
3335            }
3336        }
3337        if let Some(entry) = current {
3338            entries.push(entry);
3339        }
3340        let ids: Vec<&str> = entries
3341            .iter()
3342            .filter_map(|e| e.mg_id.as_deref().filter(|s| !s.is_empty()))
3343            .collect();
3344        for i in 0..ids.len() {
3345            for j in (i + 1)..ids.len() {
3346                if ids[i] == ids[j] {
3347                    return ConditionResult::True;
3348                }
3349            }
3350        }
3351        ConditionResult::False
3352    }
3353
3354    /// [124] Wenn noch mindestens eine weitere SG8 SEQ+Z20 (OBIS-Daten der Zähleinrichtung Smartmeter-Gateway) mit derselben Zeitraum-ID aus dem DE1050 dieser SG8 mit dem SG8 RFF+MG (Gerätenummer des Zählers...
3355    // REVIEW: Extends condition 123 by also requiring that the matching SG8 groups share the same Zeitraum-ID (from SEQ.elements[1][0] = DE1050 / C286.d1050). Both the Zeitraum-ID and the RFF+MG device number must be identical across at least two different SG8 instances with SEQ+Z20 for the condition to be True. (medium confidence)
3356    fn evaluate_124(&self, ctx: &EvaluationContext) -> ConditionResult {
3357        struct Sg8Z20Entry {
3358            zeitraum_id: String,
3359            mg_id: Option<String>,
3360        }
3361        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3362        let mut current: Option<Sg8Z20Entry> = None;
3363        for seg in ctx.segments {
3364            match seg.id.as_str() {
3365                "SEQ" => {
3366                    if let Some(entry) = current.take() {
3367                        entries.push(entry);
3368                    }
3369                    if seg
3370                        .elements
3371                        .first()
3372                        .and_then(|e: &Vec<String>| e.first())
3373                        .is_some_and(|v: &String| v == "Z20")
3374                    {
3375                        let zeitraum_id = seg
3376                            .elements
3377                            .get(1)
3378                            .and_then(|e: &Vec<String>| e.first())
3379                            .cloned()
3380                            .unwrap_or_default();
3381                        current = Some(Sg8Z20Entry {
3382                            zeitraum_id,
3383                            mg_id: None,
3384                        });
3385                    }
3386                }
3387                "RFF" if current.is_some() => {
3388                    if seg
3389                        .elements
3390                        .first()
3391                        .and_then(|e: &Vec<String>| e.first())
3392                        .is_some_and(|v: &String| v == "MG")
3393                    {
3394                        if let Some(ref mut entry) = current {
3395                            if entry.mg_id.is_none() {
3396                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3397                            }
3398                        }
3399                    }
3400                }
3401                _ => {}
3402            }
3403        }
3404        if let Some(entry) = current {
3405            entries.push(entry);
3406        }
3407        for i in 0..entries.len() {
3408            for j in (i + 1)..entries.len() {
3409                let ei = &entries[i];
3410                let ej = &entries[j];
3411                if !ei.zeitraum_id.is_empty()
3412                    && ei.zeitraum_id == ej.zeitraum_id
3413                    && ei.mg_id.is_some()
3414                    && !ei.mg_id.as_deref().unwrap_or("").is_empty()
3415                    && ei.mg_id == ej.mg_id
3416                {
3417                    return ConditionResult::True;
3418                }
3419            }
3420        }
3421        ConditionResult::False
3422    }
3423
3424    /// [125] Wenn in derselben SG8 SEQ+Z76/ ZC5/ ZC6 (Messstellenbetriebsabrechnungsdaten der Marktlokation) im DE4347 des PIA Z02 (Gruppenartikel-ID / Artikel-ID) vorhanden
3425    fn evaluate_125(&self, ctx: &EvaluationContext) -> ConditionResult {
3426        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3427        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
3428        let found = seq_segs.iter().any(|s| {
3429            s.elements
3430                .first()
3431                .and_then(|e| e.first())
3432                .is_some_and(|v| ["Z76", "ZC5", "ZC6"].contains(&v.as_str()))
3433        });
3434        ConditionResult::from(found)
3435    }
3436
3437    /// [126] Es ist der Wert einzutragen, der sich aus der Wiederholungshäufigkeit des SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: Gültige Daten/ Keine Daten) ergibt. Bedeutet: Das erste SG6 RFF+Z49/ Z53...
3438    fn evaluate_126(&self, ctx: &EvaluationContext) -> ConditionResult {
3439        // Check: RFF+Z49 or Z53 repetition count determines value
3440        // Approximate: check if RFF+Z49 or Z53 exists (counter is structural)
3441        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z49").len()
3442            + ctx.find_segments_with_qualifier("RFF", 0, "Z53").len();
3443        ConditionResult::from(count > 0)
3444    }
3445
3446    /// [127] Es ist der Wert einzutragen, der sich aus der Wiederholungshäufigkeit des SG6 RFF+Z48/ Z55 (Verwendungszeitraum der Daten: Erwartete Daten/ Keine Daten erwartet) ergibt. Bedeutet: Das erste SG6  R...
3447    fn evaluate_127(&self, ctx: &EvaluationContext) -> ConditionResult {
3448        // Check: RFF+Z48 or Z55 repetition count determines value
3449        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z48").len()
3450            + ctx.find_segments_with_qualifier("RFF", 0, "Z55").len();
3451        ConditionResult::from(count > 0)
3452    }
3453
3454    /// [128] Es ist der Wert einzutragen, der sich aus der Wiederholungshäufigkeit des SG6 RFF+Z47/ Z54 (Verwendungszeitraum der Daten: Im System vorhandene Daten/ Im System keine Daten vorhanden) ergibt. Bede...
3455    fn evaluate_128(&self, ctx: &EvaluationContext) -> ConditionResult {
3456        // Check: RFF+Z47 or Z54 repetition count determines value
3457        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z47").len()
3458            + ctx.find_segments_with_qualifier("RFF", 0, "Z54").len();
3459        ConditionResult::from(count > 0)
3460    }
3461
3462    /// [129] Innerhalb eines SG4 IDE müssen alle DE3225 der SG5 LOC+Z16 (Marktlokation) den identischen Wert enthalten
3463    fn evaluate_129(&self, ctx: &EvaluationContext) -> ConditionResult {
3464        // Check: all LOC+Z16 DE3225 values are identical
3465        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z16");
3466        if loc_segs.len() <= 1 {
3467            return ConditionResult::True;
3468        }
3469        let first_val = loc_segs[0].elements.get(1).and_then(|e| e.first());
3470        let all_same = loc_segs
3471            .iter()
3472            .all(|s| s.elements.get(1).and_then(|e| e.first()) == first_val);
3473        ConditionResult::from(all_same)
3474    }
3475
3476    /// [130] Wenn an Messlokation vorhanden
3477    fn evaluate_130(&self, _ctx: &EvaluationContext) -> ConditionResult {
3478        // TODO: implement
3479        ConditionResult::Unknown
3480    }
3481
3482    /// [131] Wenn dieses DTM+Z25 (Verwendung der Daten ab) im SG6 RFF (Verwendungszeitraum der Daten) mit der Zeitraum ID \"1\" im DE1156 gekennzeichnet ist, muss das Datum in diesem DE2380 der direkt auf DTM+1...
3483    fn evaluate_131(&self, ctx: &EvaluationContext) -> ConditionResult {
3484        // Check: DTM+Z25 is in SG6 RFF with Zeitraum-ID "1" in DE1156
3485        let has_zeitraum_id_1 = ctx.find_segments("RFF").iter().any(|s| {
3486            s.elements
3487                .first()
3488                .and_then(|e| e.get(2))
3489                .is_some_and(|v| v == "1")
3490        });
3491        ConditionResult::from(has_zeitraum_id_1)
3492    }
3493
3494    /// [132] Wenn dieses DTM+Z25 (Verwendung der Daten ab) im SG6 RFF (Verwendungszeitraum der Daten) mit der Zeitraum ID "1" im DE1156 gekennzeichnet ist, muss das Datum in diesem DE2380 der direkt auf DTM+137...
3495    // REVIEW: Validates that when RFF in SG6 has Zeitraum-ID '1' (elements[0][2] = DE1156), the DTM+Z25 date must equal the day immediately following DTM+137 (Nachrichtendatum) at 00:00. Parses the YYYYMMDD portion of DTM+137, computes next calendar day with correct month/year rollover, then checks that DTM+Z25's first 12 characters match YYYYMMDDHHMM with HHMM=0000. Note: German timezone offset (CET/CEST) validation is not performed — this checks the stored datetime value directly, which is the common encoding in EDIFACT 303 format for this use case. (medium confidence)
3496    fn evaluate_132(&self, ctx: &EvaluationContext) -> ConditionResult {
3497        // Condition applies only when RFF in SG6 has Zeitraum-ID "1" in DE1156 (elements[0][2])
3498        let has_zeitraum_id_1 = ctx.find_segments("RFF").iter().any(|s| {
3499            s.elements
3500                .first()
3501                .and_then(|e| e.get(2))
3502                .is_some_and(|v: &String| v == "1")
3503        });
3504        if !has_zeitraum_id_1 {
3505            return ConditionResult::False;
3506        }
3507        // Get message date from DTM+137 (format 303: CCYYMMDDHHmm...)
3508        let msg_date_str = match ctx
3509            .find_segments_with_qualifier("DTM", 0, "137")
3510            .into_iter()
3511            .next()
3512        {
3513            Some(seg) => match seg.elements.first().and_then(|e| e.get(1)) {
3514                Some(v) if v.len() >= 8 => v.clone(),
3515                _ => return ConditionResult::Unknown,
3516            },
3517            None => return ConditionResult::Unknown,
3518        };
3519        // Get DTM+Z25 (Verwendung der Daten ab) value
3520        let usage_date_str = match ctx
3521            .find_segments_with_qualifier("DTM", 0, "Z25")
3522            .into_iter()
3523            .next()
3524        {
3525            Some(seg) => match seg.elements.first().and_then(|e| e.get(1)) {
3526                Some(v) if v.len() >= 12 => v.clone(),
3527                _ => return ConditionResult::Unknown,
3528            },
3529            None => return ConditionResult::Unknown,
3530        };
3531        // Parse YYYYMMDD from DTM+137 value
3532        let year: u32 = match msg_date_str[..4].parse() {
3533            Ok(v) => v,
3534            Err(_) => return ConditionResult::Unknown,
3535        };
3536        let month: u32 = match msg_date_str[4..6].parse() {
3537            Ok(v) => v,
3538            Err(_) => return ConditionResult::Unknown,
3539        };
3540        let day: u32 = match msg_date_str[6..8].parse() {
3541            Ok(v) => v,
3542            Err(_) => return ConditionResult::Unknown,
3543        };
3544        let days_in_month: u32 = match month {
3545            1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
3546            4 | 6 | 9 | 11 => 30,
3547            2 => {
3548                if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) {
3549                    29
3550                } else {
3551                    28
3552                }
3553            }
3554            _ => return ConditionResult::Unknown,
3555        };
3556        let (next_year, next_month, next_day): (u32, u32, u32) = if day >= days_in_month {
3557            if month == 12 {
3558                (year + 1, 1, 1)
3559            } else {
3560                (year, month + 1, 1)
3561            }
3562        } else {
3563            (year, month, day + 1)
3564        };
3565        // Expected DTM+Z25: next day at 00:00 — format 303 prefix YYYYMMDDHHMM = 12 chars
3566        let expected_prefix = format!("{:04}{:02}{:02}0000", next_year, next_month, next_day);
3567        let actual_len = usage_date_str.len().min(12);
3568        let actual_prefix = &usage_date_str[..actual_len];
3569        ConditionResult::from(actual_prefix == expected_prefix.as_str())
3570    }
3571
3572    /// [133] Wenn an der übermittelten Marktlokation / Messlokation vorhanden
3573    fn evaluate_133(&self, _ctx: &EvaluationContext) -> ConditionResult {
3574        // TODO: implement
3575        ConditionResult::Unknown
3576    }
3577
3578    /// [134] Wenn dieses DTM+Z25 (Verwendung der Daten ab) nicht im SG6 RFF+Z48/ Z55 (Verwendungszeitraum der Daten: Erwartete Daten/ Keine Daten erwartet) mit der Zeitraum ID "1" im DE1156 ist, muss das Datum ...
3579    // REVIEW: Validates time-slice continuity for Z48/Z55 (Erwartete Daten/Keine Daten erwartet) SG6 groups: every slice with Zeitraum-ID > 1 must have its DTM+Z25 (start date) equal to the DTM+Z26 (end date) of the preceding slice. Zeitraum-ID is in RFF.C506.DE1156 = elements[0][2]. Requires navigator for per-SG6-instance DTM extraction. (medium confidence)
3580    fn evaluate_134(&self, ctx: &EvaluationContext) -> ConditionResult {
3581        let nav = match ctx.navigator() {
3582            Some(n) => n,
3583            None => return ConditionResult::Unknown,
3584        };
3585        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
3586        // Collect (zeitraum_id, dtm_z25_value, dtm_z26_value) for every Z48/Z55 SG6
3587        let mut slices: Vec<(u32, Option<String>, Option<String>)> = Vec::new();
3588        for i in 0..sg6_count {
3589            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
3590            // RFF C506: [0][0]=qualifier, [0][1]=ref-id, [0][2]=Zeitraum-ID
3591            let maybe_zid = rffs.iter().find_map(|s| {
3592                let qual = s
3593                    .elements
3594                    .first()
3595                    .and_then(|e| e.first())
3596                    .map(|v| v.as_str())?;
3597                if qual == "Z48" || qual == "Z55" {
3598                    s.elements
3599                        .first()
3600                        .and_then(|e| e.get(2))
3601                        .and_then(|v| v.parse::<u32>().ok())
3602                } else {
3603                    None
3604                }
3605            });
3606            if let Some(zid) = maybe_zid {
3607                let dtms = nav.find_segments_in_group("DTM", &["SG4", "SG6"], i);
3608                let z25 = dtms.iter().find_map(|s| {
3609                    if s.elements
3610                        .first()
3611                        .and_then(|e| e.first())
3612                        .map(|v| v.as_str())
3613                        == Some("Z25")
3614                    {
3615                        s.elements.first().and_then(|e| e.get(1)).cloned()
3616                    } else {
3617                        None
3618                    }
3619                });
3620                let z26 = dtms.iter().find_map(|s| {
3621                    if s.elements
3622                        .first()
3623                        .and_then(|e| e.first())
3624                        .map(|v| v.as_str())
3625                        == Some("Z26")
3626                    {
3627                        s.elements.first().and_then(|e| e.get(1)).cloned()
3628                    } else {
3629                        None
3630                    }
3631                });
3632                slices.push((zid, z25, z26));
3633            }
3634        }
3635        if slices.is_empty() {
3636            return ConditionResult::Unknown;
3637        }
3638        // For each slice with Zeitraum-ID > 1: its DTM+Z25 must equal DTM+Z26 of the previous slice
3639        for i in 0..slices.len() {
3640            let (zid, ref z25, _) = slices[i];
3641            if zid <= 1 {
3642                continue;
3643            }
3644            let prev_id = zid - 1;
3645            match slices.iter().find(|(id, _, _)| *id == prev_id) {
3646                Some((_, _, prev_z26)) => match (z25, prev_z26) {
3647                    (Some(curr), Some(prev_end)) => {
3648                        if curr != prev_end {
3649                            return ConditionResult::False;
3650                        }
3651                    }
3652                    _ => return ConditionResult::Unknown,
3653                },
3654                None => return ConditionResult::Unknown,
3655            }
3656        }
3657        ConditionResult::True
3658    }
3659
3660    /// [135] Wenn dieses DTM+Z25 (Verwendung der Daten ab) nicht im SG6 RFF+Z47/ Z54 (Verwendungszeitraum der Daten: Im System vorhandene Daten/ Im System keine Daten vorhanden) mit der Zeitraum ID "1" im DE115...
3661    // REVIEW: Identical time-slice continuity logic as condition 134 but applied to Z47/Z54 (Im System vorhandene Daten/Im System keine Daten vorhanden) SG6 groups. Same structural rule: DTM+Z25 of slice N must equal DTM+Z26 of slice N-1 for all N > 1. (medium confidence)
3662    fn evaluate_135(&self, ctx: &EvaluationContext) -> ConditionResult {
3663        let nav = match ctx.navigator() {
3664            Some(n) => n,
3665            None => return ConditionResult::Unknown,
3666        };
3667        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
3668        // Collect (zeitraum_id, dtm_z25_value, dtm_z26_value) for every Z47/Z54 SG6
3669        let mut slices: Vec<(u32, Option<String>, Option<String>)> = Vec::new();
3670        for i in 0..sg6_count {
3671            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
3672            // RFF C506: [0][0]=qualifier, [0][1]=ref-id, [0][2]=Zeitraum-ID
3673            let maybe_zid = rffs.iter().find_map(|s| {
3674                let qual = s
3675                    .elements
3676                    .first()
3677                    .and_then(|e| e.first())
3678                    .map(|v| v.as_str())?;
3679                if qual == "Z47" || qual == "Z54" {
3680                    s.elements
3681                        .first()
3682                        .and_then(|e| e.get(2))
3683                        .and_then(|v| v.parse::<u32>().ok())
3684                } else {
3685                    None
3686                }
3687            });
3688            if let Some(zid) = maybe_zid {
3689                let dtms = nav.find_segments_in_group("DTM", &["SG4", "SG6"], i);
3690                let z25 = dtms.iter().find_map(|s| {
3691                    if s.elements
3692                        .first()
3693                        .and_then(|e| e.first())
3694                        .map(|v| v.as_str())
3695                        == Some("Z25")
3696                    {
3697                        s.elements.first().and_then(|e| e.get(1)).cloned()
3698                    } else {
3699                        None
3700                    }
3701                });
3702                let z26 = dtms.iter().find_map(|s| {
3703                    if s.elements
3704                        .first()
3705                        .and_then(|e| e.first())
3706                        .map(|v| v.as_str())
3707                        == Some("Z26")
3708                    {
3709                        s.elements.first().and_then(|e| e.get(1)).cloned()
3710                    } else {
3711                        None
3712                    }
3713                });
3714                slices.push((zid, z25, z26));
3715            }
3716        }
3717        if slices.is_empty() {
3718            return ConditionResult::Unknown;
3719        }
3720        // For each slice with Zeitraum-ID > 1: its DTM+Z25 must equal DTM+Z26 of the previous slice
3721        for i in 0..slices.len() {
3722            let (zid, ref z25, _) = slices[i];
3723            if zid <= 1 {
3724                continue;
3725            }
3726            let prev_id = zid - 1;
3727            match slices.iter().find(|(id, _, _)| *id == prev_id) {
3728                Some((_, _, prev_z26)) => match (z25, prev_z26) {
3729                    (Some(curr), Some(prev_end)) => {
3730                        if curr != prev_end {
3731                            return ConditionResult::False;
3732                        }
3733                    }
3734                    _ => return ConditionResult::Unknown,
3735                },
3736                None => return ConditionResult::Unknown,
3737            }
3738        }
3739        ConditionResult::True
3740    }
3741
3742    /// [136] Wenn SG10 CCI+++E13 (Zählertyp) CAV+EHZ (elektronischer Haushaltszähler) vorhanden
3743    fn evaluate_136(&self, ctx: &EvaluationContext) -> ConditionResult {
3744        let cci_segs = ctx.find_segments("CCI");
3745        let found = cci_segs.iter().any(|s| {
3746            s.elements
3747                .get(2)
3748                .and_then(|e| e.first())
3749                .is_some_and(|v| v == "E13")
3750        });
3751        ConditionResult::from(found)
3752    }
3753
3754    /// [137] Nicht bei Neuanlage
3755    fn evaluate_137(&self, ctx: &EvaluationContext) -> ConditionResult {
3756        // Check: not a Neuanlage (new registration) process
3757        // STS+7 with transaktionsgrund containing Neuanlage indicators
3758        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
3759        let is_neuanlage = sts_segs.iter().any(|s| {
3760            s.elements
3761                .get(2)
3762                .and_then(|e| e.first())
3763                .is_some_and(|v| v == "E01" || v == "E03")
3764        });
3765        ConditionResult::from(!is_neuanlage)
3766    }
3767
3768    /// [138] Wenn im selben QTY im DE6063 Z35 (Abschlag) vorhanden
3769    fn evaluate_138(&self, ctx: &EvaluationContext) -> ConditionResult {
3770        let qty_segs = ctx.find_segments("QTY");
3771        let found = qty_segs.iter().any(|s| {
3772            s.elements
3773                .first()
3774                .and_then(|e| e.first())
3775                .is_some_and(|v| ["Z35"].contains(&v.as_str()))
3776        });
3777        ConditionResult::from(found)
3778    }
3779
3780    /// [139] Wenn SG10 CAV+IVA (Individuelle Abstimmung) nicht vorhanden
3781    // REVIEW: Condition is True when SG10 CAV+IVA (Individuelle Abstimmung) is NOT present anywhere. Uses parent-child navigation to iterate all SG8 instances and their SG10 child groups, checking CAV elements[0][0] for 'IVA'. Falls back to message-wide lacks_qualifier when no navigator is available. Returns False as soon as any CAV+IVA is found, True if none found. (medium confidence)
3782    fn evaluate_139(&self, ctx: &EvaluationContext) -> ConditionResult {
3783        let nav = match ctx.navigator {
3784            Some(n) => n,
3785            None => return ctx.lacks_qualifier("CAV", 0, "IVA"),
3786        };
3787        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3788        for i in 0..sg8_count {
3789            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3790            for j in 0..sg10_count {
3791                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3792                if cavs.iter().any(|s| {
3793                    s.elements
3794                        .first()
3795                        .and_then(|e: &Vec<String>| e.first())
3796                        .is_some_and(|v: &String| v == "IVA")
3797                }) {
3798                    return ConditionResult::False;
3799                }
3800            }
3801        }
3802        ConditionResult::True
3803    }
3804
3805    /// [140] Wenn im selben QTY im DE6063 Z34/Z35 (Zuschlag/Abschlag) vorhanden
3806    fn evaluate_140(&self, ctx: &EvaluationContext) -> ConditionResult {
3807        let qty_segs = ctx.find_segments("QTY");
3808        let found = qty_segs.iter().any(|s| {
3809            s.elements
3810                .first()
3811                .and_then(|e| e.first())
3812                .is_some_and(|v| ["Z34", "Z35"].contains(&v.as_str()))
3813        });
3814        ConditionResult::from(found)
3815    }
3816
3817    /// [141] Wenn SG10 CAV+MIW/ MPW/ MBW vorhanden
3818    fn evaluate_141(&self, ctx: &EvaluationContext) -> ConditionResult {
3819        let cav_segs = ctx.find_segments("CAV");
3820        let found = cav_segs.iter().any(|s| {
3821            s.elements
3822                .first()
3823                .and_then(|e| e.first())
3824                .is_some_and(|v| ["MIW", "MPW", "MBW"].contains(&v.as_str()))
3825        });
3826        ConditionResult::from(found)
3827    }
3828
3829    /// [142] Wenn SG8 PIA+Z01+:Z04/ Z05 (Berechnung Tagesmitteltemperatur: vom Anbieter zur Verfügung gestellte - / Äquivalente Tagesmitteltemperatur) vorhanden
3830    fn evaluate_142(&self, ctx: &EvaluationContext) -> ConditionResult {
3831        let pia_segs = ctx.find_segments("PIA");
3832        let found = pia_segs.iter().any(|s| {
3833            s.elements
3834                .first()
3835                .and_then(|e| e.first())
3836                .is_some_and(|v| v == "Z01")
3837                && s.elements
3838                    .get(1)
3839                    .and_then(|e| e.get(1))
3840                    .is_some_and(|v| ["Z04", "Z05"].contains(&v.as_str()))
3841        });
3842        ConditionResult::from(found)
3843    }
3844
3845    /// [143] Wenn STS+7++ZX3 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3846    fn evaluate_143(&self, ctx: &EvaluationContext) -> ConditionResult {
3847        let sts_segs = ctx.find_segments("STS");
3848        let found = sts_segs.iter().any(|s| {
3849            s.elements
3850                .first()
3851                .and_then(|e| e.first())
3852                .is_some_and(|v| v == "7")
3853                && s.elements
3854                    .get(2)
3855                    .and_then(|e| e.first())
3856                    .is_some_and(|v| v == "ZX3")
3857        });
3858        ConditionResult::from(found)
3859    }
3860
3861    /// [144] Wenn STS+7++ZAN (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3862    fn evaluate_144(&self, ctx: &EvaluationContext) -> ConditionResult {
3863        let sts_segs = ctx.find_segments("STS");
3864        let found = sts_segs.iter().any(|s| {
3865            s.elements
3866                .first()
3867                .and_then(|e| e.first())
3868                .is_some_and(|v| v == "7")
3869                && s.elements
3870                    .get(2)
3871                    .and_then(|e| e.first())
3872                    .is_some_and(|v| v == "ZAN")
3873        });
3874        ConditionResult::from(found)
3875    }
3876
3877    /// [145] Wenn STS+7++ZX2 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3878    fn evaluate_145(&self, ctx: &EvaluationContext) -> ConditionResult {
3879        let sts_segs = ctx.find_segments("STS");
3880        let found = sts_segs.iter().any(|s| {
3881            s.elements
3882                .first()
3883                .and_then(|e| e.first())
3884                .is_some_and(|v| v == "7")
3885                && s.elements
3886                    .get(2)
3887                    .and_then(|e| e.first())
3888                    .is_some_and(|v| v == "ZX2")
3889        });
3890        ConditionResult::from(found)
3891    }
3892
3893    /// [146] Wenn STS+7++ZA0 (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3894    fn evaluate_146(&self, ctx: &EvaluationContext) -> ConditionResult {
3895        let sts_segs = ctx.find_segments("STS");
3896        let found = sts_segs.iter().any(|s| {
3897            s.elements
3898                .first()
3899                .and_then(|e| e.first())
3900                .is_some_and(|v| v == "7")
3901                && s.elements
3902                    .get(2)
3903                    .and_then(|e| e.first())
3904                    .is_some_and(|v| v == "ZA0")
3905        });
3906        ConditionResult::from(found)
3907    }
3908
3909    /// [147] Wenn in Anfrage vorhanden
3910    fn evaluate_147(&self, _ctx: &EvaluationContext) -> ConditionResult {
3911        // TODO: implement
3912        ConditionResult::Unknown
3913    }
3914
3915    /// [148] Wenn in dieser SG8 im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), die Artikel-ID 1-02-0-017/ 1-02-0-018 vorhanden
3916    fn evaluate_148(&self, ctx: &EvaluationContext) -> ConditionResult {
3917        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3918        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
3919        let found = pia_segs.iter().any(|s| {
3920            s.elements
3921                .first()
3922                .and_then(|e| e.first())
3923                .is_some_and(|v| v == "Z02")
3924                && s.elements
3925                    .get(1)
3926                    .and_then(|e| e.first())
3927                    .is_some_and(|v| ["1-02-0-017", "1-02-0-018"].contains(&v.as_str()))
3928        });
3929        ConditionResult::from(found)
3930    }
3931
3932    /// [149] Wenn zwei SG8 SEQ+Z45 (Netznutzungsabrechnungsdaten der Marktlokation), mit  derselben Zeitraum-ID im DE1050, mit Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), eine SG8 mit 1-02-0-017...
3933    // REVIEW: Condition checks whether two SG8 SEQ+Z45 groups with the same Zeitraum-ID (elements[1][0]) each contain PIA+Z02 (DE4347=Z02) with product codes '1-02-0-017' and '1-02-0-018'. Uses collect_group_values to gather SEQ qualifiers and Zeitraum-IDs per group instance, then PIA qualifiers and product codes (elements[1][0]=DE7140) per group instance. Positional alignment of pia_quals and pia_prods (both from same PIA segments in same order) enables reliable qualifier-to-product matching. Groups product codes by Zeitraum-ID to check co-occurrence. (medium confidence)
3934    fn evaluate_149(&self, ctx: &EvaluationContext) -> ConditionResult {
3935        use std::collections::{HashMap, HashSet};
3936        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
3937        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
3938        let pia_quals = ctx.collect_group_values("PIA", 0, 0, &["SG4", "SG8"]);
3939        let pia_prods = ctx.collect_group_values("PIA", 1, 0, &["SG4", "SG8"]);
3940        // Map group index -> Zeitraum-ID for SG8s with SEQ+Z45
3941        let mut idx_to_zeitraum: HashMap<usize, String> = HashMap::new();
3942        for ((gi, q), (_, zid)) in seq_quals.iter().zip(seq_zeitraum.iter()) {
3943            if q == "Z45" && !zid.is_empty() {
3944                idx_to_zeitraum.insert(*gi, zid.clone());
3945            }
3946        }
3947        if idx_to_zeitraum.is_empty() {
3948            return ConditionResult::False;
3949        }
3950        // Map Zeitraum-ID -> set of PIA+Z02 product codes found in the same SG8 group
3951        let mut zeitraum_products: HashMap<String, HashSet<String>> = HashMap::new();
3952        for (i, (gi, qual)) in pia_quals.iter().enumerate() {
3953            if qual == "Z02" {
3954                if let Some(zid) = idx_to_zeitraum.get(gi) {
3955                    if let Some((_, prod)) = pia_prods.get(i) {
3956                        if !prod.is_empty() {
3957                            zeitraum_products
3958                                .entry(zid.clone())
3959                                .or_default()
3960                                .insert(prod.clone());
3961                        }
3962                    }
3963                }
3964            }
3965        }
3966        ConditionResult::from(
3967            zeitraum_products
3968                .values()
3969                .any(|codes| codes.contains("1-02-0-017") && codes.contains("1-02-0-018")),
3970        )
3971    }
3972
3973    /// [150] Wenn in dieser SG8 im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), die Artikel-ID 1-02-0-002 vorhanden
3974    fn evaluate_150(&self, ctx: &EvaluationContext) -> ConditionResult {
3975        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3976        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
3977        let found = pia_segs.iter().any(|s| {
3978            s.elements
3979                .first()
3980                .and_then(|e| e.first())
3981                .is_some_and(|v| v == "Z02")
3982                && s.elements
3983                    .get(1)
3984                    .and_then(|e| e.first())
3985                    .is_some_and(|v| ["1-02-0-002"].contains(&v.as_str()))
3986        });
3987        ConditionResult::from(found)
3988    }
3989
3990    /// [151] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Ü̈bertragungsweg \"aus dem SMGW\" bestellt wurde. Der Empfänger der Werte kann dadurch seine...
3991    fn evaluate_151(&self, ctx: &EvaluationContext) -> ConditionResult {
3992        ctx.external.evaluate("code_list_membership_check")
3993    }
3994
3995    /// [152] Wenn FTX+Z28 (IP-Range des Absenders) nicht vorhanden
3996    fn evaluate_152(&self, ctx: &EvaluationContext) -> ConditionResult {
3997        let ftx_segs = ctx.find_segments("FTX");
3998        let found = ftx_segs.iter().any(|s| {
3999            s.elements
4000                .first()
4001                .and_then(|e| e.first())
4002                .is_some_and(|v| v == "Z28")
4003        });
4004        ConditionResult::from(!found)
4005    }
4006
4007    /// [153] Wenn FTX+Z27 (IP-Adresse des Absenders) nicht vorhanden
4008    fn evaluate_153(&self, ctx: &EvaluationContext) -> ConditionResult {
4009        let ftx_segs = ctx.find_segments("FTX");
4010        let found = ftx_segs.iter().any(|s| {
4011            s.elements
4012                .first()
4013                .and_then(|e| e.first())
4014                .is_some_and(|v| v == "Z27")
4015        });
4016        ConditionResult::from(!found)
4017    }
4018
4019    /// [156] Wenn SG4 FTX+Z17 (Zieladresse URI) vorhanden
4020    fn evaluate_156(&self, ctx: &EvaluationContext) -> ConditionResult {
4021        let ftx_segs = ctx.find_segments("FTX");
4022        let found = ftx_segs.iter().any(|s| {
4023            s.elements
4024                .first()
4025                .and_then(|e| e.first())
4026                .is_some_and(|v| v == "Z17")
4027        });
4028        ConditionResult::from(found)
4029    }
4030
4031    /// [161] Wenn SG4 STS+7++E03+ZW8 (Transaktionsgrundergänzung Fall 1) vorhanden
4032    fn evaluate_161(&self, ctx: &EvaluationContext) -> ConditionResult {
4033        let sts_segs = ctx.find_segments("STS");
4034        let found = sts_segs.iter().any(|s| {
4035            s.elements
4036                .first()
4037                .and_then(|e| e.first())
4038                .is_some_and(|v| v == "7")
4039                && s.elements
4040                    .get(2)
4041                    .and_then(|e| e.first())
4042                    .is_some_and(|v| v == "E03")
4043                && s.elements
4044                    .get(3)
4045                    .and_then(|e| e.first())
4046                    .is_some_and(|v| v == "ZW8")
4047        });
4048        ConditionResult::from(found)
4049    }
4050
4051    /// [162] Wenn SG4 STS+7++E03+ZW9 (Transaktionsgrundergänzung Fall 2) vorhanden
4052    fn evaluate_162(&self, ctx: &EvaluationContext) -> ConditionResult {
4053        let sts_segs = ctx.find_segments("STS");
4054        let found = sts_segs.iter().any(|s| {
4055            s.elements
4056                .first()
4057                .and_then(|e| e.first())
4058                .is_some_and(|v| v == "7")
4059                && s.elements
4060                    .get(2)
4061                    .and_then(|e| e.first())
4062                    .is_some_and(|v| v == "E03")
4063                && s.elements
4064                    .get(3)
4065                    .and_then(|e| e.first())
4066                    .is_some_and(|v| v == "ZW9")
4067        });
4068        ConditionResult::from(found)
4069    }
4070
4071    /// [163] Wenn SG4 STS+7++E03+ZX0 (Transaktionsgrundergänzung Fall 3) vorhanden
4072    fn evaluate_163(&self, ctx: &EvaluationContext) -> ConditionResult {
4073        let sts_segs = ctx.find_segments("STS");
4074        let found = sts_segs.iter().any(|s| {
4075            s.elements
4076                .first()
4077                .and_then(|e| e.first())
4078                .is_some_and(|v| v == "7")
4079                && s.elements
4080                    .get(2)
4081                    .and_then(|e| e.first())
4082                    .is_some_and(|v| v == "E03")
4083                && s.elements
4084                    .get(3)
4085                    .and_then(|e| e.first())
4086                    .is_some_and(|v| v == "ZX0")
4087        });
4088        ConditionResult::from(found)
4089    }
4090
4091    /// [164] Wenn SG4 STS+7++E03+ZX1 (Transaktionsgrundergänzung Fall 4) vorhanden
4092    fn evaluate_164(&self, ctx: &EvaluationContext) -> ConditionResult {
4093        let sts_segs = ctx.find_segments("STS");
4094        let found = sts_segs.iter().any(|s| {
4095            s.elements
4096                .first()
4097                .and_then(|e| e.first())
4098                .is_some_and(|v| v == "7")
4099                && s.elements
4100                    .get(2)
4101                    .and_then(|e| e.first())
4102                    .is_some_and(|v| v == "E03")
4103                && s.elements
4104                    .get(3)
4105                    .and_then(|e| e.first())
4106                    .is_some_and(|v| v == "ZX1")
4107        });
4108        ConditionResult::from(found)
4109    }
4110
4111    /// [165] Wenn bekannt
4112    fn evaluate_165(&self, _ctx: &EvaluationContext) -> ConditionResult {
4113        // TODO: implement
4114        ConditionResult::Unknown
4115    }
4116
4117    /// [166] Wenn vorhanden
4118    fn evaluate_166(&self, _ctx: &EvaluationContext) -> ConditionResult {
4119        // "If present" — always true when the field is being validated
4120        ConditionResult::True
4121    }
4122
4123    /// [167] Wenn SG5 LOC+Z21 (Tranche) vorhanden
4124    fn evaluate_167(&self, ctx: &EvaluationContext) -> ConditionResult {
4125        let loc_segs = ctx.find_segments("LOC");
4126        let found = loc_segs.iter().any(|s| {
4127            s.elements
4128                .first()
4129                .and_then(|e| e.first())
4130                .is_some_and(|v| v == "Z21")
4131        });
4132        ConditionResult::from(found)
4133    }
4134
4135    /// [170] Wenn Anschrift der Marktlokation vorhanden
4136    fn evaluate_170(&self, _ctx: &EvaluationContext) -> ConditionResult {
4137        // TODO: implement
4138        ConditionResult::Unknown
4139    }
4140
4141    /// [172] Wenn im selben QTY im DE6063 Z37 (Kein Zu- und Abschlag) vorhanden
4142    fn evaluate_172(&self, ctx: &EvaluationContext) -> ConditionResult {
4143        let qty_segs = ctx.find_segments("QTY");
4144        let found = qty_segs.iter().any(|s| {
4145            s.elements
4146                .first()
4147                .and_then(|e| e.first())
4148                .is_some_and(|v| ["Z37"].contains(&v.as_str()))
4149        });
4150        ConditionResult::from(found)
4151    }
4152
4153    /// [173] Wenn im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) das DE1156 (Fortlaufende Nummer eines Lokationsbündels im Geschäftsvorfall) in derselben SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (...
4154    // REVIEW: Condition is True when, in an SG8 with SEQ+Z58/ZC9/ZD0/ZD6 (Zuordnung Lokation zum Objektcode), RFF+Z33 exists but its DE1156 (elements[0][2] = Fortlaufende Nummer) is absent or empty. Uses collect_group_values to find target SG8 group indices by SEQ qualifier, then positionally aligns rff_quals[i] with rff_de1156[i] (same RFF segment) to check DE1156 emptiness scoped to those groups. Returns Unknown if no RFF+Z33 found in target groups (condition not applicable), False if all RFF+Z33 have DE1156 present. (medium confidence)
4155    fn evaluate_173(&self, ctx: &EvaluationContext) -> ConditionResult {
4156        // HAND-EDITED: "in derselben SG8" — this SG8, a SEQ+Z58/ZC9/ZD0/ZD6, has an
4157        // RFF+Z33 without DE1156.
4158        let kinds = ctx.scoped_find_segments_in("SG8", "SEQ");
4159        if !kinds
4160            .iter()
4161            .any(|s| matches!(s.get_element(0), "Z58" | "ZC9" | "ZD0" | "ZD6"))
4162        {
4163            return ConditionResult::Unknown;
4164        }
4165        let z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
4166        if z33.is_empty() {
4167            return ConditionResult::Unknown;
4168        }
4169        ConditionResult::from(z33.iter().any(|s| s.get_component(0, 2).is_empty()))
4170    }
4171
4172    /// [174] Wenn im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) das DE1156 Fortlaufende Nummer eines Lokationsbündels im Geschäftsvorfall) in derselben SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Z...
4173    fn evaluate_174(&self, ctx: &EvaluationContext) -> ConditionResult {
4174        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4175        // Check: RFF+Z33 has a value in DE1156 (fortlaufende Nummer)
4176        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
4177        let found = rff_z33.iter().any(|s| {
4178            s.elements
4179                .first()
4180                .and_then(|e| e.get(2))
4181                .is_some_and(|v| !v.is_empty())
4182        });
4183        ConditionResult::from(found)
4184    }
4185
4186    /// [175] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+Z58 (Zuordnung Lokation zum Objektcode des Lokationsbündels) in einem weite...
4187    // REVIEW: Cross-SG8 check: find all SG8 instances with SEQ+Z58 that also have RFF+Z33. Check whether any Objektcode (DE1154) with the same fortlaufende Nummer (DE1156) appears in two distinct SG8 instances. Uses nav.find_segments_in_group to access SG8-level segments directly, following the existing navigator pattern. (medium confidence)
4188    fn evaluate_175(&self, ctx: &EvaluationContext) -> ConditionResult {
4189        let nav = match ctx.navigator {
4190            Some(n) => n,
4191            None => return ConditionResult::Unknown,
4192        };
4193        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4194        let mut entries: Vec<(usize, String, String)> = Vec::new();
4195        for i in 0..sg8_count {
4196            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4197            if !seqs.iter().any(|s| {
4198                s.elements
4199                    .first()
4200                    .and_then(|e: &Vec<String>| e.first())
4201                    .is_some_and(|v: &String| v == "Z58")
4202            }) {
4203                continue;
4204            }
4205            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4206            for rff in &rffs {
4207                let elems = match rff.elements.first() {
4208                    Some(e) => e,
4209                    None => continue,
4210                };
4211                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4212                    continue;
4213                }
4214                let objektcode = elems.get(1).cloned().unwrap_or_default();
4215                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4216                if !objektcode.is_empty() {
4217                    entries.push((i, objektcode, fort_nr));
4218                }
4219            }
4220        }
4221        for a in 0..entries.len() {
4222            for b in (a + 1)..entries.len() {
4223                if entries[a].0 != entries[b].0
4224                    && entries[a].1 == entries[b].1
4225                    && entries[a].2 == entries[b].2
4226                {
4227                    return ConditionResult::True;
4228                }
4229            }
4230        }
4231        ConditionResult::False
4232    }
4233
4234    /// [176] Wenn in demselben Segment das DE1153 mit dem Code Z34 vorgelagerte Messlokation vorhanden ist
4235    fn evaluate_176(&self, ctx: &EvaluationContext) -> ConditionResult {
4236        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z34"))
4237    }
4238
4239    /// [177] Wenn in demselben Segment das DE1153 mit dem Code Z35 vorgelagerte Netzlokation vorhanden ist
4240    fn evaluate_177(&self, ctx: &EvaluationContext) -> ConditionResult {
4241        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z35"))
4242    }
4243
4244    /// [178] Wenn das LOC+Z18 (Netzlokation) vorhanden ist
4245    fn evaluate_178(&self, ctx: &EvaluationContext) -> ConditionResult {
4246        let loc_segs = ctx.find_segments("LOC");
4247        let found = loc_segs.iter().any(|s| {
4248            s.elements
4249                .first()
4250                .and_then(|e| e.first())
4251                .is_some_and(|v| v == "Z18")
4252        });
4253        ConditionResult::from(found)
4254    }
4255
4256    /// [179] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+Z58/ ZC9 / ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels...
4257    fn evaluate_179(&self, ctx: &EvaluationContext) -> ConditionResult {
4258        // HAND-EDITED: this SG8's Objektcode (RFF+Z33 DE1154) must be in a
4259        // sibling SG8 with the same SEQ code (Z58/ZC9/ZD0/ZD6). Unknown without
4260        // a group scope: "derselben SG8" cannot be told apart then.
4261        const KINDS: [&str; 4] = ["Z58", "ZC9", "ZD0", "ZD6"];
4262        let Some(siblings) = ctx.sibling_scopes("SG8") else {
4263            return ConditionResult::Unknown;
4264        };
4265        let seq_of = |segs: &[OwnedSegment]| {
4266            segs.iter()
4267                .find(|s| s.id == "SEQ")
4268                .map(|s| s.get_element(0).to_string())
4269        };
4270        let objektcodes = |segs: &[OwnedSegment]| -> Vec<String> {
4271            segs.iter()
4272                .filter(|s| s.id == "RFF" && s.get_element(0) == "Z33")
4273                .map(|s| s.get_component(0, 1).to_string())
4274                .filter(|v| !v.is_empty())
4275                .collect()
4276        };
4277        let own_seq = seq_of(&ctx.scoped_find_segments_in("SG8", "SEQ"));
4278        let Some(own_seq) = own_seq.filter(|q| KINDS.contains(&q.as_str())) else {
4279            return ConditionResult::False;
4280        };
4281        let own = objektcodes(&ctx.scoped_find_segments_in("SG8", "RFF"));
4282        if own.is_empty() {
4283            return ConditionResult::False;
4284        }
4285        let found = siblings.iter().any(|path| {
4286            seq_of(&ctx.find_segments_at(path, "SEQ")).as_deref() == Some(own_seq.as_str())
4287                && objektcodes(&ctx.find_segments_at(path, "RFF"))
4288                    .iter()
4289                    .any(|code| own.contains(code))
4290        });
4291        ConditionResult::from(found)
4292    }
4293
4294    /// [180] Wenn im selben QTY im DE6063 Z46 (Kein Abschlag) vorhanden
4295    fn evaluate_180(&self, ctx: &EvaluationContext) -> ConditionResult {
4296        let qty_segs = ctx.find_segments("QTY");
4297        let found = qty_segs.iter().any(|s| {
4298            s.elements
4299                .first()
4300                .and_then(|e| e.first())
4301                .is_some_and(|v| ["Z46"].contains(&v.as_str()))
4302        });
4303        ConditionResult::from(found)
4304    }
4305
4306    /// [184] Wenn in derselben SG8 SEQ+Z18 (Daten der Messlokation) ein CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
4307    fn evaluate_184(&self, ctx: &EvaluationContext) -> ConditionResult {
4308        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4309        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4310        let found = seq_segs.iter().any(|s| {
4311            s.elements
4312                .first()
4313                .and_then(|e| e.first())
4314                .is_some_and(|v| ["Z18"].contains(&v.as_str()))
4315        });
4316        ConditionResult::from(found)
4317    }
4318
4319    /// [190] Wenn in derselben SG8 SEQ+Z60/ ZG8/ ZG9/ ZE0  (Produkt-Daten der Netzlokation) PIA+5+9991000000721:Z12 vorhanden
4320    fn evaluate_190(&self, ctx: &EvaluationContext) -> ConditionResult {
4321        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4322        // Check: SG8 with SEQ+Z60/ZG8/ZG9/ZE0 has PIA+5 with code 9991000000721
4323        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4324        let found = pia_segs.iter().any(|s| {
4325            s.elements
4326                .first()
4327                .and_then(|e| e.first())
4328                .is_some_and(|v| v == "5")
4329                && s.elements
4330                    .get(1)
4331                    .and_then(|e| e.first())
4332                    .is_some_and(|v| v == "9991000000721")
4333        });
4334        ConditionResult::from(found)
4335    }
4336
4337    /// [191] Wenn im SG4 IDE+Z01 (Identifikation einer Liste) STS+E01 (Status der Antwort der Liste) nicht vorhanden
4338    fn evaluate_191(&self, ctx: &EvaluationContext) -> ConditionResult {
4339        let sts_segs = ctx.find_segments("STS");
4340        let found = sts_segs.iter().any(|s| {
4341            s.elements
4342                .first()
4343                .and_then(|e| e.first())
4344                .is_some_and(|v| v == "E01")
4345        });
4346        ConditionResult::from(!found)
4347    }
4348
4349    /// [192] Wenn SG4 STS+7++XXX+ZW4/ZAP (Transaktionsgrund / Ergänzung: Verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
4350    fn evaluate_192(&self, ctx: &EvaluationContext) -> ConditionResult {
4351        let sts_segs = ctx.find_segments("STS");
4352        let found = sts_segs.iter().any(|s| {
4353            s.elements
4354                .first()
4355                .and_then(|e| e.first())
4356                .is_some_and(|v| v == "7")
4357                && s.elements
4358                    .get(3)
4359                    .and_then(|e| e.first())
4360                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
4361        });
4362        ConditionResult::from(found)
4363    }
4364
4365    /// [193] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E06 (Niederspannung) in dieser SG vorhanden
4366    fn evaluate_193(&self, ctx: &EvaluationContext) -> ConditionResult {
4367        let nav = match ctx.navigator {
4368            Some(n) => n,
4369            None => {
4370                let cavs = ctx.find_segments("CAV");
4371                return ConditionResult::from(cavs.iter().any(|s| {
4372                    s.elements
4373                        .first()
4374                        .and_then(|e: &Vec<String>| e.first())
4375                        .is_some_and(|v: &String| v == "E06")
4376                }));
4377            }
4378        };
4379        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4380        for i in 0..sg8_count {
4381            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4382            for j in 0..sg10_count {
4383                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4384                let has_cci_zb5 = ccis.iter().any(|s| {
4385                    s.elements
4386                        .get(2)
4387                        .and_then(|e: &Vec<String>| e.first())
4388                        .is_some_and(|v: &String| v == "ZB5")
4389                });
4390                if !has_cci_zb5 {
4391                    continue;
4392                }
4393                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4394                if cavs.iter().any(|s| {
4395                    s.elements
4396                        .first()
4397                        .and_then(|e: &Vec<String>| e.first())
4398                        .is_some_and(|v: &String| v == "E06")
4399                }) {
4400                    return ConditionResult::True;
4401                }
4402            }
4403        }
4404        ConditionResult::False
4405    }
4406
4407    /// [194] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E05 (Mittelspannung) in dieser SG vorhanden
4408    fn evaluate_194(&self, ctx: &EvaluationContext) -> ConditionResult {
4409        let cci_segs = ctx.find_segments("CCI");
4410        let has_cci = cci_segs.iter().any(|s| {
4411            s.elements
4412                .iter()
4413                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4414        });
4415        let cav_segs = ctx.find_segments("CAV");
4416        let has_cav = cav_segs.iter().any(|s| {
4417            s.elements
4418                .first()
4419                .and_then(|e| e.first())
4420                .is_some_and(|v| v == "E05")
4421        });
4422        ConditionResult::from(has_cci && has_cav)
4423    }
4424
4425    /// [195] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E04 (Hochspannung) in dieser SG vorhanden
4426    fn evaluate_195(&self, ctx: &EvaluationContext) -> ConditionResult {
4427        let cci_segs = ctx.find_segments("CCI");
4428        let has_cci = cci_segs.iter().any(|s| {
4429            s.elements
4430                .iter()
4431                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4432        });
4433        let cav_segs = ctx.find_segments("CAV");
4434        let has_cav = cav_segs.iter().any(|s| {
4435            s.elements
4436                .first()
4437                .and_then(|e| e.first())
4438                .is_some_and(|v| v == "E04")
4439        });
4440        ConditionResult::from(has_cci && has_cav)
4441    }
4442
4443    /// [196] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+Z58/ZC9/ZD0 (Zuordnung Lokation zum Objektcode des Lokationsbündels) in ein...
4444    // REVIEW: Extended cross-SG8 check vs 175: now requires matching SEQ qualifier code (DE1229), Zeitraum-ID from SEQ DE1050 (elements[1][0]), Objektcode (RFF+Z33 DE1154), AND fortlaufende Nummer (RFF+Z33 DE1156) to all be identical between two distinct SG8 instances. SEQ qualifiers are Z58/ZC9/ZD0 per schema. (medium confidence)
4445    fn evaluate_196(&self, ctx: &EvaluationContext) -> ConditionResult {
4446        let nav = match ctx.navigator {
4447            Some(n) => n,
4448            None => return ConditionResult::Unknown,
4449        };
4450        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4451        let mut entries: Vec<(usize, String, String, String, String)> = Vec::new();
4452        for i in 0..sg8_count {
4453            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4454            let seq_opt = seqs.iter().find(|s| {
4455                s.elements
4456                    .first()
4457                    .and_then(|e: &Vec<String>| e.first())
4458                    .is_some_and(|v: &String| matches!(v.as_str(), "Z58" | "ZC9" | "ZD0"))
4459            });
4460            let seq = match seq_opt {
4461                Some(s) => s,
4462                None => continue,
4463            };
4464            let seq_code = seq
4465                .elements
4466                .first()
4467                .and_then(|e: &Vec<String>| e.first())
4468                .cloned()
4469                .unwrap_or_default();
4470            let zeitraum_id = seq
4471                .elements
4472                .get(1)
4473                .and_then(|e: &Vec<String>| e.first())
4474                .cloned()
4475                .unwrap_or_default();
4476            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4477            for rff in &rffs {
4478                let elems = match rff.elements.first() {
4479                    Some(e) => e,
4480                    None => continue,
4481                };
4482                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4483                    continue;
4484                }
4485                let objektcode = elems.get(1).cloned().unwrap_or_default();
4486                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4487                if !objektcode.is_empty() {
4488                    entries.push((
4489                        i,
4490                        seq_code.clone(),
4491                        zeitraum_id.clone(),
4492                        objektcode,
4493                        fort_nr,
4494                    ));
4495                }
4496            }
4497        }
4498        for a in 0..entries.len() {
4499            for b in (a + 1)..entries.len() {
4500                if entries[a].0 != entries[b].0
4501                    && entries[a].1 == entries[b].1
4502                    && entries[a].2 == entries[b].2
4503                    && entries[a].3 == entries[b].3
4504                    && entries[a].4 == entries[b].4
4505                {
4506                    return ConditionResult::True;
4507                }
4508            }
4509        }
4510        ConditionResult::False
4511    }
4512
4513    /// [197] Wenn das RFF+Z18 (Marktlokation) aus dieser SG8 auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der Marktlokation), in dem SG10 CCI+++ZA6 (...
4514    // REVIEW: Two-phase cross-SG8 check. Phase 1: collect Marktlokation IDs from SG8 instances that (a) have SEQ+Z01 and (b) have an SG10 child with CCI elements[2][0]=ZA6 and CAV elements[0][0]=E02. Phase 2: check whether any SG8's RFF+Z18 references one of those qualifying Marktlokation IDs, establishing the cross-reference described in the condition. (medium confidence)
4515    fn evaluate_197(&self, ctx: &EvaluationContext) -> ConditionResult {
4516        let nav = match ctx.navigator {
4517            Some(n) => n,
4518            None => return ConditionResult::Unknown,
4519        };
4520        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4521        let mut qualifying_malo_ids: Vec<String> = Vec::new();
4522        for i in 0..sg8_count {
4523            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4524            if !seqs.iter().any(|s| {
4525                s.elements
4526                    .first()
4527                    .and_then(|e: &Vec<String>| e.first())
4528                    .is_some_and(|v: &String| v == "Z01")
4529            }) {
4530                continue;
4531            }
4532            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4533            let has_prog = (0..sg10_count).any(|j| {
4534                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4535                let has_za6 = ccis.iter().any(|s| {
4536                    s.elements
4537                        .get(2)
4538                        .and_then(|e: &Vec<String>| e.first())
4539                        .is_some_and(|v: &String| v == "ZA6")
4540                });
4541                if !has_za6 {
4542                    return false;
4543                }
4544                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4545                cavs.iter().any(|s| {
4546                    s.elements
4547                        .first()
4548                        .and_then(|e: &Vec<String>| e.first())
4549                        .is_some_and(|v: &String| v == "E02")
4550                })
4551            });
4552            if !has_prog {
4553                continue;
4554            }
4555            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4556            for rff in &rffs {
4557                let elems = match rff.elements.first() {
4558                    Some(e) => e,
4559                    None => continue,
4560                };
4561                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4562                    continue;
4563                }
4564                if let Some(malo_id) = elems.get(1) {
4565                    if !malo_id.is_empty() {
4566                        qualifying_malo_ids.push(malo_id.clone());
4567                    }
4568                }
4569            }
4570        }
4571        if qualifying_malo_ids.is_empty() {
4572            return ConditionResult::False;
4573        }
4574        for i in 0..sg8_count {
4575            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4576            for rff in &rffs {
4577                let elems = match rff.elements.first() {
4578                    Some(e) => e,
4579                    None => continue,
4580                };
4581                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4582                    continue;
4583                }
4584                if let Some(malo_id) = elems.get(1) {
4585                    if qualifying_malo_ids.contains(malo_id) {
4586                        return ConditionResult::True;
4587                    }
4588                }
4589            }
4590        }
4591        ConditionResult::False
4592    }
4593
4594    /// [198] Wenn SG4 STS+7++E03(Transaktionsgrund: Wechsel) vorhanden
4595    fn evaluate_198(&self, ctx: &EvaluationContext) -> ConditionResult {
4596        let sts_segs = ctx.find_segments("STS");
4597        let found = sts_segs.iter().any(|s| {
4598            s.elements
4599                .first()
4600                .and_then(|e| e.first())
4601                .is_some_and(|v| v == "7")
4602                && s.elements
4603                    .get(2)
4604                    .and_then(|e| e.first())
4605                    .is_some_and(|v| ["E03"].contains(&v.as_str()))
4606        });
4607        ConditionResult::from(found)
4608    }
4609
4610    /// [199] Wenn in der SG8 SEQ+Z01/ SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) mit identischer Zeitraum-ID im DE1050 CAV+E02 (SLP/SEP) vorhanden
4611    // REVIEW: Checks whether any SG8 with SEQ+Z01 (Daten der Marktlokation) contains an SG10 child group where CCI elements[2][0]=ZA6 (Prognosegrundlage: Prognose auf Basis von Profilen) and CAV elements[0][0]=E02 (SLP/SEP) appear in the same SG10 instance. The 'identischer Zeitraum-ID' constraint from SEQ DE1050 is structurally satisfied by operating within the same SG8 instance; deeper Zeitraum-ID cross-referencing with other groups is omitted as it would require knowing the external referencing context. (medium confidence)
4612    fn evaluate_199(&self, ctx: &EvaluationContext) -> ConditionResult {
4613        let nav = match ctx.navigator {
4614            Some(n) => n,
4615            None => {
4616                return ctx.filtered_parent_child_has_qualifier(
4617                    &["SG4", "SG8"],
4618                    "SEQ",
4619                    0,
4620                    "Z01",
4621                    "SG10",
4622                    "CCI",
4623                    2,
4624                    "ZA6",
4625                )
4626            }
4627        };
4628        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4629        for i in 0..sg8_count {
4630            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4631            if !seqs.iter().any(|s| {
4632                s.elements
4633                    .first()
4634                    .and_then(|e: &Vec<String>| e.first())
4635                    .is_some_and(|v: &String| v == "Z01")
4636            }) {
4637                continue;
4638            }
4639            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4640            for j in 0..sg10_count {
4641                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4642                if !ccis.iter().any(|s| {
4643                    s.elements
4644                        .get(2)
4645                        .and_then(|e: &Vec<String>| e.first())
4646                        .is_some_and(|v: &String| v == "ZA6")
4647                }) {
4648                    continue;
4649                }
4650                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4651                if cavs.iter().any(|s| {
4652                    s.elements
4653                        .first()
4654                        .and_then(|e: &Vec<String>| e.first())
4655                        .is_some_and(|v: &String| v == "E02")
4656                }) {
4657                    return ConditionResult::True;
4658                }
4659            }
4660        }
4661        ConditionResult::False
4662    }
4663
4664    /// [201] Wenn die Marktlokation / Tranche nicht den ganzen Bilanzierungsmonat dem gleichen Tupel aus Bilanzkreis, Zeitreihentyp, Spannungsebene und Lieferant zugeordnet ist
4665    fn evaluate_201(&self, _ctx: &EvaluationContext) -> ConditionResult {
4666        // TODO: implement
4667        ConditionResult::Unknown
4668    }
4669
4670    /// [202] Wenn SG8 SEQ+Z78 RFF+Z39 (Keine standardisierte Lokationsbündelstruktur vorhanden), mit identischer Zeitraum-ID im DE1050 wie im DE3224 dieses Segments
4671    // REVIEW: Collects Zeitraum-IDs from SG5 LOC.DE3224 (elements[1][3]), checks SEQ+Z78 DE1050 (elements[1][0]) for a match, then verifies RFF+Z39 exists. Co-occurrence of SEQ+Z78 and RFF+Z39 in the same SG8 instance is not verified without navigator group-instance access. (medium confidence)
4672    fn evaluate_202(&self, ctx: &EvaluationContext) -> ConditionResult {
4673        use std::collections::HashSet;
4674        // Collect Zeitraum-IDs from all SG5 LOC segments (DE3224 at elements[1][3])
4675        let loc_zeitraum_ids: HashSet<String> = ctx
4676            .find_segments("LOC")
4677            .into_iter()
4678            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
4679            .filter(|v| !v.is_empty())
4680            .collect();
4681        if loc_zeitraum_ids.is_empty() {
4682            return ConditionResult::False;
4683        }
4684        // Check if any SEQ+Z78 (SG8 Lokationsbündelstruktur) has DE1050 matching a LOC DE3224
4685        let matching_seq = ctx
4686            .find_segments_with_qualifier("SEQ", 0, "Z78")
4687            .into_iter()
4688            .any(|s| {
4689                s.elements
4690                    .get(1)
4691                    .and_then(|e: &Vec<String>| e.first())
4692                    .is_some_and(|v: &String| {
4693                        !v.is_empty() && loc_zeitraum_ids.contains(v.as_str())
4694                    })
4695            });
4696        if !matching_seq {
4697            return ConditionResult::False;
4698        }
4699        // Verify RFF+Z39 also exists (co-occurrence in same SG8 not verified without navigator)
4700        ctx.has_qualifier("RFF", 0, "Z39")
4701    }
4702
4703    /// [203] Wenn STS+7++E06 / Z39 / ZC6 / ZC7/ ZT6/ ZT7 vorhanden
4704    fn evaluate_203(&self, ctx: &EvaluationContext) -> ConditionResult {
4705        let sts_segs = ctx.find_segments("STS");
4706        let found = sts_segs.iter().any(|s| {
4707            s.elements
4708                .first()
4709                .and_then(|e| e.first())
4710                .is_some_and(|v| v == "7")
4711                && s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
4712                    ["E06", "Z39", "ZC6", "ZC7", "ZT6", "ZT7"].contains(&v.as_str())
4713                })
4714        });
4715        ConditionResult::from(found)
4716    }
4717
4718    /// [204] Wenn im SG8+SEQ+Z03/ ZA3/ ZA4 (Zähleinrichtungsdaten) mit der selben Zeitraum-ID im SG8 SEQ DE1050, sowie identischer Qualität im DE1229 der SG8 SEQ für die in diesem RFF DE1154 genannte Geräte...
4719    // REVIEW: Checks each Zähleinrichtungsdaten SEQ qualifier (Z03/ZA3/ZA4) — if any SG8 instance has that SEQ qualifier but no RFF+Z14 (Smartmeter-Gateway reference), the condition is true. The additional Zeitraum-ID and Gerätenummer cross-matching from the full condition text cannot be implemented without per-instance SG8 segment access. (medium confidence)
4720    fn evaluate_204(&self, ctx: &EvaluationContext) -> ConditionResult {
4721        // True if any SG8 with Zähleinrichtungsdaten (SEQ+Z03/ZA3/ZA4) lacks RFF+Z14 (Smartmeter-Gateway)
4722        for qual in &["Z03", "ZA3", "ZA4"] {
4723            if matches!(
4724                ctx.any_group_has_qualifier_without(
4725                    "SEQ",
4726                    0,
4727                    qual,
4728                    "RFF",
4729                    0,
4730                    "Z14",
4731                    &["SG4", "SG8"]
4732                ),
4733                ConditionResult::True
4734            ) {
4735                return ConditionResult::True;
4736            }
4737        }
4738        ConditionResult::False
4739    }
4740
4741    /// [205] Wenn SG5 LOC+Z19 (Steuerbare Ressource) vorhanden
4742    fn evaluate_205(&self, ctx: &EvaluationContext) -> ConditionResult {
4743        let loc_segs = ctx.find_segments("LOC");
4744        let found = loc_segs.iter().any(|s| {
4745            s.elements
4746                .first()
4747                .and_then(|e| e.first())
4748                .is_some_and(|v| v == "Z19")
4749        });
4750        ConditionResult::from(found)
4751    }
4752
4753    /// [206] Wenn SG4 STS+7++ZG5 (Transaktionsgrund: Aufhebung der zukünftigen Zuordnung aufgrund §38 EEG 2014 bzw. §21b Abs. 1 Nr. 2 EEG 2017) nicht vorhanden
4754    fn evaluate_206(&self, ctx: &EvaluationContext) -> ConditionResult {
4755        let sts_segs = ctx.find_segments("STS");
4756        let found = sts_segs.iter().any(|s| {
4757            s.elements
4758                .first()
4759                .and_then(|e| e.first())
4760                .is_some_and(|v| v == "7")
4761                && s.elements
4762                    .get(2)
4763                    .and_then(|e| e.first())
4764                    .is_some_and(|v| ["ZG5"].contains(&v.as_str()))
4765        });
4766        ConditionResult::from(!found)
4767    }
4768
4769    /// [209] Wenn im selben Segment im DE2379 der Code 303 vorhanden ist
4770    fn evaluate_209(&self, ctx: &EvaluationContext) -> ConditionResult {
4771        ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "303"))
4772    }
4773
4774    /// [210] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
4775    fn evaluate_210(&self, ctx: &EvaluationContext) -> ConditionResult {
4776        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4777        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4778        let found = cci_segs.iter().any(|s| {
4779            s.elements
4780                .get(2)
4781                .and_then(|e| e.first())
4782                .is_some_and(|v| v == "ZA6")
4783        });
4784        ConditionResult::from(found)
4785    }
4786
4787    /// [212] Wenn im selben SG12 NAD DE3124 nicht vorhanden
4788    fn evaluate_212(&self, ctx: &EvaluationContext) -> ConditionResult {
4789        let nad_segs = ctx.find_segments("NAD");
4790        let found = nad_segs.iter().any(|s| {
4791            s.elements
4792                .get(3)
4793                .and_then(|e| e.first())
4794                .is_some_and(|v| !v.is_empty())
4795        });
4796        ConditionResult::from(!found)
4797    }
4798
4799    /// [213] Wenn SG12 NAD+Z09 (Kunde des Lieferanten) vorhanden
4800    fn evaluate_213(&self, ctx: &EvaluationContext) -> ConditionResult {
4801        let nad_segs = ctx.find_segments("NAD");
4802        let found = nad_segs.iter().any(|s| {
4803            s.elements
4804                .first()
4805                .and_then(|e| e.first())
4806                .is_some_and(|v| v == "Z09")
4807        });
4808        ConditionResult::from(found)
4809    }
4810
4811    /// [215] Wenn in derselben SG8 (Zähleinrichtungsdaten) SG10 CCI+++E13 CAV+MME (Zählertyp: mME) vorhanden
4812    fn evaluate_215(&self, ctx: &EvaluationContext) -> ConditionResult {
4813        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8 (its
4814        // SG10s included). Both segments are looked for anywhere in that SG8,
4815        // not within one SG10.
4816        let has_cci_e13 = ctx.scoped_find_segments_in("SG8", "CCI").iter().any(|s| {
4817            s.elements
4818                .get(2)
4819                .and_then(|e| e.first())
4820                .is_some_and(|v| v == "E13")
4821        });
4822        let has_cav_mme = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
4823            s.elements
4824                .first()
4825                .and_then(|e| e.first())
4826                .is_some_and(|v| v == "MME")
4827        });
4828        ConditionResult::from(has_cci_e13 && has_cav_mme)
4829    }
4830
4831    /// [216] Wenn CCI+++Z88 (Netznutzung) CAV+Z74:::Z08 (Netznutzungsvertrag: Direkter Vertrag zwischen Kunden und NB) vorhanden
4832    fn evaluate_216(&self, ctx: &EvaluationContext) -> ConditionResult {
4833        let cci_segs = ctx.find_segments("CCI");
4834        let found = cci_segs.iter().any(|s| {
4835            s.elements
4836                .get(2)
4837                .and_then(|e| e.first())
4838                .is_some_and(|v| v == "Z88")
4839        });
4840        ConditionResult::from(found)
4841    }
4842
4843    /// [217] Wenn SG6 RFF+Z49 (Verwendungszeitraum der Daten: \"Gültige Daten\") vorhanden
4844    fn evaluate_217(&self, ctx: &EvaluationContext) -> ConditionResult {
4845        let rff_segs = ctx.find_segments("RFF");
4846        let found = rff_segs.iter().any(|s| {
4847            s.elements
4848                .first()
4849                .and_then(|e| e.first())
4850                .is_some_and(|v| v == "Z49")
4851        });
4852        ConditionResult::from(found)
4853    }
4854
4855    /// [219] Wenn SG12 NAD+Z65 (Informativer Kunde des Lieferanten) vorhanden
4856    fn evaluate_219(&self, ctx: &EvaluationContext) -> ConditionResult {
4857        let nad_segs = ctx.find_segments("NAD");
4858        let found = nad_segs.iter().any(|s| {
4859            s.elements
4860                .first()
4861                .and_then(|e| e.first())
4862                .is_some_and(|v| v == "Z65")
4863        });
4864        ConditionResult::from(found)
4865    }
4866
4867    /// [220] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) in dieser SG8 vorhanden
4868    fn evaluate_220(&self, ctx: &EvaluationContext) -> ConditionResult {
4869        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4870        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4871        let has_seq = seq_segs.iter().any(|s| {
4872            s.elements
4873                .first()
4874                .and_then(|e| e.first())
4875                .is_some_and(|v| ["Z01", "Z98"].contains(&v.as_str()))
4876        });
4877        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4878        let has_cci = cci_segs.iter().any(|s| {
4879            s.elements
4880                .first()
4881                .and_then(|e| e.first())
4882                .is_some_and(|v| v == "Z30")
4883                && s.elements
4884                    .get(2)
4885                    .and_then(|e| e.first())
4886                    .is_some_and(|v| v == "Z06")
4887        });
4888        ConditionResult::from(has_seq && has_cci)
4889    }
4890
4891    /// [221] Wenn kein SG8 SEQ+Z15 (Daten der Tranche) auf die gleiche Marktlokation-ID mit dem RFF+Z18 (Marktlokation) referenziert wie dieses SG8 SEQ+Z01 (Daten der Marktlokation)
4892    fn evaluate_221(&self, ctx: &EvaluationContext) -> ConditionResult {
4893        // Check: no SG8 with SEQ+Z15 has RFF+Z18 referencing same Marktlokation
4894        let has_seq_z15 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z15").is_empty();
4895        if !has_seq_z15 {
4896            return ConditionResult::True;
4897        }
4898        // Approximate: SEQ+Z15 exists, check if it references via RFF+Z18
4899        ctx.any_group_has_qualifier_without("SEQ", 0, "Z15", "RFF", 0, "Z18", &["SG4", "SG8"])
4900    }
4901
4902    /// [223] Wenn das RFF+Z18 (Marktlokation) aus dieser SG8 auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der Marktlokation) verweist, und dort das S...
4903    // HAND-EDITED (mako-twin, 55043): the generated version only checked that
4904    // some RFF+Z18 names some LOC+Z16 and dropped the Erzeugung clause, so it
4905    // held for every message with a Marktlokation. The scope never narrows to
4906    // one SG8 (only top-level groups are scoped), so "this SG8" is any SG8
4907    // other than SEQ+Z01, as in the neighbouring conditions.
4908    fn evaluate_223(&self, ctx: &EvaluationContext) -> ConditionResult {
4909        let Some(blocks) = sg8_blocks(ctx) else {
4910            return ConditionResult::Unknown;
4911        };
4912        let loc_ids: Vec<String> = ctx
4913            .find_segments_with_qualifier("LOC", 0, "Z16")
4914            .iter()
4915            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
4916            .collect();
4917        let malo_refs = |b: &Sg8Block| -> Vec<String> {
4918            b.malo_refs
4919                .iter()
4920                .filter(|id| loc_ids.contains(id))
4921                .cloned()
4922                .collect()
4923        };
4924        // Marktlokationen whose SEQ+Z01 carries SG10 CCI+Z30++Z06 (Erzeugung).
4925        let erzeugung: Vec<String> = blocks
4926            .iter()
4927            .filter(|b| b.seq == "Z01")
4928            .filter(|b| {
4929                b.sg10.iter().any(|(ccis, _)| {
4930                    ccis.iter().any(|c| {
4931                        component(c, 0, 0) == Some("Z30") && component(c, 2, 0) == Some("Z06")
4932                    })
4933                })
4934            })
4935            .flat_map(|b| malo_refs(b))
4936            .collect();
4937        let referenced = blocks
4938            .iter()
4939            .filter(|b| b.seq != "Z01")
4940            .any(|b| malo_refs(b).iter().any(|id| erzeugung.contains(id)));
4941        ConditionResult::from(referenced)
4942    }
4943
4944    /// [224] Wenn kein SG8 SEQ+ZE7 (Informative Daten der Tranche) auf die gleiche Marktlokation-ID mit dem RFF+Z18 (Marktlokation) referenziert wie dieses SG8 SEQ+Z98 (Informative Daten der Marktlokation)
4945    fn evaluate_224(&self, ctx: &EvaluationContext) -> ConditionResult {
4946        // Check: no SG8 with SEQ+ZE7 has RFF+Z18 referencing same Marktlokation
4947        let has_seq_ze7 = !ctx.find_segments_with_qualifier("SEQ", 0, "ZE7").is_empty();
4948        if !has_seq_ze7 {
4949            return ConditionResult::True;
4950        }
4951        ctx.any_group_has_qualifier_without("SEQ", 0, "ZE7", "RFF", 0, "Z18", &["SG4", "SG8"])
4952    }
4953
4954    /// [227] Wenn im SG8 SEQ+Z98 (Daten der Marktlokation) das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E14 (TLP/TEP) vorhanden
4955    fn evaluate_227(&self, ctx: &EvaluationContext) -> ConditionResult {
4956        // Check: in SG8 with SEQ+Z98, SG10 has CCI+++ZA6 and CAV+E14 or ETK
4957        let r1 = ctx.filtered_parent_child_has_qualifier(
4958            &["SG4", "SG8"],
4959            "SEQ",
4960            0,
4961            "Z98",
4962            "SG10",
4963            "CAV",
4964            0,
4965            "E14",
4966        );
4967        if r1 == ConditionResult::True {
4968            return ConditionResult::True;
4969        }
4970        ctx.filtered_parent_child_has_qualifier(
4971            &["SG4", "SG8"],
4972            "SEQ",
4973            0,
4974            "Z98",
4975            "SG10",
4976            "CAV",
4977            0,
4978            "ETK",
4979        )
4980    }
4981
4982    /// [229] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels)...
4983    // REVIEW: Detects if the same (SEQ-qualifier, Zeitraum-ID, RFF+Z33-Objektcode) triple appears in two different SG8 instances — which is what the condition guards against. Uses collect_group_values to correlate SEQ and RFF data by SG8 instance index. Limitation: collect_group_values may only return the first RFF per SG8 instance, so Z33 may be missed if it is not the first RFF in the group. (medium confidence)
4984    fn evaluate_229(&self, ctx: &EvaluationContext) -> ConditionResult {
4985        use std::collections::HashSet;
4986        let target_quals: HashSet<&str> = ["Z58", "ZC9", "ZD0", "ZD6"].iter().cloned().collect();
4987        // Collect per-SG8-instance: SEQ qualifier and Zeitraum-ID
4988        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
4989        let seq_zeitraums = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
4990        // Collect per-SG8-instance: RFF qualifier and DE1154 value
4991        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
4992        let rff_codes = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
4993        // Detect duplicate (seq_qual, zeitraum_id, objektcode) combinations across SG8 instances
4994        let mut seen: HashSet<(String, String, String)> = HashSet::new();
4995        for (idx, seq_qual) in &seq_quals {
4996            if seq_qual.is_empty() || !target_quals.contains(seq_qual.as_str()) {
4997                continue;
4998            }
4999            let zeitraum_id = seq_zeitraums
5000                .iter()
5001                .find(|(i, _)| i == idx)
5002                .map(|(_, v)| v.as_str())
5003                .unwrap_or("");
5004            // Find RFF+Z33 in this SG8 instance (correlation by instance index)
5005            for (ridx, rff_qual) in rff_quals.iter() {
5006                if ridx != idx || rff_qual != "Z33" {
5007                    continue;
5008                }
5009                if let Some((_, obj_code)) = rff_codes.iter().find(|(i, _)| i == ridx) {
5010                    if obj_code.is_empty() {
5011                        continue;
5012                    }
5013                    let key = (seq_qual.clone(), zeitraum_id.to_string(), obj_code.clone());
5014                    if seen.contains(&key) {
5015                        return ConditionResult::True;
5016                    }
5017                    seen.insert(key);
5018                }
5019            }
5020        }
5021        ConditionResult::False
5022    }
5023
5024    /// [232] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z52 (iMS) vorhanden
5025    fn evaluate_232(&self, ctx: &EvaluationContext) -> ConditionResult {
5026        let cci_segs = ctx.find_segments("CCI");
5027        let found = cci_segs.iter().any(|s| {
5028            s.elements
5029                .get(2)
5030                .and_then(|e| e.first())
5031                .is_some_and(|v| v == "Z83")
5032        });
5033        ConditionResult::from(found)
5034    }
5035
5036    /// [233] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle NB
5037    fn evaluate_233(&self, ctx: &EvaluationContext) -> ConditionResult {
5038        ctx.external.evaluate("recipient_role_check")
5039    }
5040
5041    /// [234] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z53 (kME/ mME) vorhanden
5042    fn evaluate_234(&self, ctx: &EvaluationContext) -> ConditionResult {
5043        let cci_segs = ctx.find_segments("CCI");
5044        let found = cci_segs.iter().any(|s| {
5045            s.elements
5046                .get(2)
5047                .and_then(|e| e.first())
5048                .is_some_and(|v| v == "Z83")
5049        });
5050        ConditionResult::from(found)
5051    }
5052
5053    /// [237] Wenn in derselben SG8 im DE7140 des PIA+Z02 (Gruppenartikel-ID / Artikel-ID) eine Gruppenartikel-ID / Artikel-ID mit 1-06 (entspricht Entgelte des Messstellenbetriebs bei kME) beginnt
5054    fn evaluate_237(&self, ctx: &EvaluationContext) -> ConditionResult {
5055        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5056        // Check: PIA+Z02 with code starting with 1-08 and containing AGS
5057        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5058        let found = pia_segs.iter().any(|s| {
5059            s.elements
5060                .first()
5061                .and_then(|e| e.first())
5062                .is_some_and(|v| v == "Z02")
5063                && s.elements
5064                    .get(1)
5065                    .and_then(|e| e.first())
5066                    .is_some_and(|v| v.starts_with("1-08") && v.contains("AGS"))
5067        });
5068        ConditionResult::from(found)
5069    }
5070
5071    /// [238] Wenn SG4 IDE+24 (Vorgang) nicht vorhanden
5072    fn evaluate_238(&self, ctx: &EvaluationContext) -> ConditionResult {
5073        let ide_segs = ctx.find_segments("IDE");
5074        let found = ide_segs.iter().any(|s| {
5075            s.elements
5076                .first()
5077                .and_then(|e| e.first())
5078                .is_some_and(|v| v == "24")
5079        });
5080        ConditionResult::from(!found)
5081    }
5082
5083    /// [239] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+Z65 (Wärme/Kälte) vorhanden
5084    fn evaluate_239(&self, ctx: &EvaluationContext) -> ConditionResult {
5085        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
5086        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
5087        let found = cci_segs.iter().any(|s| {
5088            s.elements
5089                .first()
5090                .and_then(|e| e.first())
5091                .is_some_and(|v| v == "Z17")
5092        });
5093        ConditionResult::from(found)
5094    }
5095
5096    /// [240] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) CCI+Z22++Z91 (Veräußerungsform der erzeugenden Marktlokation:: Marktprämie) vorhanden
5097    fn evaluate_240(&self, ctx: &EvaluationContext) -> ConditionResult {
5098        let seq_segs = ctx.find_segments("SEQ");
5099        let found = seq_segs.iter().any(|s| {
5100            s.elements
5101                .first()
5102                .and_then(|e| e.first())
5103                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
5104        });
5105        ConditionResult::from(found)
5106    }
5107
5108    /// [241] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle MSB
5109    fn evaluate_241(&self, ctx: &EvaluationContext) -> ConditionResult {
5110        ctx.external.evaluate("recipient_role_check")
5111    }
5112
5113    /// [243] Wenn SG10 CCI+6++ZA8 (Aggreg.verantw. NB) in dieser SG8 vorhanden
5114    fn evaluate_243(&self, ctx: &EvaluationContext) -> ConditionResult {
5115        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5116        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5117        let found = cci_segs.iter().any(|s| {
5118            s.elements
5119                .first()
5120                .and_then(|e| e.first())
5121                .is_some_and(|v| v == "6")
5122                && s.elements
5123                    .get(2)
5124                    .and_then(|e| e.first())
5125                    .is_some_and(|v| v == "ZA8")
5126        });
5127        ConditionResult::from(found)
5128    }
5129
5130    /// [244] Wenn SG10 CCI+6++ZA9 (Aggreg.verantw. ÜNB) in dieser SG8 vorhanden
5131    fn evaluate_244(&self, ctx: &EvaluationContext) -> ConditionResult {
5132        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5133        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5134        let found = cci_segs.iter().any(|s| {
5135            s.elements
5136                .first()
5137                .and_then(|e| e.first())
5138                .is_some_and(|v| v == "6")
5139                && s.elements
5140                    .get(2)
5141                    .and_then(|e| e.first())
5142                    .is_some_and(|v| v == "ZA9")
5143        });
5144        ConditionResult::from(found)
5145    }
5146
5147    /// [248] Wenn die Inbetriebnahme der verbrauchenden Marktlokation innerhalb der letzten drei Jahre war und die Energie der Marktlokation mit iMS ausgestatteten Messlokationen erfolgt
5148    fn evaluate_248(&self, _ctx: &EvaluationContext) -> ConditionResult {
5149        // TODO: implement
5150        ConditionResult::Unknown
5151    }
5152
5153    /// [249] Innerhalb eines SG4 IDE müssen alle DE1131 der SG4 STS+E01 (Status der Antwort) den identischen Wert enthalten
5154    fn evaluate_249(&self, ctx: &EvaluationContext) -> ConditionResult {
5155        // Check: all STS+E01 DE1131 values are identical
5156        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
5157        if sts_segs.len() <= 1 {
5158            return ConditionResult::True;
5159        }
5160        let first_val = sts_segs[0].elements.first().and_then(|e| e.get(1));
5161        let all_same = sts_segs
5162            .iter()
5163            .all(|s| s.elements.first().and_then(|e| e.get(1)) == first_val);
5164        ConditionResult::from(all_same)
5165    }
5166
5167    /// [251] Wenn MP-ID in SG2 NAD+MS (Nachrichtensender) in der Rolle BIKO
5168    fn evaluate_251(&self, ctx: &EvaluationContext) -> ConditionResult {
5169        ctx.external.evaluate("sender_role_check")
5170    }
5171
5172    /// [252] Wenn DE0068 vorhanden
5173    fn evaluate_252(&self, ctx: &EvaluationContext) -> ConditionResult {
5174        let unb_segs = ctx.find_segments("UNB");
5175        let found = unb_segs.iter().any(|s| {
5176            s.elements
5177                .get(4)
5178                .and_then(|e| e.first())
5179                .is_some_and(|v| !v.is_empty())
5180        });
5181        ConditionResult::from(found)
5182    }
5183
5184    /// [254] Es ist der Code \"AUA\" einzutragen
5185    fn evaluate_254(&self, _ctx: &EvaluationContext) -> ConditionResult {
5186        // Check: the code "AUA" is present
5187        ConditionResult::True // AUA is always the required code when this condition applies
5188    }
5189
5190    /// [255] Wenn Marktlokation am Redispatch 2.0 teilnimmt
5191    fn evaluate_255(&self, _ctx: &EvaluationContext) -> ConditionResult {
5192        // TODO: implement
5193        ConditionResult::Unknown
5194    }
5195
5196    /// [256] Wenn in derselben SG8 SEQ (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) das PIA+5+1-b?:1.8.e / 1-b?:2.8.e vorhanden
5197    fn evaluate_256(&self, ctx: &EvaluationContext) -> ConditionResult {
5198        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5199        // Check: PIA+5 has OBIS code matching 1-?:1.8.? or 1-?:2.8.? pattern
5200        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5201        let found = pia_segs.iter().any(|s| {
5202            s.elements
5203                .first()
5204                .and_then(|e| e.first())
5205                .is_some_and(|v| v == "5")
5206                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5207                    (v.contains(":1.8.") || v.contains(":2.8.")) && v.starts_with("1-")
5208                })
5209        });
5210        ConditionResult::from(found)
5211    }
5212
5213    /// [257] Wenn in derselben SG8 SEQ (OBIS-Daten der Marktlokation) das PIA+5+1-b?:1.9.e vorhanden
5214    fn evaluate_257(&self, ctx: &EvaluationContext) -> ConditionResult {
5215        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5216        // Check: PIA+5 has OBIS code matching 1-?:1.9.? pattern
5217        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5218        let found = pia_segs.iter().any(|s| {
5219            s.elements
5220                .first()
5221                .and_then(|e| e.first())
5222                .is_some_and(|v| v == "5")
5223                && s.elements
5224                    .get(1)
5225                    .and_then(|e| e.first())
5226                    .is_some_and(|v| v.contains(":1.9.") && v.starts_with("1-"))
5227        });
5228        ConditionResult::from(found)
5229    }
5230
5231    /// [259] Wenn in diesem PIA der Code im DE7140 mit 1-01, 1-03, 1-05 enthalten ist
5232    fn evaluate_259(&self, ctx: &EvaluationContext) -> ConditionResult {
5233        let pia_segs = ctx.find_segments("PIA");
5234        let found = pia_segs.iter().any(|s| {
5235            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5236                let prefixes = ["1-01", "1-03", "1-05"];
5237                prefixes.iter().any(|p| v.starts_with(p))
5238            })
5239        });
5240        ConditionResult::from(found)
5241    }
5242
5243    /// [261] Wenn in diesem PIA der Code im DE7140 mit 1-07-1/2, 1-10-1/2/4/5/6/7/8/9, 1-11-1 beginnt
5244    fn evaluate_261(&self, ctx: &EvaluationContext) -> ConditionResult {
5245        let pia_segs = ctx.find_segments("PIA");
5246        let found = pia_segs.iter().any(|s| {
5247            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5248                let prefixes = [
5249                    "1-07-1", "2", "1-10-1", "2", "4", "5", "6", "7", "8", "9", "1-11-1",
5250                ];
5251                prefixes.iter().any(|p| v.starts_with(p))
5252            })
5253        });
5254        ConditionResult::from(found)
5255    }
5256
5257    /// [262] Wenn Produkt an der Marktlokation vorhanden ist
5258    fn evaluate_262(&self, _ctx: &EvaluationContext) -> ConditionResult {
5259        // TODO: implement
5260        ConditionResult::Unknown
5261    }
5262
5263    /// [265] Wenn im SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+Z36 (TEP m. Referenzmessung) vorhanden
5264    // HAND-EDITED (mako-twin, 55043): the generated version looked for
5265    // CAV+E14/ETK in any SG10 under SEQ+Z01/Z98, ignoring CCI+++ZA6; the AHB
5266    // asks for CAV+Z36 (TEP m. Referenzmessung) in the SG10 CCI+++ZA6.
5267    fn evaluate_265(&self, ctx: &EvaluationContext) -> ConditionResult {
5268        let Some(blocks) = sg8_blocks(ctx) else {
5269            return ConditionResult::Unknown;
5270        };
5271        let found = blocks
5272            .iter()
5273            .filter(|b| b.seq == "Z01" || b.seq == "Z98")
5274            .any(|b| {
5275                b.sg10.iter().any(|(ccis, cavs)| {
5276                    ccis.iter().any(|c| component(c, 2, 0) == Some("ZA6"))
5277                        && cavs.iter().any(|c| component(c, 0, 0) == Some("Z36"))
5278                })
5279            });
5280        ConditionResult::from(found)
5281    }
5282
5283    /// [266] Wenn im SG8 SEQ+Z01 (Daten der Marktlokation) mit identischer Zeitraum-ID im SG8 SEQ+Z01 DE1050 wie in dieser SG8 SEQ, das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis vo...
5284    // REVIEW: Navigates all SG8 instances and their SG10 children, checking for CCI with elements[2][0]=ZA6 (Prognosegrundlage) co-occurring with CAV with elements[0][0]=Z36 (TEP m. Referenzmessung) in the same SG10 instance. The parent SG8 SEQ+Z01 filter and Zeitraum-ID cross-matching are not verified due to lack of direct SG8-level segment access; relies on CCI+ZA6/CAV+Z36 being specific enough to Marktlokation groups. (medium confidence)
5285    fn evaluate_266(&self, ctx: &EvaluationContext) -> ConditionResult {
5286        let nav = match ctx.navigator {
5287            Some(n) => n,
5288            None => {
5289                // Fallback: check any SG8+SEQ+Z01 child SG10 has CAV+Z36
5290                return ctx.filtered_parent_child_has_qualifier(
5291                    &["SG4", "SG8"],
5292                    "SEQ",
5293                    0,
5294                    "Z01",
5295                    "SG10",
5296                    "CAV",
5297                    0,
5298                    "Z36",
5299                );
5300            }
5301        };
5302        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5303        for i in 0..sg8_count {
5304            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
5305            for j in 0..sg10_count {
5306                // CCI+++ZA6 means elements[2][0] == "ZA6"
5307                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
5308                let has_cci_za6 = ccis.iter().any(|s| {
5309                    s.elements
5310                        .get(2)
5311                        .and_then(|e: &Vec<String>| e.first())
5312                        .is_some_and(|v: &String| v == "ZA6")
5313                });
5314                if has_cci_za6 {
5315                    let cavs =
5316                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
5317                    if cavs.iter().any(|s| {
5318                        s.elements
5319                            .first()
5320                            .and_then(|e: &Vec<String>| e.first())
5321                            .is_some_and(|v: &String| v == "Z36")
5322                    }) {
5323                        return ConditionResult::True;
5324                    }
5325                }
5326            }
5327        }
5328        ConditionResult::False
5329    }
5330
5331    /// [267] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
5332    fn evaluate_267(&self, ctx: &EvaluationContext) -> ConditionResult {
5333        let seq_segs = ctx.find_segments("SEQ");
5334        let found = seq_segs.iter().any(|s| {
5335            s.elements
5336                .first()
5337                .and_then(|e| e.first())
5338                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
5339        });
5340        ConditionResult::from(!found)
5341    }
5342
5343    /// [268] Wenn der Code im DE3207 in der \"EDI@Energy Codeliste der europäischen Ländercodes\" in der Spalte \"PLZ vorhanden\" ein \"X\" aufgeführt ist
5344    fn evaluate_268(&self, _ctx: &EvaluationContext) -> ConditionResult {
5345        // Most European countries have postal codes — default to true
5346        ConditionResult::True
5347    }
5348
5349    /// [269] Es sind alle OBIS-Kennzahlen gem. EDI@Energy Codeliste der OBIS-Kennzahlen und Medien für den deutschen Energiemarkt Kap.3 anzugeben welche an der Tranche erforderlich sind
5350    fn evaluate_269(&self, _ctx: &EvaluationContext) -> ConditionResult {
5351        // TODO: implement
5352        ConditionResult::Unknown
5353    }
5354
5355    /// [270] Wenn in diesem PIA der Code im DE7140 mit 1-02, 1-04, 1-06, 1-09 beginnt
5356    fn evaluate_270(&self, ctx: &EvaluationContext) -> ConditionResult {
5357        let pia_segs = ctx.find_segments("PIA");
5358        let found = pia_segs.iter().any(|s| {
5359            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5360                let prefixes = ["1-02", "1-04", "1-06", "1-09"];
5361                prefixes.iter().any(|p| v.starts_with(p))
5362            })
5363        });
5364        ConditionResult::from(found)
5365    }
5366
5367    /// [273] Wenn in derselben SG8 SEQ+Z20 (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) das PIA+5+1-b?:1.8.e / 1-b?:2.8.e / 1-b?:3.8.e / 1-b?:4.8.e / 1-b?:5.8.e / 1-b?:6.8.e / 1-b?:7.8.e / 1-b?:8.8.e ...
5368    fn evaluate_273(&self, ctx: &EvaluationContext) -> ConditionResult {
5369        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5370        // Check: PIA+5 has OBIS code matching energy quantity patterns in SG8 SEQ+Z20
5371        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5372        let found = pia_segs.iter().any(|s| {
5373            s.elements
5374                .first()
5375                .and_then(|e| e.first())
5376                .is_some_and(|v| v == "5")
5377                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5378                    v.starts_with("1-")
5379                        && (v.contains(":1.8.") || v.contains(":2.8.") || v.contains(":1.29."))
5380                })
5381        });
5382        ConditionResult::from(found)
5383    }
5384
5385    /// [279] Wenn STS+7++xxx+ZW4 / ZAP (Transaktionsgrundergänzung  verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
5386    fn evaluate_279(&self, ctx: &EvaluationContext) -> ConditionResult {
5387        let sts_segs = ctx.find_segments("STS");
5388        let found = sts_segs.iter().any(|s| {
5389            s.elements
5390                .first()
5391                .and_then(|e| e.first())
5392                .is_some_and(|v| v == "7")
5393                && s.elements
5394                    .get(3)
5395                    .and_then(|e| e.first())
5396                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
5397        });
5398        ConditionResult::from(found)
5399    }
5400
5401    /// [280] Nur MP-ID aus Sparte Strom
5402    fn evaluate_280(&self, ctx: &EvaluationContext) -> ConditionResult {
5403        // Check: Marktpartner-ID is from Sparte Strom
5404        // Approximate: check NAD+MS C082 for Strom MP-ID format
5405        ctx.external.evaluate("sector_check")
5406    }
5407
5408    /// [282] Wenn in diesem PIA der Code im DE7140 1-01-6-005, 1-01-9-001/002, 1-07-3-001, 1- 08-1-001, 1-08-3-001, 1-08-4-001/002/003/004, 1-10-3-001 enthalten ist
5409    fn evaluate_282(&self, ctx: &EvaluationContext) -> ConditionResult {
5410        // Check: PIA DE7140 has one of the specific codes
5411        let pia_segs = ctx.find_segments("PIA");
5412        let found = pia_segs.iter().any(|s| {
5413            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5414                v.starts_with("1-01-6-005")
5415                    || v.starts_with("1-01-9-001")
5416                    || v.starts_with("1-01-9-002")
5417                    || v.starts_with("1-07-3-001")
5418                    || v.starts_with("1-08-1-001")
5419                    || v.starts_with("1-08-3-001")
5420                    || v.starts_with("1-08-4-001")
5421                    || v.starts_with("1-08-4-002")
5422                    || v.starts_with("1-08-4-003")
5423                    || v.starts_with("1-08-5")
5424            })
5425        });
5426        ConditionResult::from(found)
5427    }
5428
5429    /// [284] Erlaubte Codes aus der Codeliste der Gruppenartikel- und Artikel-ID sind in der Spalte UTILMD/Codeverwendung mit X gekennzeichnet
5430    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
5431    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
5432    // Regenerating this function should preserve the shape, but re-verify
5433    // against the committed body if generate-conditions is run.
5434    fn evaluate_284(&self, ctx: &EvaluationContext) -> ConditionResult {
5435        // [284] Erlaubte Codes aus der Codeliste der Gruppenartikel-
5436        // und Artikel-ID mit X in der Spalte UTILMD/Codeverwendung.
5437        //
5438        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
5439        // narrows to the enclosing SG8 instance; otherwise falls back to
5440        // message-wide find_segments. Matches the AHB wording
5441        // "in derselben SG8 im PIA DE7140 ein Code...".
5442        use crate::eval::CodelistColumn;
5443        let any_match = ctx.scoped_find_segments("PIA").iter().any(|s| {
5444            let is_z02 = s
5445                .elements
5446                .first()
5447                .and_then(|e| e.first())
5448                .is_some_and(|v| v == "Z02");
5449            if !is_z02 {
5450                return false;
5451            }
5452            let code = match s.elements.get(1).and_then(|e| e.first()) {
5453                Some(c) if !c.is_empty() => c,
5454                _ => return false,
5455            };
5456            ctx.codelist
5457                .flag(code, CodelistColumn::UtilmdCodeverwendung)
5458                .unwrap_or(false)
5459        });
5460        ConditionResult::from(any_match)
5461    }
5462
5463    /// [285] Wenn in diesem PIA der Code im DE7140 mit 1-08-3-AGS angegeben ist
5464    fn evaluate_285(&self, ctx: &EvaluationContext) -> ConditionResult {
5465        // Check: PIA DE7140 starts with 1-08-3-AGS
5466        let pia_segs = ctx.find_segments("PIA");
5467        let found = pia_segs.iter().any(|s| {
5468            s.elements
5469                .get(1)
5470                .and_then(|e| e.first())
5471                .is_some_and(|v| v.contains("1-08-3") && v.contains("AGS"))
5472        });
5473        ConditionResult::from(found)
5474    }
5475
5476    /// [286] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/2/4/5-AGS-KG
5477    fn evaluate_286(&self, ctx: &EvaluationContext) -> ConditionResult {
5478        // Check: PIA DE7140 matches 1-08-1/2/4/5-AGS-KG pattern
5479        let pia_segs = ctx.find_segments("PIA");
5480        let found = pia_segs.iter().any(|s| {
5481            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5482                (v.starts_with("1-08-1")
5483                    || v.starts_with("1-08-2")
5484                    || v.starts_with("1-08-4")
5485                    || v.starts_with("1-08-5"))
5486                    && v.contains("AGS")
5487                    && v.contains("KG")
5488            })
5489        });
5490        ConditionResult::from(found)
5491    }
5492
5493    /// [287] Erlaubte Codes aus der PRICAT BGM+ Z70 (Preisblatt Netznutzung) des verantwortlichen NB
5494    fn evaluate_287(&self, _ctx: &EvaluationContext) -> ConditionResult {
5495        // TODO: implement
5496        ConditionResult::Unknown
5497    }
5498
5499    /// [288] Wenn in derselben SG8 (OBIS-Daten der Marktlokation) das PIA+5+1-b?:2.9.e vorhanden
5500    fn evaluate_288(&self, ctx: &EvaluationContext) -> ConditionResult {
5501        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5502        // Check: PIA+5 has OBIS code matching 1-?:2.9.? pattern
5503        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5504        let found = pia_segs.iter().any(|s| {
5505            s.elements
5506                .first()
5507                .and_then(|e| e.first())
5508                .is_some_and(|v| v == "5")
5509                && s.elements
5510                    .get(1)
5511                    .and_then(|e| e.first())
5512                    .is_some_and(|v| v.contains(":2.9.") && v.starts_with("1-"))
5513        });
5514        ConditionResult::from(found)
5515    }
5516
5517    /// [291] Wenn in derselben SG8 (Zuordnung Lokation zum Objektcode des Lokationsbündels) das RFF+Z37 vorhanden
5518    fn evaluate_291(&self, ctx: &EvaluationContext) -> ConditionResult {
5519        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5520        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
5521        let found = rff_segs.iter().any(|s| {
5522            s.elements
5523                .first()
5524                .and_then(|e| e.first())
5525                .is_some_and(|v| v == "Z37")
5526        });
5527        ConditionResult::from(found)
5528    }
5529
5530    /// [292] Es sind nur die Produkt-Codes erlaubt, die in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTILMD)\" in der Spalte \"Anmeldung einer Zuordnung des LFN (UTIL...
5531    fn evaluate_292(&self, ctx: &EvaluationContext) -> ConditionResult {
5532        ctx.external.evaluate("code_list_membership_check")
5533    }
5534
5535    /// [293] Wenn SG5 LOC+Z22 (ruhende Marktlokation) vorhanden
5536    fn evaluate_293(&self, ctx: &EvaluationContext) -> ConditionResult {
5537        let loc_segs = ctx.find_segments("LOC");
5538        let found = loc_segs.iter().any(|s| {
5539            s.elements
5540                .first()
5541                .and_then(|e| e.first())
5542                .is_some_and(|v| v == "Z22")
5543        });
5544        ConditionResult::from(found)
5545    }
5546
5547    /// [294] Wenn SG5 LOC+Z18 (Netzlokation) vorhanden
5548    fn evaluate_294(&self, ctx: &EvaluationContext) -> ConditionResult {
5549        let loc_segs = ctx.find_segments("LOC");
5550        let found = loc_segs.iter().any(|s| {
5551            s.elements
5552                .first()
5553                .and_then(|e| e.first())
5554                .is_some_and(|v| v == "Z18")
5555        });
5556        ConditionResult::from(found)
5557    }
5558
5559    /// [295] Wenn SG5 LOC+Z15 (MaBiS-Zählpunkt) nicht vorhanden
5560    fn evaluate_295(&self, ctx: &EvaluationContext) -> ConditionResult {
5561        let loc_segs = ctx.find_segments("LOC");
5562        let found = loc_segs.iter().any(|s| {
5563            s.elements
5564                .first()
5565                .and_then(|e| e.first())
5566                .is_some_and(|v| v == "Z15")
5567        });
5568        ConditionResult::from(!found)
5569    }
5570
5571    /// [296] Wenn SG5 LOC+Z20 (Technische Ressource) vorhanden
5572    fn evaluate_296(&self, ctx: &EvaluationContext) -> ConditionResult {
5573        let loc_segs = ctx.find_segments("LOC");
5574        let found = loc_segs.iter().any(|s| {
5575            s.elements
5576                .first()
5577                .and_then(|e| e.first())
5578                .is_some_and(|v| v == "Z20")
5579        });
5580        ConditionResult::from(found)
5581    }
5582
5583    /// [297] #kv# Wenn SG5 LOC+Z18 (Netzlokation) nicht vorhanden
5584    fn evaluate_297(&self, ctx: &EvaluationContext) -> ConditionResult {
5585        let loc_segs = ctx.find_segments("LOC");
5586        let found = loc_segs.iter().any(|s| {
5587            s.elements
5588                .first()
5589                .and_then(|e| e.first())
5590                .is_some_and(|v| v == "Z18")
5591        });
5592        ConditionResult::from(!found)
5593    }
5594
5595    /// [298] Wenn SG5 LOC+Z20 (Technische Ressource) nicht vorhanden
5596    fn evaluate_298(&self, ctx: &EvaluationContext) -> ConditionResult {
5597        let loc_segs = ctx.find_segments("LOC");
5598        let found = loc_segs.iter().any(|s| {
5599            s.elements
5600                .first()
5601                .and_then(|e| e.first())
5602                .is_some_and(|v| v == "Z20")
5603        });
5604        ConditionResult::from(!found)
5605    }
5606
5607    /// [299] #kv# Wenn SG5 LOC+Z19 (Steuerbare Ressource) nicht vorhanden
5608    fn evaluate_299(&self, ctx: &EvaluationContext) -> ConditionResult {
5609        let loc_segs = ctx.find_segments("LOC");
5610        let found = loc_segs.iter().any(|s| {
5611            s.elements
5612                .first()
5613                .and_then(|e| e.first())
5614                .is_some_and(|v| v == "Z19")
5615        });
5616        ConditionResult::from(!found)
5617    }
5618
5619    /// [300] Wenn SG8 SEQ+Z15/ ZE7 (Daten der Tranche) nicht vorhanden
5620    fn evaluate_300(&self, ctx: &EvaluationContext) -> ConditionResult {
5621        let seq_segs = ctx.find_segments("SEQ");
5622        let found = seq_segs.iter().any(|s| {
5623            s.elements
5624                .first()
5625                .and_then(|e| e.first())
5626                .is_some_and(|v| ["Z15", "ZE7"].contains(&v.as_str()))
5627        });
5628        ConditionResult::from(!found)
5629    }
5630
5631    /// [301] Wenn BGM+E03 (Änderungsmeldung) vorhanden
5632    fn evaluate_301(&self, ctx: &EvaluationContext) -> ConditionResult {
5633        let bgm_segs = ctx.find_segments("BGM");
5634        let found = bgm_segs.iter().any(|s| {
5635            s.elements
5636                .first()
5637                .and_then(|e| e.first())
5638                .is_some_and(|v| v == "E03")
5639        });
5640        ConditionResult::from(found)
5641    }
5642
5643    /// [302] Wenn SG8 SEQ+Z08 (Profilschardaten) nicht vorhanden
5644    fn evaluate_302(&self, ctx: &EvaluationContext) -> ConditionResult {
5645        let seq_segs = ctx.find_segments("SEQ");
5646        let found = seq_segs.iter().any(|s| {
5647            s.elements
5648                .first()
5649                .and_then(|e| e.first())
5650                .is_some_and(|v| ["Z08"].contains(&v.as_str()))
5651        });
5652        ConditionResult::from(!found)
5653    }
5654
5655    /// [303] Wenn SG8 SEQ+Z21 (Profildaten) nicht vorhanden
5656    fn evaluate_303(&self, ctx: &EvaluationContext) -> ConditionResult {
5657        let seq_segs = ctx.find_segments("SEQ");
5658        let found = seq_segs.iter().any(|s| {
5659            s.elements
5660                .first()
5661                .and_then(|e| e.first())
5662                .is_some_and(|v| ["Z21"].contains(&v.as_str()))
5663        });
5664        ConditionResult::from(!found)
5665    }
5666
5667    /// [304] Wenn SG8 SEQ+Z22 (Daten der Summenzeitreihe) vorhanden
5668    fn evaluate_304(&self, ctx: &EvaluationContext) -> ConditionResult {
5669        let seq_segs = ctx.find_segments("SEQ");
5670        let found = seq_segs.iter().any(|s| {
5671            s.elements
5672                .first()
5673                .and_then(|e| e.first())
5674                .is_some_and(|v| ["Z22"].contains(&v.as_str()))
5675        });
5676        ConditionResult::from(found)
5677    }
5678
5679    /// [305] Wenn SG8 SEQ+Z24 (Daten der Überführungszeitreihe) vorhanden
5680    fn evaluate_305(&self, ctx: &EvaluationContext) -> ConditionResult {
5681        let seq_segs = ctx.find_segments("SEQ");
5682        let found = seq_segs.iter().any(|s| {
5683            s.elements
5684                .first()
5685                .and_then(|e| e.first())
5686                .is_some_and(|v| ["Z24"].contains(&v.as_str()))
5687        });
5688        ConditionResult::from(found)
5689    }
5690
5691    /// [306] Wenn SG5 LOC+Z22 (ruhende Marktlokation) mit derselben Zeitraum-ID im DE3224 vorhanden wie in dieser SG8 im DE1050 (Referenz auf Zeitraum-ID) des SEQ+Z58
5692    fn evaluate_306(&self, ctx: &EvaluationContext) -> ConditionResult {
5693        use std::collections::HashSet;
5694        // Collect Zeitraum-IDs from SG5 LOC+Z22 (Ruhende Marktlokation) — DE3224 at elements[1][3]
5695        let loc_z22_ids: HashSet<String> = ctx
5696            .find_segments_with_qualifier("LOC", 0, "Z22")
5697            .into_iter()
5698            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
5699            .filter(|v| !v.is_empty())
5700            .collect();
5701        if loc_z22_ids.is_empty() {
5702            return ConditionResult::False;
5703        }
5704        // Check if any SG8 SEQ+Z58 has DE1050 (elements[1][0]) matching a LOC+Z22 DE3224
5705        let has_match = ctx
5706            .find_segments_with_qualifier("SEQ", 0, "Z58")
5707            .iter()
5708            .any(|s| {
5709                s.elements
5710                    .get(1)
5711                    .and_then(|e: &Vec<String>| e.first())
5712                    .is_some_and(|v: &String| !v.is_empty() && loc_z22_ids.contains(v.as_str()))
5713            });
5714        ConditionResult::from(has_match)
5715    }
5716
5717    /// [307] Wenn SG5 LOC+Z22 (ruhende Marktlokation) mit derselben Zeitraum-ID im DE3224 nicht vorhanden wie in dieser SG8 im DE1050 (Referenz auf Zeitraum-ID) des SEQ+Z58
5718    // REVIEW: Collects Zeitraum-IDs from SG5 LOC+Z22 segments (DE3224 = elements[1][3]) and SEQ+Z58 segments (DE1050 = elements[1][0]). Returns True when any SEQ+Z58 has a Zeitraum-ID with no matching LOC+Z22 entry. Message-wide fallback since group-level Zeitraum-ID isolation is not possible without navigator. (medium confidence)
5719    fn evaluate_307(&self, ctx: &EvaluationContext) -> ConditionResult {
5720        let seq_segs = ctx.find_segments_with_qualifier("SEQ", 0, "Z58");
5721        if seq_segs.is_empty() {
5722            return ConditionResult::Unknown;
5723        }
5724        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z22");
5725        let loc_z22_zeitraum_ids: Vec<&str> = loc_segs
5726            .iter()
5727            .filter_map(|s| s.elements.get(1).and_then(|e| e.get(3)).map(|v| v.as_str()))
5728            .filter(|v| !v.is_empty())
5729            .collect();
5730        ConditionResult::from(seq_segs.iter().any(|seq| {
5731            let zid = seq
5732                .elements
5733                .get(1)
5734                .and_then(|e: &Vec<String>| e.first())
5735                .map(|v| v.as_str())
5736                .unwrap_or("");
5737            !zid.is_empty() && !loc_z22_zeitraum_ids.contains(&zid)
5738        }))
5739    }
5740
5741    /// [309] Wenn SG5 LOC+Z22 (ruhende Marktlokation) nicht vorhanden
5742    fn evaluate_309(&self, ctx: &EvaluationContext) -> ConditionResult {
5743        let loc_segs = ctx.find_segments("LOC");
5744        let found = loc_segs.iter().any(|s| {
5745            s.elements
5746                .first()
5747                .and_then(|e| e.first())
5748                .is_some_and(|v| v == "Z22")
5749        });
5750        ConditionResult::from(!found)
5751    }
5752
5753    /// [314] Es sind alle OBIS-Kennzahlen gem. Codeliste der OBIS-Kennzahlen und Medien Kapitel 3 anzugeben welche an der Marktlokation erforderlich sind. Der Mindestumfang der OBIS-Kennzahlen ergibt sich aus d...
5754    fn evaluate_314(&self, ctx: &EvaluationContext) -> ConditionResult {
5755        ctx.external.evaluate("code_list_membership_check")
5756    }
5757
5758    /// [315] Wenn BGM+Z88 (Datenclearing) vorhanden
5759    fn evaluate_315(&self, ctx: &EvaluationContext) -> ConditionResult {
5760        ctx.external.evaluate("data_clearing_required")
5761    }
5762
5763    /// [316] Wenn in derselben SG8 das RFF+Z14 (Smartmeter-Gateway) nicht vorhanden
5764    fn evaluate_316(&self, ctx: &EvaluationContext) -> ConditionResult {
5765        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5766        ctx.this_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
5767    }
5768
5769    /// [317] Es ist derselbe Wert wie im DE2380 von DTM+92 (Datum Vertragsbeginn) einzutragen
5770    fn evaluate_317(&self, ctx: &EvaluationContext) -> ConditionResult {
5771        // Check: DTM+92 (Datum Vertragsbeginn) exists — value identity is structural
5772        let found = !ctx.find_segments_with_qualifier("DTM", 0, "92").is_empty();
5773        if found {
5774            ConditionResult::True
5775        } else {
5776            ConditionResult::Unknown
5777        }
5778    }
5779
5780    /// [318] Es ist derselbe Wert wie im DE2380 von DTM+93 (Datum Vertragende) einzutragen
5781    fn evaluate_318(&self, ctx: &EvaluationContext) -> ConditionResult {
5782        // Check: DTM+93 (Datum Vertragende) exists — value identity is structural
5783        let found = !ctx.find_segments_with_qualifier("DTM", 0, "93").is_empty();
5784        if found {
5785            ConditionResult::True
5786        } else {
5787            ConditionResult::Unknown
5788        }
5789    }
5790
5791    /// [321] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
5792    fn evaluate_321(&self, ctx: &EvaluationContext) -> ConditionResult {
5793        let com_segs = ctx.find_segments("COM");
5794        let has_em = com_segs.iter().any(|s| {
5795            s.elements
5796                .first()
5797                .and_then(|e| e.get(1))
5798                .is_some_and(|v| v == "EM")
5799        });
5800        ConditionResult::from(has_em)
5801    }
5802
5803    /// [322] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
5804    fn evaluate_322(&self, ctx: &EvaluationContext) -> ConditionResult {
5805        let com_segs = ctx.find_segments("COM");
5806        let has_phone = com_segs.iter().any(|s| {
5807            s.elements
5808                .first()
5809                .and_then(|e| e.get(1))
5810                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
5811        });
5812        ConditionResult::from(has_phone)
5813    }
5814
5815    /// [323] Es sind alle OBIS-Kennzahlen gem. EDI@Energy Codeliste der OBIS-Kennzahlen und Medien für den deutschen Energiemarkt Kap. 3. anzugeben, welche an der Zähleinrichtung genutzt werden. Der Mindestum...
5816    fn evaluate_323(&self, ctx: &EvaluationContext) -> ConditionResult {
5817        ctx.external.evaluate("code_list_membership_check")
5818    }
5819
5820    /// [327] Wenn die Zeitraum-ID im DE1050 dieser SG8 SEQ, welche mit der Zeitraum-ID einer SG6 RFF+Z47/Z48/Z49 (Verwendungszeitraum der Daten) identisch ist und in deren SG das SG6 DTM+Z25 (Verwendung der Dat...
5821    // REVIEW: Step 1: Collect all SEQ DE1050 Zeitraum-IDs. Step 2: Find RFF+Z47/Z48/Z49 (Verwendungszeitraum) whose DE1156 (elements[0][2]) matches. Step 3: Check co-located DTM+Z25 DE2380 (elements[0][1]) >= threshold after normalising EDIFACT escape '?+' to '+'. Falls back to message-wide DTM search which may give false positives if multiple SG6 groups are present, but is sufficient for typical single-transaction messages. (medium confidence)
5822    fn evaluate_327(&self, ctx: &EvaluationContext) -> ConditionResult {
5823        const THRESHOLD: &str = "202212312300+00";
5824        let seq_zeitraum_ids: Vec<String> = ctx
5825            .find_segments("SEQ")
5826            .iter()
5827            .filter_map(|s| {
5828                s.elements
5829                    .get(1)
5830                    .and_then(|e: &Vec<String>| e.first())
5831                    .cloned()
5832            })
5833            .filter(|v| !v.is_empty())
5834            .collect();
5835        if seq_zeitraum_ids.is_empty() {
5836            return ConditionResult::Unknown;
5837        }
5838        let rff_segs = ctx.find_segments("RFF");
5839        let matching_rff = rff_segs.iter().any(|rff| {
5840            let qual = rff
5841                .elements
5842                .first()
5843                .and_then(|e: &Vec<String>| e.first())
5844                .map(|v| v.as_str())
5845                .unwrap_or("");
5846            if !["Z47", "Z48", "Z49"].contains(&qual) {
5847                return false;
5848            }
5849            let zid = rff
5850                .elements
5851                .first()
5852                .and_then(|e| e.get(2))
5853                .map(|v| v.as_str())
5854                .unwrap_or("");
5855            !zid.is_empty() && seq_zeitraum_ids.iter().any(|id| id.as_str() == zid)
5856        });
5857        if !matching_rff {
5858            return ConditionResult::False;
5859        }
5860        for dtm in ctx.find_segments_with_qualifier("DTM", 0, "Z25") {
5861            let val = dtm
5862                .elements
5863                .first()
5864                .and_then(|e| e.get(1))
5865                .map(|v| v.replace("?+", "+"))
5866                .unwrap_or_default();
5867            if !val.is_empty() && val.as_str() >= THRESHOLD {
5868                return ConditionResult::True;
5869            }
5870        }
5871        ConditionResult::False
5872    }
5873
5874    /// [329] Wenn in dieser SG4 IDE+24 das STS+E01++A03: E_0096 bzw. A04:E_0097 (Status der Antwort) nicht vorhanden
5875    fn evaluate_329(&self, ctx: &EvaluationContext) -> ConditionResult {
5876        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
5877        // Check: STS+E01 does NOT have A03 with E_0096 or A04 with E_0097
5878        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
5879        let has_a03_e0096 = sts_segs.iter().any(|s| {
5880            s.elements
5881                .get(2)
5882                .and_then(|e| e.first())
5883                .is_some_and(|v| v == "A03")
5884                && s.elements
5885                    .get(2)
5886                    .and_then(|e| e.get(1))
5887                    .is_some_and(|v| v == "E_0096")
5888        });
5889        let has_a04_e0097 = sts_segs.iter().any(|s| {
5890            s.elements
5891                .get(2)
5892                .and_then(|e| e.first())
5893                .is_some_and(|v| v == "A04")
5894                && s.elements
5895                    .get(2)
5896                    .and_then(|e| e.get(1))
5897                    .is_some_and(|v| v == "E_0097")
5898        });
5899        ConditionResult::from(!has_a03_e0096 && !has_a04_e0097)
5900    }
5901
5902    /// [331] Wenn im SG8 SEQ+Z98 (Daten der Marktlokation) das SG10 CCI+6++ZA9 (Aggreg. verantw. ÜNB) nicht vorhanden
5903    fn evaluate_331(&self, ctx: &EvaluationContext) -> ConditionResult {
5904        let seq_segs = ctx.find_segments("SEQ");
5905        let has_seq = seq_segs.iter().any(|s| {
5906            s.elements
5907                .first()
5908                .and_then(|e| e.first())
5909                .is_some_and(|v| v == "Z98")
5910        });
5911        let cci_segs = ctx.find_segments("CCI");
5912        let has_cci = cci_segs.iter().any(|s| {
5913            s.elements
5914                .first()
5915                .and_then(|e| e.first())
5916                .is_some_and(|v| v == "6")
5917                && s.elements
5918                    .get(2)
5919                    .and_then(|e| e.first())
5920                    .is_some_and(|v| v == "ZA9")
5921        });
5922        ConditionResult::from(!(has_seq && has_cci))
5923    }
5924
5925    /// [332] Wenn im SG8 SEQ+Z01 (Daten der Marktlokation) mit identischer Zeitraum-ID im DE1050 wie in dieser SG8, das SG10 CCI+6++ZA9 (Aggreg. verantw. ÜNB) nicht vorhanden
5926    // REVIEW: Checks whether any SG8 instance lacks CCI+6++ZA9 (Aggregationsverantwortung ÜNB, elements[0]='6' and elements[2][0]='ZA9') in its SG10 children. With navigator: iterates SG8 instances and their SG10 children. Without navigator: falls back to message-wide CCI+6 with ZA9 check. The Zeitraum-ID cross-reference between this SG8 and the Z01 SG8 cannot be fully verified without direct SG8-level segment access via navigator, so this is an approximation that may produce false positives in multi-SG4-group messages. (medium confidence)
5927    fn evaluate_332(&self, ctx: &EvaluationContext) -> ConditionResult {
5928        let nav = match ctx.navigator {
5929            Some(n) => n,
5930            None => {
5931                if ctx.find_segments_with_qualifier("SEQ", 0, "Z01").is_empty() {
5932                    return ConditionResult::Unknown;
5933                }
5934                let has_cci_za9 = ctx
5935                    .find_segments_with_qualifier("CCI", 0, "6")
5936                    .iter()
5937                    .any(|s| {
5938                        s.elements
5939                            .get(2)
5940                            .and_then(|e: &Vec<String>| e.first())
5941                            .map(|v| v.as_str())
5942                            == Some("ZA9")
5943                    });
5944                return ConditionResult::from(!has_cci_za9);
5945            }
5946        };
5947        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5948        if sg8_count == 0 {
5949            return ConditionResult::Unknown;
5950        }
5951        for sg8_idx in 0..sg8_count {
5952            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], sg8_idx, "SG10");
5953            let mut has_cci_za9 = false;
5954            for sg10_idx in 0..sg10_count {
5955                let ccis = nav.find_segments_in_child_group(
5956                    "CCI",
5957                    &["SG4", "SG8"],
5958                    sg8_idx,
5959                    "SG10",
5960                    sg10_idx,
5961                );
5962                if ccis.iter().any(|s| {
5963                    s.elements
5964                        .first()
5965                        .and_then(|e: &Vec<String>| e.first())
5966                        .map(|v| v.as_str())
5967                        == Some("6")
5968                        && s.elements
5969                            .get(2)
5970                            .and_then(|e: &Vec<String>| e.first())
5971                            .map(|v| v.as_str())
5972                            == Some("ZA9")
5973                }) {
5974                    has_cci_za9 = true;
5975                    break;
5976                }
5977            }
5978            if !has_cci_za9 {
5979                return ConditionResult::True;
5980            }
5981        }
5982        ConditionResult::False
5983    }
5984
5985    /// [333] Wenn in diesem RFF \"Referenz auf die Lokationsbündelstruktur\" im DE1153 der Code Z31 \"Lokationsbündelstruktur\" vorhanden ist
5986    fn evaluate_333(&self, ctx: &EvaluationContext) -> ConditionResult {
5987        let rff_segs = ctx.find_segments("RFF");
5988        let found = rff_segs.iter().any(|s| {
5989            s.elements
5990                .first()
5991                .and_then(|e| e.first())
5992                .is_some_and(|v| v == "Z31")
5993        });
5994        ConditionResult::from(found)
5995    }
5996
5997    /// [334] Nur MP-ID aus Sparte Gas
5998    fn evaluate_334(&self, ctx: &EvaluationContext) -> ConditionResult {
5999        // Check: Marktpartner-ID is from Sparte Gas
6000        ctx.external.evaluate("sector_check")
6001    }
6002
6003    /// [335] Wenn in dieser SG8 im SEQ+Z58 (Zuordnung Lokation zum Objektcode des Lokationsbündels) im DE1050 eine Zeitraum-ID angegeben wird, wie in einer SG8 SEQ+Z78 RFF+Z31 (Referenz auf die Lokationsbünde...
6004    // REVIEW: Collects Zeitraum-IDs from SEQ+Z58 DE1050 (elements[1][0]). Verifies an SG8 with SEQ+Z78 exists (entry for Lokationsbündelstruktur). Collects IDs from RFF+Z31 DE1154 (elements[0][1]) and checks if any SEQ+Z58 Zeitraum-ID matches. Co-location of RFF+Z31 with SEQ+Z78 in the same SG8 instance is approximated message-wide; exact co-location verification would require navigator SG8-level segment access. (medium confidence)
6005    fn evaluate_335(&self, ctx: &EvaluationContext) -> ConditionResult {
6006        // HAND-EDITED: the Zeitraum-ID of *this* SG8's SEQ+Z58 (DE1050), looked
6007        // for in the RFF+Z31 of any SG8 SEQ+Z78 of the message.
6008        let z58_ids: Vec<String> = ctx
6009            .scoped_find_segments_in_with_qualifier("SG8", "SEQ", 0, "Z58")
6010            .iter()
6011            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
6012            .filter(|v| !v.is_empty())
6013            .collect();
6014        if z58_ids.is_empty() {
6015            return ConditionResult::Unknown;
6016        }
6017        if ctx.find_segments_with_qualifier("SEQ", 0, "Z78").is_empty() {
6018            return ConditionResult::False;
6019        }
6020        let rff_z31_ids: Vec<String> = ctx
6021            .find_segments_with_qualifier("RFF", 0, "Z31")
6022            .iter()
6023            .filter_map(|rff| rff.elements.first().and_then(|e| e.get(1)).cloned())
6024            .filter(|v| !v.is_empty())
6025            .collect();
6026        ConditionResult::from(z58_ids.iter().any(|id| rff_z31_ids.contains(id)))
6027    }
6028
6029    /// [336] Wenn in Änderungsmeldung gefüllt
6030    fn evaluate_336(&self, _ctx: &EvaluationContext) -> ConditionResult {
6031        // TODO: implement
6032        ConditionResult::Unknown
6033    }
6034
6035    /// [337] Wenn SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96 vorhanden
6036    fn evaluate_337(&self, ctx: &EvaluationContext) -> ConditionResult {
6037        let cci_segs = ctx.find_segments("CCI");
6038        let has_cci = cci_segs.iter().any(|s| {
6039            s.elements
6040                .get(2)
6041                .and_then(|e| e.first())
6042                .is_some_and(|v| v == "ZB4")
6043        });
6044        let cav_segs = ctx.find_segments("CAV");
6045        let has_cav = cav_segs.iter().any(|s| {
6046            s.elements
6047                .first()
6048                .and_then(|e| e.first())
6049                .is_some_and(|v| ["Z95", "Z96"].contains(&v.as_str()))
6050        });
6051        ConditionResult::from(has_cci && has_cav)
6052    }
6053
6054    /// [338] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96/ Z97/ Z99/ ZA1/ ZA3/ ZA4/ZG7 vorhanden
6055    fn evaluate_338(&self, ctx: &EvaluationContext) -> ConditionResult {
6056        let cci_segs = ctx.find_segments("CCI");
6057        let has_cci = cci_segs.iter().any(|s| {
6058            s.elements
6059                .get(2)
6060                .and_then(|e| e.first())
6061                .is_some_and(|v| v == "ZB4")
6062        });
6063        let cav_segs = ctx.find_segments("CAV");
6064        let has_cav = cav_segs.iter().any(|s| {
6065            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6066                ["Z95", "Z96", "Z97", "Z99", "ZA1", "ZA3", "ZA4", "ZG7"].contains(&v.as_str())
6067            })
6068        });
6069        ConditionResult::from(has_cci && has_cav)
6070    }
6071
6072    /// [339] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z97/ Z98/ Z99/ ZA0/ ZA1/ ZA2/ ZA3/ ZA6/ ZG7 vorhanden
6073    fn evaluate_339(&self, ctx: &EvaluationContext) -> ConditionResult {
6074        let cci_segs = ctx.find_segments("CCI");
6075        let has_cci = cci_segs.iter().any(|s| {
6076            s.elements
6077                .get(2)
6078                .and_then(|e| e.first())
6079                .is_some_and(|v| v == "ZB4")
6080        });
6081        let cav_segs = ctx.find_segments("CAV");
6082        let has_cav = cav_segs.iter().any(|s| {
6083            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6084                [
6085                    "Z97", "Z98", "Z99", "ZA0", "ZA1", "ZA2", "ZA3", "ZA6", "ZG7",
6086                ]
6087                .contains(&v.as_str())
6088            })
6089        });
6090        ConditionResult::from(has_cci && has_cav)
6091    }
6092
6093    /// [340] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z97/ Z98/ Z99/ ZA1/ ZA2/ ZA3/ ZA4/ ZA5/ ZA6 vorhanden
6094    fn evaluate_340(&self, ctx: &EvaluationContext) -> ConditionResult {
6095        let cci_segs = ctx.find_segments("CCI");
6096        let has_cci = cci_segs.iter().any(|s| {
6097            s.elements
6098                .get(2)
6099                .and_then(|e| e.first())
6100                .is_some_and(|v| v == "ZB4")
6101        });
6102        let cav_segs = ctx.find_segments("CAV");
6103        let has_cav = cav_segs.iter().any(|s| {
6104            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6105                [
6106                    "Z95", "Z97", "Z98", "Z99", "ZA1", "ZA2", "ZA3", "ZA4", "ZA5", "ZA6",
6107                ]
6108                .contains(&v.as_str())
6109            })
6110        });
6111        ConditionResult::from(has_cci && has_cav)
6112    }
6113
6114    /// [341] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z96/ ZA0/ ZG7 vorhanden
6115    fn evaluate_341(&self, ctx: &EvaluationContext) -> ConditionResult {
6116        let cci_segs = ctx.find_segments("CCI");
6117        let has_cci = cci_segs.iter().any(|s| {
6118            s.elements
6119                .get(2)
6120                .and_then(|e| e.first())
6121                .is_some_and(|v| v == "ZB4")
6122        });
6123        let cav_segs = ctx.find_segments("CAV");
6124        let has_cav = cav_segs.iter().any(|s| {
6125            s.elements
6126                .first()
6127                .and_then(|e| e.first())
6128                .is_some_and(|v| ["Z96", "ZA0", "ZG7"].contains(&v.as_str()))
6129        });
6130        ConditionResult::from(has_cci && has_cav)
6131    }
6132
6133    /// [342] Wenn aktiver MaBiS-ZP für den Betrachtungszeitraum vorhanden
6134    fn evaluate_342(&self, _ctx: &EvaluationContext) -> ConditionResult {
6135        // TODO: implement
6136        ConditionResult::Unknown
6137    }
6138
6139    /// [344] Wenn in dieser SG8 das SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+ZF1 (Netzgangzeitreihe (NGZ)) vorhanden
6140    fn evaluate_344(&self, ctx: &EvaluationContext) -> ConditionResult {
6141        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6142        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6143        let has_cci = cci_segs.iter().any(|s| {
6144            s.elements
6145                .get(2)
6146                .and_then(|e| e.first())
6147                .is_some_and(|v| v == "ZB4")
6148        });
6149        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
6150        let has_cav = cav_segs.iter().any(|s| {
6151            s.elements
6152                .first()
6153                .and_then(|e| e.first())
6154                .is_some_and(|v| ["ZF1"].contains(&v.as_str()))
6155        });
6156        ConditionResult::from(has_cci && has_cav)
6157    }
6158
6159    /// [345] Wenn SG5 LOC+Z17 (Messlokation) vorhanden
6160    fn evaluate_345(&self, ctx: &EvaluationContext) -> ConditionResult {
6161        let loc_segs = ctx.find_segments("LOC");
6162        let found = loc_segs.iter().any(|s| {
6163            s.elements
6164                .first()
6165                .and_then(|e| e.first())
6166                .is_some_and(|v| v == "Z17")
6167        });
6168        ConditionResult::from(found)
6169    }
6170
6171    /// [346] Wenn SG10 CCI+++E13 (Zählertyp) CAV+MME (mME) vorhanden
6172    fn evaluate_346(&self, ctx: &EvaluationContext) -> ConditionResult {
6173        let cci_segs = ctx.find_segments("CCI");
6174        let found = cci_segs.iter().any(|s| {
6175            s.elements
6176                .get(2)
6177                .and_then(|e| e.first())
6178                .is_some_and(|v| v == "E13")
6179        });
6180        ConditionResult::from(found)
6181    }
6182
6183    /// [347] Wenn SG5 LOC+Z16 (Marktlokation) nicht vorhanden
6184    fn evaluate_347(&self, ctx: &EvaluationContext) -> ConditionResult {
6185        let loc_segs = ctx.find_segments("LOC");
6186        let found = loc_segs.iter().any(|s| {
6187            s.elements
6188                .first()
6189                .and_then(|e| e.first())
6190                .is_some_and(|v| v == "Z16")
6191        });
6192        ConditionResult::from(!found)
6193    }
6194
6195    /// [348] Wenn SG5 LOC+Z21 (Tranche) nicht vorhanden
6196    fn evaluate_348(&self, ctx: &EvaluationContext) -> ConditionResult {
6197        let loc_segs = ctx.find_segments("LOC");
6198        let found = loc_segs.iter().any(|s| {
6199            s.elements
6200                .first()
6201                .and_then(|e| e.first())
6202                .is_some_and(|v| v == "Z21")
6203        });
6204        ConditionResult::from(!found)
6205    }
6206
6207    /// [349] Wenn SG5 LOC+Z17 (Messlokation) nicht vorhanden
6208    fn evaluate_349(&self, ctx: &EvaluationContext) -> ConditionResult {
6209        let loc_segs = ctx.find_segments("LOC");
6210        let found = loc_segs.iter().any(|s| {
6211            s.elements
6212                .first()
6213                .and_then(|e| e.first())
6214                .is_some_and(|v| v == "Z17")
6215        });
6216        ConditionResult::from(!found)
6217    }
6218
6219    /// [350] Wenn SG8 SEQ+Z78/ZD5 RFF+Z31 (Referenz auf die Lokationsbündelstruktur) vorhanden
6220    fn evaluate_350(&self, ctx: &EvaluationContext) -> ConditionResult {
6221        let seq_segs = ctx.find_segments("SEQ");
6222        let has_seq = seq_segs.iter().any(|s| {
6223            s.elements
6224                .first()
6225                .and_then(|e| e.first())
6226                .is_some_and(|v| ["Z78", "ZD5"].contains(&v.as_str()))
6227        });
6228        let rff_segs = ctx.find_segments("RFF");
6229        let has_rff = rff_segs.iter().any(|s| {
6230            s.elements
6231                .first()
6232                .and_then(|e| e.first())
6233                .is_some_and(|v| v == "Z31")
6234        });
6235        ConditionResult::from(has_seq && has_rff)
6236    }
6237
6238    /// [351] Wenn SG4 STS+E01++A04/ A05 /A13 /A14 (Status der Antwort) vorhanden
6239    fn evaluate_351(&self, ctx: &EvaluationContext) -> ConditionResult {
6240        let sts_segs = ctx.find_segments("STS");
6241        let found = sts_segs.iter().any(|s| {
6242            s.elements
6243                .first()
6244                .and_then(|e| e.first())
6245                .is_some_and(|v| v == "E01")
6246                && s.elements
6247                    .get(2)
6248                    .and_then(|e| e.first())
6249                    .is_some_and(|v| ["A04", "A05", "A13", "A14"].contains(&v.as_str()))
6250        });
6251        ConditionResult::from(found)
6252    }
6253
6254    /// [352] Wenn SG4 STS++A05/ A06/ A14 /A15 (Status der Antwort) vorhanden
6255    fn evaluate_352(&self, ctx: &EvaluationContext) -> ConditionResult {
6256        let sts_segs = ctx.find_segments("STS");
6257        let found = sts_segs.iter().any(|s| {
6258            s.elements
6259                .get(1)
6260                .and_then(|e| e.first())
6261                .is_some_and(|v| ["A05", "A06", "A14", "A15"].contains(&v.as_str()))
6262        });
6263        ConditionResult::from(found)
6264    }
6265
6266    /// [355] Wenn SG4 STS+E01++A45 (Status der Antwort) vorhanden
6267    fn evaluate_355(&self, ctx: &EvaluationContext) -> ConditionResult {
6268        let sts_segs = ctx.find_segments("STS");
6269        let found = sts_segs.iter().any(|s| {
6270            s.elements
6271                .first()
6272                .and_then(|e| e.first())
6273                .is_some_and(|v| v == "E01")
6274                && s.elements
6275                    .get(2)
6276                    .and_then(|e| e.first())
6277                    .is_some_and(|v| ["A45"].contains(&v.as_str()))
6278        });
6279        ConditionResult::from(found)
6280    }
6281
6282    /// [356] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6283    fn evaluate_356(&self, ctx: &EvaluationContext) -> ConditionResult {
6284        let sts_segs = ctx.find_segments("STS");
6285        let found = sts_segs.iter().any(|s| {
6286            s.elements
6287                .first()
6288                .and_then(|e| e.first())
6289                .is_some_and(|v| v == "E01")
6290                && s.elements
6291                    .get(2)
6292                    .and_then(|e| e.first())
6293                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6294        });
6295        ConditionResult::from(found)
6296    }
6297
6298    /// [357] Wenn SG4 STS+E01++A03/ A09/ A12/ A17 (Status der Antwort) vorhanden
6299    fn evaluate_357(&self, ctx: &EvaluationContext) -> ConditionResult {
6300        let sts_segs = ctx.find_segments("STS");
6301        let found = sts_segs.iter().any(|s| {
6302            s.elements
6303                .first()
6304                .and_then(|e| e.first())
6305                .is_some_and(|v| v == "E01")
6306                && s.elements
6307                    .get(2)
6308                    .and_then(|e| e.first())
6309                    .is_some_and(|v| ["A03", "A09", "A12", "A17"].contains(&v.as_str()))
6310        });
6311        ConditionResult::from(found)
6312    }
6313
6314    /// [358] Wenn SG4 STS+E01++A06 (Status der Antwort) vorhanden
6315    fn evaluate_358(&self, ctx: &EvaluationContext) -> ConditionResult {
6316        let sts_segs = ctx.find_segments("STS");
6317        let found = sts_segs.iter().any(|s| {
6318            s.elements
6319                .first()
6320                .and_then(|e| e.first())
6321                .is_some_and(|v| v == "E01")
6322                && s.elements
6323                    .get(2)
6324                    .and_then(|e| e.first())
6325                    .is_some_and(|v| ["A06"].contains(&v.as_str()))
6326        });
6327        ConditionResult::from(found)
6328    }
6329
6330    /// [359] Es sind nur Antwortcodes aus dem Cluster Ablehnung erlaubt
6331    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6332    // Preserve across regeneration until the codegen prompt is updated.
6333    fn evaluate_359(&self, ctx: &EvaluationContext) -> ConditionResult {
6334        all_sts_e01_in_cluster(ctx, Cluster::is_ablehnung)
6335    }
6336
6337    /// [360] Es sind nur Antwortcodes aus dem Cluster Zustimmung erlaubt
6338    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6339    // Preserve across regeneration until the codegen prompt is updated.
6340    fn evaluate_360(&self, ctx: &EvaluationContext) -> ConditionResult {
6341        all_sts_e01_in_cluster(ctx, Cluster::is_zustimmung)
6342    }
6343
6344    /// [363] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6345    fn evaluate_363(&self, ctx: &EvaluationContext) -> ConditionResult {
6346        let sts_segs = ctx.find_segments("STS");
6347        let found = sts_segs.iter().any(|s| {
6348            s.elements
6349                .first()
6350                .and_then(|e| e.first())
6351                .is_some_and(|v| v == "E01")
6352                && s.elements
6353                    .get(2)
6354                    .and_then(|e| e.first())
6355                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6356        });
6357        ConditionResult::from(found)
6358    }
6359
6360    /// [365] Wenn in dieser SG4 IDE+24 das STS+E01++A04:E_0047/E_0014 bzw. A03:E_0049/E_0004 (Status der Antwort) nicht vorhanden
6361    fn evaluate_365(&self, ctx: &EvaluationContext) -> ConditionResult {
6362        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6363        // Check: STS+E01 does NOT have A04 with E_0047/E_0014 or A03 with E_0049/E_0004
6364        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6365        let has_excluded = sts_segs.iter().any(|s| {
6366            let code = s
6367                .elements
6368                .get(2)
6369                .and_then(|e| e.first())
6370                .map(|v| v.as_str());
6371            let ebd = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
6372            matches!(
6373                (code, ebd),
6374                (Some("A04"), Some("E_0047"))
6375                    | (Some("A04"), Some("E_0014"))
6376                    | (Some("A03"), Some("E_0049"))
6377                    | (Some("A03"), Some("E_0004"))
6378            )
6379        });
6380        ConditionResult::from(!has_excluded)
6381    }
6382
6383    /// [366] Bis auf den Code A30 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6384    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6385    // Preserve across regeneration until the codegen prompt is updated.
6386    fn evaluate_366(&self, ctx: &EvaluationContext) -> ConditionResult {
6387        ebd_ablehnung_except(ctx, "E_0624", &["A30"])
6388    }
6389
6390    /// [367] Wenn SG4 STS+E01++A05/ A14 (Status der Antwort) vorhanden
6391    fn evaluate_367(&self, ctx: &EvaluationContext) -> ConditionResult {
6392        let sts_segs = ctx.find_segments("STS");
6393        let found = sts_segs.iter().any(|s| {
6394            s.elements
6395                .first()
6396                .and_then(|e| e.first())
6397                .is_some_and(|v| v == "E01")
6398                && s.elements
6399                    .get(2)
6400                    .and_then(|e| e.first())
6401                    .is_some_and(|v| ["A05", "A14"].contains(&v.as_str()))
6402        });
6403        ConditionResult::from(found)
6404    }
6405
6406    /// [368] Bis auf den Code A41 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6407    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6408    // Preserve across regeneration until the codegen prompt is updated.
6409    fn evaluate_368(&self, ctx: &EvaluationContext) -> ConditionResult {
6410        ebd_ablehnung_except(ctx, "E_0624", &["A41"])
6411    }
6412
6413    /// [370] Wenn SG10 CAV+TLS/TES/BIT/GET/GAT/SOT/WNT/WFT/WAT
6414    fn evaluate_370(&self, ctx: &EvaluationContext) -> ConditionResult {
6415        let cav_segs = ctx.find_segments("CAV");
6416        let found = cav_segs.iter().any(|s| {
6417            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6418                [
6419                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
6420                ]
6421                .contains(&v.as_str())
6422            })
6423        });
6424        ConditionResult::from(found)
6425    }
6426
6427    /// [371] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
6428    fn evaluate_371(&self, ctx: &EvaluationContext) -> ConditionResult {
6429        let seq_segs = ctx.find_segments("SEQ");
6430        let found = seq_segs.iter().any(|s| {
6431            s.elements
6432                .first()
6433                .and_then(|e| e.first())
6434                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
6435        });
6436        ConditionResult::from(!found)
6437    }
6438
6439    /// [372] Wenn SG10 CAV+TES/BIT/GET/GAT/SOT/WNT/WFT/WAT vorhanden
6440    fn evaluate_372(&self, ctx: &EvaluationContext) -> ConditionResult {
6441        let cav_segs = ctx.find_segments("CAV");
6442        let found = cav_segs.iter().any(|s| {
6443            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6444                ["TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT"].contains(&v.as_str())
6445            })
6446        });
6447        ConditionResult::from(found)
6448    }
6449
6450    /// [373] Wenn ein Referenzprofil an der Marktlokation vorhanden ist
6451    fn evaluate_373(&self, _ctx: &EvaluationContext) -> ConditionResult {
6452        // TODO: implement
6453        ConditionResult::Unknown
6454    }
6455
6456    /// [375] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z24 (Daten der Überführungszeitreihe) nicht vorhanden
6457    fn evaluate_375(&self, ctx: &EvaluationContext) -> ConditionResult {
6458        // Check: no SG8 SEQ+Z24 (Daten der Überführungszeitreihe) exists
6459        ctx.lacks_qualifier("SEQ", 0, "Z24")
6460    }
6461
6462    /// [376] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z22 (Daten der Summenzeitreihe) nicht vorhanden
6463    fn evaluate_376(&self, ctx: &EvaluationContext) -> ConditionResult {
6464        // Check: no SG8 SEQ+Z22 (Daten der Summenzeitreihe) exists
6465        ctx.lacks_qualifier("SEQ", 0, "Z22")
6466    }
6467
6468    /// [377] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS vorhanden
6469    // REVIEW: Checks if any SG8 has a child SG10 with CCI[0]='15' and CCI[2]='Z21' (Überführungszeitreihentyp) followed by CAV with value 'AUS'. Follows the navigator pattern from example 10 exactly. (medium confidence)
6470    fn evaluate_377(&self, ctx: &EvaluationContext) -> ConditionResult {
6471        let nav = match ctx.navigator {
6472            Some(n) => n,
6473            None => return ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]),
6474        };
6475        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6476        for i in 0..sg8_count {
6477            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6478            for j in 0..sg10_count {
6479                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6480                let has_cci = ccis.iter().any(|s| {
6481                    s.elements
6482                        .first()
6483                        .and_then(|e: &Vec<String>| e.first())
6484                        .is_some_and(|v: &String| v == "15")
6485                        && s.elements
6486                            .get(2)
6487                            .and_then(|e: &Vec<String>| e.first())
6488                            .is_some_and(|v: &String| v == "Z21")
6489                });
6490                if has_cci {
6491                    let cavs =
6492                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6493                    if cavs.iter().any(|s| {
6494                        s.elements
6495                            .first()
6496                            .and_then(|e: &Vec<String>| e.first())
6497                            .is_some_and(|v: &String| v == "AUS")
6498                    }) {
6499                        return ConditionResult::True;
6500                    }
6501                }
6502            }
6503        }
6504        ConditionResult::False
6505    }
6506
6507    /// [378] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS nicht vorhanden
6508    // REVIEW: Logical negation of condition 377: True when CCI+15++Z21 CAV+AUS is NOT present in any SG8's SG10 child. Iterates all instances and returns True only if the combination is never found. (medium confidence)
6509    fn evaluate_378(&self, ctx: &EvaluationContext) -> ConditionResult {
6510        let nav = match ctx.navigator {
6511            Some(n) => n,
6512            None => {
6513                let r = ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]);
6514                return match r {
6515                    ConditionResult::True => ConditionResult::False,
6516                    ConditionResult::False => ConditionResult::True,
6517                    ConditionResult::Unknown => ConditionResult::Unknown,
6518                };
6519            }
6520        };
6521        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6522        for i in 0..sg8_count {
6523            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6524            for j in 0..sg10_count {
6525                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6526                let has_cci = ccis.iter().any(|s| {
6527                    s.elements
6528                        .first()
6529                        .and_then(|e: &Vec<String>| e.first())
6530                        .is_some_and(|v: &String| v == "15")
6531                        && s.elements
6532                            .get(2)
6533                            .and_then(|e: &Vec<String>| e.first())
6534                            .is_some_and(|v: &String| v == "Z21")
6535                });
6536                if has_cci {
6537                    let cavs =
6538                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6539                    if cavs.iter().any(|s| {
6540                        s.elements
6541                            .first()
6542                            .and_then(|e: &Vec<String>| e.first())
6543                            .is_some_and(|v: &String| v == "AUS")
6544                    }) {
6545                        return ConditionResult::False;
6546                    }
6547                }
6548            }
6549        }
6550        ConditionResult::True
6551    }
6552
6553    /// [379] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+ Code für EEG-Überführungszeitreihen (ausgenommen AU1) vorhanden
6554    fn evaluate_379(&self, ctx: &EvaluationContext) -> ConditionResult {
6555        let cci_segs = ctx.find_segments("CCI");
6556        let found = cci_segs.iter().any(|s| {
6557            s.elements
6558                .first()
6559                .and_then(|e| e.first())
6560                .is_some_and(|v| v == "15")
6561                && s.elements
6562                    .get(2)
6563                    .and_then(|e| e.first())
6564                    .is_some_and(|v| ["Z21"].contains(&v.as_str()))
6565        });
6566        ConditionResult::from(found)
6567    }
6568
6569    /// [380] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS/AU1 vorhanden
6570    // REVIEW: Variant of condition 377 that accepts either 'AUS' or 'AU1' as the CAV value for the Überführungszeitreihentyp. Same SG8→SG10 parent-child navigation, broadened CAV value check. (medium confidence)
6571    fn evaluate_380(&self, ctx: &EvaluationContext) -> ConditionResult {
6572        let nav = match ctx.navigator {
6573            Some(n) => n,
6574            None => {
6575                return ctx.any_group_has_any_qualifier("CAV", 0, &["AUS", "AU1"], &["SG4", "SG8"])
6576            }
6577        };
6578        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6579        for i in 0..sg8_count {
6580            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6581            for j in 0..sg10_count {
6582                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6583                let has_cci = ccis.iter().any(|s| {
6584                    s.elements
6585                        .first()
6586                        .and_then(|e: &Vec<String>| e.first())
6587                        .is_some_and(|v: &String| v == "15")
6588                        && s.elements
6589                            .get(2)
6590                            .and_then(|e: &Vec<String>| e.first())
6591                            .is_some_and(|v: &String| v == "Z21")
6592                });
6593                if has_cci {
6594                    let cavs =
6595                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6596                    if cavs.iter().any(|s| {
6597                        s.elements
6598                            .first()
6599                            .and_then(|e: &Vec<String>| e.first())
6600                            .is_some_and(|v: &String| v == "AUS" || v == "AU1")
6601                    }) {
6602                        return ConditionResult::True;
6603                    }
6604                }
6605            }
6606        }
6607        ConditionResult::False
6608    }
6609
6610    /// [384] Wenn in derselben SG8 SG10 CCI+Z37++ZD1 (Basis zur Bildung der Tranchengröße) (Prozentual) vorhanden
6611    fn evaluate_384(&self, ctx: &EvaluationContext) -> ConditionResult {
6612        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6613        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6614        let found = cci_segs.iter().any(|s| {
6615            s.elements
6616                .first()
6617                .and_then(|e| e.first())
6618                .is_some_and(|v| v == "Z37")
6619                && s.elements
6620                    .get(2)
6621                    .and_then(|e| e.first())
6622                    .is_some_and(|v| v == "ZD1")
6623        });
6624        ConditionResult::from(found)
6625    }
6626
6627    /// [386] Wenn mehr als eine SG8 SEQ+Z02/ ZE3 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden
6628    fn evaluate_386(&self, ctx: &EvaluationContext) -> ConditionResult {
6629        // Check: more than one SG8 SEQ+Z02/ZE3 with OBIS energy quantity in PIA+5
6630        let seq_count = ctx
6631            .find_segments("SEQ")
6632            .iter()
6633            .filter(|s| {
6634                s.elements
6635                    .first()
6636                    .and_then(|e| e.first())
6637                    .is_some_and(|v| v == "Z02" || v == "ZE3")
6638            })
6639            .count();
6640        ConditionResult::from(seq_count > 1)
6641    }
6642
6643    /// [387] Wenn in derselben SG8 (OBIS Daten der Marktlokation) eine OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden und es sich lt. Codeliste OBIS um eine OBIS mit zugeordneter Zählzeit handelt (Tarif: e...
6644    fn evaluate_387(&self, ctx: &EvaluationContext) -> ConditionResult {
6645        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6646        // Check: same SG8 (OBIS Daten) has OBIS energy quantity code in PIA+5
6647        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6648        let found = pia_segs.iter().any(|s| {
6649            s.elements
6650                .first()
6651                .and_then(|e| e.first())
6652                .is_some_and(|v| v == "5")
6653                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6654                    v.starts_with("1-")
6655                        && (v.contains(":1.8.")
6656                            || v.contains(":2.8.")
6657                            || v.contains(":1.9.")
6658                            || v.contains(":2.9."))
6659                })
6660        });
6661        ConditionResult::from(found)
6662    }
6663
6664    /// [388] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: \"Gültige Daten\") anzugeben, wenn in diesem Zeitraum Blindarbeit bzw. -leis...
6665    fn evaluate_388(&self, ctx: &EvaluationContext) -> ConditionResult {
6666        // Check: at least one RFF+Z49 (Zeitraum) exists
6667        let found = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6668        ConditionResult::from(found)
6669    }
6670
6671    /// [391] Wenn in derselben SG8 SEQ+Z20 (OBIS Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl für Zählerstand im PIA+5 vorhanden und es sich lt. Codeliste OBIS um eine OBIS mit zugeordn...
6672    fn evaluate_391(&self, ctx: &EvaluationContext) -> ConditionResult {
6673        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6674        // Check: OBIS Zählerstand (meter reading) code in PIA+5 within SEQ+Z20
6675        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6676        let found = pia_segs.iter().any(|s| {
6677            s.elements
6678                .first()
6679                .and_then(|e| e.first())
6680                .is_some_and(|v| v == "5")
6681                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6682                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
6683                })
6684        });
6685        ConditionResult::from(found)
6686    }
6687
6688    /// [392] Wenn in diesem PIA der Code im Format n1-n2-n1 angegeben ist
6689    fn evaluate_392(&self, ctx: &EvaluationContext) -> ConditionResult {
6690        // Check: PIA code in format n1-n2-n1 (e.g., "1-08-1")
6691        let pia_segs = ctx.find_segments("PIA");
6692        let found = pia_segs.iter().any(|s| {
6693            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6694                let parts: Vec<&str> = v.split('-').collect();
6695                parts.len() == 3
6696                    && parts[0].len() == 1
6697                    && parts[0].chars().all(|c| c.is_ascii_digit())
6698                    && parts[1].len() == 2
6699                    && parts[1].chars().all(|c| c.is_ascii_digit())
6700                    && parts[2].len() == 1
6701                    && parts[2].chars().all(|c| c.is_ascii_digit())
6702            })
6703        });
6704        ConditionResult::from(found)
6705    }
6706
6707    /// [393] Wenn in diesem PIA der Code im Format n1-n2-n1-n3 angegeben ist
6708    fn evaluate_393(&self, ctx: &EvaluationContext) -> ConditionResult {
6709        // Check: PIA code in format n1-n2-n1-n3 (e.g., "1-08-1-001")
6710        let pia_segs = ctx.find_segments("PIA");
6711        let found = pia_segs.iter().any(|s| {
6712            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6713                let parts: Vec<&str> = v.split('-').collect();
6714                parts.len() == 4
6715                    && parts[0].len() == 1
6716                    && parts[0].chars().all(|c| c.is_ascii_digit())
6717                    && parts[1].len() == 2
6718                    && parts[1].chars().all(|c| c.is_ascii_digit())
6719                    && parts[2].len() == 1
6720                    && parts[2].chars().all(|c| c.is_ascii_digit())
6721                    && parts[3].len() == 3
6722                    && parts[3].chars().all(|c| c.is_ascii_digit())
6723            })
6724        });
6725        ConditionResult::from(found)
6726    }
6727
6728    /// [394] Wenn in diesem PIA der Code im Format n1-n2-n1-n8-n2 angegeben ist
6729    fn evaluate_394(&self, ctx: &EvaluationContext) -> ConditionResult {
6730        // Check: PIA code in format n1-n2-n1-n8-n2
6731        let pia_segs = ctx.find_segments("PIA");
6732        let found = pia_segs.iter().any(|s| {
6733            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6734                let parts: Vec<&str> = v.split('-').collect();
6735                parts.len() == 5
6736                    && parts[0].len() == 1
6737                    && parts[0].chars().all(|c| c.is_ascii_digit())
6738                    && parts[1].len() == 2
6739                    && parts[1].chars().all(|c| c.is_ascii_digit())
6740                    && parts[2].len() == 1
6741                    && parts[2].chars().all(|c| c.is_ascii_digit())
6742                    && parts[3].len() == 8
6743                    && parts[3].chars().all(|c| c.is_ascii_digit())
6744                    && parts[4].len() == 2
6745                    && parts[4].chars().all(|c| c.is_ascii_digit())
6746            })
6747        });
6748        ConditionResult::from(found)
6749    }
6750
6751    /// [395] Wenn in diesem PIA der Code im Format n1-n2-n1-n8 angegeben ist
6752    fn evaluate_395(&self, ctx: &EvaluationContext) -> ConditionResult {
6753        // Check: PIA code in format n1-n2-n1-n8
6754        let pia_segs = ctx.find_segments("PIA");
6755        let found = pia_segs.iter().any(|s| {
6756            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6757                let parts: Vec<&str> = v.split('-').collect();
6758                parts.len() == 4
6759                    && parts[0].len() == 1
6760                    && parts[0].chars().all(|c| c.is_ascii_digit())
6761                    && parts[1].len() == 2
6762                    && parts[1].chars().all(|c| c.is_ascii_digit())
6763                    && parts[2].len() == 1
6764                    && parts[2].chars().all(|c| c.is_ascii_digit())
6765                    && parts[3].len() == 8
6766                    && parts[3].chars().all(|c| c.is_ascii_digit())
6767            })
6768        });
6769        ConditionResult::from(found)
6770    }
6771
6772    /// [396] Wenn in derselben SG8 SEQ+Z02/ ZA1/ ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Wirkarbeit und 1/4 Stunde im PIA+5 vorhanden
6773    fn evaluate_396(&self, ctx: &EvaluationContext) -> ConditionResult {
6774        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6775        // Check: OBIS Wirkarbeit und 1/4 Stunde code in PIA+5
6776        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6777        let found = pia_segs.iter().any(|s| {
6778            s.elements
6779                .first()
6780                .and_then(|e| e.first())
6781                .is_some_and(|v| v == "5")
6782                && s.elements
6783                    .get(1)
6784                    .and_then(|e| e.first())
6785                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
6786        });
6787        ConditionResult::from(found)
6788    }
6789
6790    /// [397] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/1-08-4 beginnt
6791    fn evaluate_397(&self, ctx: &EvaluationContext) -> ConditionResult {
6792        let pia_segs = ctx.find_segments("PIA");
6793        let found = pia_segs.iter().any(|s| {
6794            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6795                let prefixes = ["1-08-1", "1-08-4"];
6796                prefixes.iter().any(|p| v.starts_with(p))
6797            })
6798        });
6799        ConditionResult::from(found)
6800    }
6801
6802    /// [398] Wenn in diesem PIA der Code im DE7140 mit 1-08-2/1-08-5 beginnt
6803    fn evaluate_398(&self, ctx: &EvaluationContext) -> ConditionResult {
6804        let pia_segs = ctx.find_segments("PIA");
6805        let found = pia_segs.iter().any(|s| {
6806            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6807                let prefixes = ["1-08-2", "1-08-5"];
6808                prefixes.iter().any(|p| v.starts_with(p))
6809            })
6810        });
6811        ConditionResult::from(found)
6812    }
6813
6814    /// [399] Wenn im SG8 SEQ+Z78 mit identischer Zeitraum-ID im DE1050 wie im DE3224 dieses Segments, das RFF+Z31 (Lokationsbündelstruktur) im DE1153 der Code Z31 (Lokationsbündelstruktur) vorhanden, und im D...
6815    // REVIEW: Condition is True when an SG8 SEQ+Z78 exists whose Zeitraum-ID (DE1050) matches DE3224 of the current LOC segment (accessed via SG5 group navigator as LOC.C517.DE3224 = elements[1][3]), AND that Z78 SG8 has RFF+Z31 with a DE1154 value that is NOT the pauschal code 9992000000018. This identifies bundle structures that reference a non-pauschal Lokationsbündelstruktur. (medium confidence)
6816    fn evaluate_399(&self, ctx: &EvaluationContext) -> ConditionResult {
6817        let nav = match ctx.navigator() {
6818            Some(n) => n,
6819            None => return ConditionResult::Unknown,
6820        };
6821        // Collect DE3224 values from LOC segments in SG5 — LOC.C517.DE3224 = elements[1][3]
6822        // These serve as the Zeitraum-IDs referenced by the condition's "dieses Segments"
6823        let sg5_count = nav.group_instance_count(&["SG4", "SG5"]);
6824        let mut loc_zeitraum_ids: Vec<String> = Vec::new();
6825        for i in 0..sg5_count {
6826            let locs = nav.find_segments_in_group("LOC", &["SG4", "SG5"], i);
6827            for loc in &locs {
6828                if let Some(zid) = loc.elements.get(1).and_then(|e| e.get(3)) {
6829                    if !zid.is_empty() {
6830                        loc_zeitraum_ids.push(zid.clone());
6831                    }
6832                }
6833            }
6834        }
6835        if loc_zeitraum_ids.is_empty() {
6836            return ConditionResult::Unknown;
6837        }
6838        let pauschal_id = "9992000000018";
6839        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6840        // Find any Z78 SG8 whose Zeitraum-ID (SEQ.C286.DE1050) matches a LOC DE3224
6841        // and that has RFF+Z31 with DE1154 != pauschal_id
6842        for i in 0..sg8_count {
6843            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
6844            for seq in &seqs {
6845                if !seq
6846                    .elements
6847                    .first()
6848                    .and_then(|e| e.first())
6849                    .is_some_and(|v| v == "Z78")
6850                {
6851                    continue;
6852                }
6853                let zid = seq
6854                    .elements
6855                    .get(1)
6856                    .and_then(|e| e.first())
6857                    .map(|s| s.as_str())
6858                    .unwrap_or("");
6859                if !loc_zeitraum_ids.iter().any(|z| z == zid) {
6860                    continue;
6861                }
6862                // Matching Z78 SG8 found — check RFF+Z31 DE1154
6863                let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
6864                for rff in &rffs {
6865                    let qual = rff
6866                        .elements
6867                        .first()
6868                        .and_then(|e| e.first())
6869                        .map(|v| v.as_str())
6870                        .unwrap_or("");
6871                    if qual == "Z31" {
6872                        let id = rff
6873                            .elements
6874                            .first()
6875                            .and_then(|e| e.get(1))
6876                            .map(|v| v.as_str())
6877                            .unwrap_or("");
6878                        let normalized = id.replace(' ', "");
6879                        if !normalized.is_empty() && normalized != pauschal_id {
6880                            return ConditionResult::True;
6881                        }
6882                    }
6883                }
6884            }
6885        }
6886        ConditionResult::False
6887    }
6888
6889    /// [401] Wenn dieses DTM+Z25 (Verwendung der Daten ab) nicht im SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: Gültige Daten/ Keine Daten) mit der Zeitraum ID \"1\" im DE1156 ist, muss das Datum dem DTM+...
6890    fn evaluate_401(&self, ctx: &EvaluationContext) -> ConditionResult {
6891        // Check: this DTM+Z25 is not in SG6 RFF+Z49 or Z53
6892        // Approximate: check if RFF+Z49 or Z53 exists
6893        let has_z49 = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6894        let has_z53 = !ctx.find_segments_with_qualifier("RFF", 0, "Z53").is_empty();
6895        ConditionResult::from(!has_z49 && !has_z53)
6896    }
6897
6898    /// [402] Wenn in derselben SG8  (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl der Werteart \"Zählerstand\" im PIA+5 vorhanden
6899    fn evaluate_402(&self, ctx: &EvaluationContext) -> ConditionResult {
6900        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6901        // Check: OBIS Zählerstand code in PIA+5
6902        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6903        let found = pia_segs.iter().any(|s| {
6904            s.elements
6905                .first()
6906                .and_then(|e| e.first())
6907                .is_some_and(|v| v == "5")
6908                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6909                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
6910                })
6911        });
6912        ConditionResult::from(found)
6913    }
6914
6915    /// [403] Wenn nicht dieselbe MP-ID in SG2 NAD+MS (Nachrichtensender) und im SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+++ZB3 (Zugeordnete Marktpartner) CAV+Z91 (Messtellenbetreiber) vorhanden
6916    // REVIEW: Gets NAD+MS MP-ID from SG2 (elements[1][0]), then navigates all SG8→SG10 instances looking for CCI with elements[2][0]='ZB3' (Zugeordnete Marktpartner) and CAV with elements[0][0]='Z91' (Messstellenbetreiber). Compares the CAV+Z91 MP-ID (elements[0][1]) with the sender MP-ID. Returns True when they differ (not the same). Note: SEQ+Z01 filter on the parent SG8 is omitted because `find_segments_in_child_group` only accesses child groups, not the parent SG8 segments; the Z91 MSB identity is message-consistent regardless. (medium confidence)
6917    fn evaluate_403(&self, ctx: &EvaluationContext) -> ConditionResult {
6918        let sender_mp_id: String = match ctx.find_segments_with_qualifier("NAD", 0, "MS").first() {
6919            Some(nad) => match nad.elements.get(1).and_then(|e: &Vec<String>| e.first()) {
6920                Some(id) if !id.is_empty() => id.clone(),
6921                _ => return ConditionResult::Unknown,
6922            },
6923            None => return ConditionResult::Unknown,
6924        };
6925        let nav = match ctx.navigator {
6926            Some(n) => n,
6927            None => return ConditionResult::Unknown,
6928        };
6929        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6930        for i in 0..sg8_count {
6931            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6932            for j in 0..sg10_count {
6933                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6934                let has_cci_zb3 = ccis.iter().any(|s| {
6935                    s.elements
6936                        .get(2)
6937                        .and_then(|e: &Vec<String>| e.first())
6938                        .is_some_and(|v: &String| v == "ZB3")
6939                });
6940                if !has_cci_zb3 {
6941                    continue;
6942                }
6943                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6944                for cav in &cavs {
6945                    if cav
6946                        .elements
6947                        .first()
6948                        .and_then(|e: &Vec<String>| e.first())
6949                        .is_some_and(|v: &String| v == "Z91")
6950                    {
6951                        if let Some(msb_id) = cav.elements.first().and_then(|e| e.get(1)) {
6952                            if !msb_id.is_empty() {
6953                                return ConditionResult::from(
6954                                    sender_mp_id.as_str() != msb_id.as_str(),
6955                                );
6956                            }
6957                        }
6958                    }
6959                }
6960            }
6961        }
6962        ConditionResult::Unknown
6963    }
6964
6965    /// [404] Wenn nicht dieselbe MP-ID in SG2 NAD+MR (Nachrichtenempfänger) und im SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+++ZB3 (Zugeordnete Marktpartner) CAV+Z91 (Messtellenbetreiber) vorhanden
6966    // REVIEW: Reads NAD+MR MP-ID from elements[1][0], then iterates all SG8 SG10 children looking for CCI with elements[2][0]==ZB3 and CAV with elements[0][0]==Z91 where the MP-ID (elements[0][1]) matches. Returns False if same MP-ID found (condition 'not same' fails), True otherwise. Approximation: cannot filter SG8 instances by SEQ+Z01/Z98 without find_segments_in_group; checks all SG8 SG10 children. This is acceptable since CCI+ZB3 CAV+Z91 patterns in other SG8 types would still represent valid MSB assignments to compare against. (medium confidence)
6967    fn evaluate_404(&self, ctx: &EvaluationContext) -> ConditionResult {
6968        let mr_nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
6969        let mr_mp_id = match mr_nads.first() {
6970            Some(nad) => nad
6971                .elements
6972                .get(1)
6973                .and_then(|e: &Vec<String>| e.first())
6974                .cloned()
6975                .unwrap_or_default(),
6976            None => return ConditionResult::Unknown,
6977        };
6978        if mr_mp_id.is_empty() {
6979            return ConditionResult::Unknown;
6980        }
6981
6982        let nav = match ctx.navigator {
6983            Some(n) => n,
6984            None => {
6985                let cavs = ctx.find_segments("CAV");
6986                let same_id_exists = cavs.iter().any(|s| {
6987                    s.elements
6988                        .first()
6989                        .and_then(|e: &Vec<String>| e.first())
6990                        .is_some_and(|v: &String| v == "Z91")
6991                        && s.elements
6992                            .first()
6993                            .and_then(|e| e.get(1))
6994                            .is_some_and(|v: &String| v == &mr_mp_id)
6995                });
6996                return ConditionResult::from(!same_id_exists);
6997            }
6998        };
6999
7000        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7001        for i in 0..sg8_count {
7002            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7003            for j in 0..sg10_count {
7004                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7005                let has_cci_zb3 = ccis.iter().any(|s| {
7006                    s.elements
7007                        .get(2)
7008                        .and_then(|e: &Vec<String>| e.first())
7009                        .is_some_and(|v: &String| v == "ZB3")
7010                });
7011                if !has_cci_zb3 {
7012                    continue;
7013                }
7014                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
7015                for cav in &cavs {
7016                    if cav
7017                        .elements
7018                        .first()
7019                        .and_then(|e: &Vec<String>| e.first())
7020                        .is_some_and(|v: &String| v == "Z91")
7021                    {
7022                        let cav_mp_id = cav
7023                            .elements
7024                            .first()
7025                            .and_then(|e| e.get(1))
7026                            .map(|s| s.as_str())
7027                            .unwrap_or("");
7028                        if cav_mp_id == mr_mp_id.as_str() {
7029                            return ConditionResult::False;
7030                        }
7031                    }
7032                }
7033            }
7034        }
7035
7036        ConditionResult::True
7037    }
7038
7039    /// [405] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Wirkarbeit und kumuliert im PIA+5 vorhanden
7040    fn evaluate_405(&self, ctx: &EvaluationContext) -> ConditionResult {
7041        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7042        // Check: OBIS Wirkarbeit kumuliert code in PIA+5
7043        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7044        let found = pia_segs.iter().any(|s| {
7045            s.elements
7046                .first()
7047                .and_then(|e| e.first())
7048                .is_some_and(|v| v == "5")
7049                && s.elements
7050                    .get(1)
7051                    .and_then(|e| e.first())
7052                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7053        });
7054        ConditionResult::from(found)
7055    }
7056
7057    /// [406] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Wirkarbeit und höchste 1/4 Stunde im Monat im PIA+5 vorhanden
7058    fn evaluate_406(&self, ctx: &EvaluationContext) -> ConditionResult {
7059        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7060        // Check: OBIS Wirkarbeit höchste 1/4 Stunde code in PIA+5
7061        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7062        let found = pia_segs.iter().any(|s| {
7063            s.elements
7064                .first()
7065                .and_then(|e| e.first())
7066                .is_some_and(|v| v == "5")
7067                && s.elements
7068                    .get(1)
7069                    .and_then(|e| e.first())
7070                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7071        });
7072        ConditionResult::from(found)
7073    }
7074
7075    /// [407] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Blindarbeit im PIA+5 vorhanden
7076    fn evaluate_407(&self, ctx: &EvaluationContext) -> ConditionResult {
7077        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7078        // Check: OBIS Blindarbeit code in PIA+5
7079        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7080        let found = pia_segs.iter().any(|s| {
7081            s.elements
7082                .first()
7083                .and_then(|e| e.first())
7084                .is_some_and(|v| v == "5")
7085                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7086                    v.starts_with("1-") && (v.contains(":3.8.") || v.contains(":4.8."))
7087                })
7088        });
7089        ConditionResult::from(found)
7090    }
7091
7092    /// [408] Wenn eine SG8 SEQ+Z02 (OBIS-Daten der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert wie das RFF+Z18 (Referenz auf die I...
7093    fn evaluate_408(&self, ctx: &EvaluationContext) -> ConditionResult {
7094        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7095        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7096        ConditionResult::from(has_seq_z02)
7097    }
7098
7099    /// [409] Wenn keine SG8 SEQ+Z02 (OBIS-Daten der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert wie das RFF+Z18 (Referenz auf die ...
7100    fn evaluate_409(&self, ctx: &EvaluationContext) -> ConditionResult {
7101        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7102        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7103        ConditionResult::from(!has_seq_z02)
7104    }
7105
7106    /// [410] Wenn eine SG8 SEQ+Z02 (OBIS-Daten der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert wie das RFF+Z18 (Referenz auf die I...
7107    fn evaluate_410(&self, ctx: &EvaluationContext) -> ConditionResult {
7108        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7109        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7110        ConditionResult::from(has_seq_z02)
7111    }
7112
7113    /// [411] Wenn keine SG8 SEQ+Z02 (OBIS-Daten der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert wie das RFF+Z18 (Referenz auf die ...
7114    fn evaluate_411(&self, ctx: &EvaluationContext) -> ConditionResult {
7115        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7116        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7117        ConditionResult::from(!has_seq_z02)
7118    }
7119
7120    /// [412] Es sind ausschließlich Konfigurationsprodukt-Codes der \"EDI@Energy Codeliste der Konfigurationen\" aus Kapitel 4.2 Konfigurationsprodukte Leistungskurvendefinition erlaubt
7121    fn evaluate_412(&self, ctx: &EvaluationContext) -> ConditionResult {
7122        ctx.external.evaluate("code_list_membership_check")
7123    }
7124
7125    /// [413] Wenn eine SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Wirkarbeit und kumuliert im PIA+5 vorhanden
7126    fn evaluate_413(&self, ctx: &EvaluationContext) -> ConditionResult {
7127        // Check: SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7128        let found = ctx.find_segments("SEQ").iter().any(|s| {
7129            s.elements
7130                .first()
7131                .and_then(|e| e.first())
7132                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7133        });
7134        ConditionResult::from(found)
7135    }
7136
7137    /// [414] Wenn keine SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Wirkarbeit und kumuliert im PIA+5 vorhanden
7138    fn evaluate_414(&self, ctx: &EvaluationContext) -> ConditionResult {
7139        // Check: no SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7140        let found = ctx.find_segments("SEQ").iter().any(|s| {
7141            s.elements
7142                .first()
7143                .and_then(|e| e.first())
7144                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7145        });
7146        ConditionResult::from(!found)
7147    }
7148
7149    /// [415] Wenn eine weitere SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Wirkarbeit und 1/4 Stunde im PIA+5 vorhanden
7150    fn evaluate_415(&self, ctx: &EvaluationContext) -> ConditionResult {
7151        // Check: another SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7152        let count = ctx
7153            .find_segments("SEQ")
7154            .iter()
7155            .filter(|s| {
7156                s.elements
7157                    .first()
7158                    .and_then(|e| e.first())
7159                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7160            })
7161            .count();
7162        ConditionResult::from(count > 1)
7163    }
7164
7165    /// [416] Wenn keine SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Wirkarbeit und 1/4 Stunde im PIA+5 vorhanden
7166    fn evaluate_416(&self, ctx: &EvaluationContext) -> ConditionResult {
7167        // Check: no other SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7168        let count = ctx
7169            .find_segments("SEQ")
7170            .iter()
7171            .filter(|s| {
7172                s.elements
7173                    .first()
7174                    .and_then(|e| e.first())
7175                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7176            })
7177            .count();
7178        ConditionResult::from(count <= 1)
7179    }
7180
7181    /// [417] Wenn für den erforderlichen Wert keine Zählzeit benötigt wird
7182    fn evaluate_417(&self, _ctx: &EvaluationContext) -> ConditionResult {
7183        // TODO: implement
7184        ConditionResult::Unknown
7185    }
7186
7187    /// [419] Wenn in diesem Datenelement kein anderes Paket in dieser SG10 in derselben SG8 SEQ zur Möglichkeit der Angabe von mindestens einem anderen Code führt
7188    fn evaluate_419(&self, _ctx: &EvaluationContext) -> ConditionResult {
7189        // TODO: implement
7190        ConditionResult::Unknown
7191    }
7192
7193    /// [420] Wenn in dieser SG8 das RFF+Z14 (Smartmeter-Gateway) vorhanden ist
7194    fn evaluate_420(&self, ctx: &EvaluationContext) -> ConditionResult {
7195        // HAND-EDITED: "in dieser SG8" — scoped to the enclosing SG8.
7196        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7197        let found = rff_segs.iter().any(|s| {
7198            s.elements
7199                .first()
7200                .and_then(|e| e.first())
7201                .is_some_and(|v| v == "Z14")
7202        });
7203        ConditionResult::from(found)
7204    }
7205
7206    /// [421] Wenn in dieser SG8 das SG10 CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden ist
7207    fn evaluate_421(&self, ctx: &EvaluationContext) -> ConditionResult {
7208        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing group.
7209        // Check: CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden
7210        ctx.this_group_has_qualifier("CCI", 0, "Z39", &["SG4", "SG8"])
7211    }
7212
7213    /// [425] Messprodukt-Code aus den Kapitel 2.1 \"Standard-Messprodukte der Marktlokation\" der Codeliste der Konfigurationen
7214    fn evaluate_425(&self, ctx: &EvaluationContext) -> ConditionResult {
7215        ctx.external.evaluate("code_list_membership_check")
7216    }
7217
7218    /// [426] Messprodukt-Code aus den Kapitel 2.2 \"Standard-Messprodukte der Tranche\" der Codeliste der Konfigurationen
7219    fn evaluate_426(&self, ctx: &EvaluationContext) -> ConditionResult {
7220        ctx.external.evaluate("code_list_membership_check")
7221    }
7222
7223    /// [427] Messprodukt-Code aus den Kapitel 2.3 \"Standard-Messprodukte der Messlokation\" der Codeliste der Konfigurationen
7224    fn evaluate_427(&self, ctx: &EvaluationContext) -> ConditionResult {
7225        ctx.external.evaluate("code_list_membership_check")
7226    }
7227
7228    /// [428] Wenn in derselben SG8 SEQ+Z27/ ZE2 (Erforderliches Messprodukt der Marktlokation) das PIA+5 DE7140 mit einem Messprodukt aus Codeliste der Konfigurationen Kapitel 2.1.1 Standard-Messprodukt der Mar...
7229    fn evaluate_428(&self, ctx: &EvaluationContext) -> ConditionResult {
7230        ctx.external.evaluate("code_list_membership_check")
7231    }
7232
7233    /// [429] Wenn in derselben SG8 SEQ+Z19/ ZF4 (Erforderliches Messprodukt der Messlokation) das PIA+5 DE7140 mit einem Messprodukt aus Codeliste der Konfigurationen Kapitel 2.3.1 Standard-Messprodukt der Mess...
7234    fn evaluate_429(&self, ctx: &EvaluationContext) -> ConditionResult {
7235        ctx.external.evaluate("code_list_membership_check")
7236    }
7237
7238    /// [430] Wenn eine andere SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert, mit PIA+5+9...
7239    fn evaluate_430(&self, ctx: &EvaluationContext) -> ConditionResult {
7240        // Check: another SG8 SEQ+Z27 exists with matching RFF+Z18
7241        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7242        ConditionResult::from(seq_z27_count > 1)
7243    }
7244
7245    /// [431] Wenn keine andere SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert, mit PIA+5+...
7246    fn evaluate_431(&self, ctx: &EvaluationContext) -> ConditionResult {
7247        // Check: no other SG8 SEQ+Z27 exists with matching RFF+Z18
7248        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7249        ConditionResult::from(seq_z27_count <= 1)
7250    }
7251
7252    /// [432] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
7253    fn evaluate_432(&self, ctx: &EvaluationContext) -> ConditionResult {
7254        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7255        // Check: PIA+5 with code 9991000000078 (Wirkarbeit Lastgang) exists
7256        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7257        let found = pia_segs.iter().any(|s| {
7258            s.elements
7259                .first()
7260                .and_then(|e| e.first())
7261                .is_some_and(|v| v == "5")
7262                && s.elements
7263                    .get(1)
7264                    .and_then(|e| e.first())
7265                    .is_some_and(|v| v == "9991000000078")
7266        });
7267        ConditionResult::from(found)
7268    }
7269
7270    /// [433] Wenn keine andere SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert, mit PIA+5 ...
7271    fn evaluate_433(&self, ctx: &EvaluationContext) -> ConditionResult {
7272        ctx.external.evaluate("code_list_membership_check")
7273    }
7274
7275    /// [435] Wenn eine andere SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert, mit PIA+5 D...
7276    fn evaluate_435(&self, ctx: &EvaluationContext) -> ConditionResult {
7277        ctx.external.evaluate("code_list_membership_check")
7278    }
7279
7280    /// [436] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000086:Z11 (Wirkarbeit höchste 1/4 Stunde im Monat) vorhanden ist
7281    fn evaluate_436(&self, ctx: &EvaluationContext) -> ConditionResult {
7282        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7283        // Check: PIA+5 with code 9991000000086 (Wirkarbeit höchste 1/4h Leistung) exists
7284        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7285        let found = pia_segs.iter().any(|s| {
7286            s.elements
7287                .first()
7288                .and_then(|e| e.first())
7289                .is_some_and(|v| v == "5")
7290                && s.elements
7291                    .get(1)
7292                    .and_then(|e| e.first())
7293                    .is_some_and(|v| v == "9991000000086")
7294        });
7295        ConditionResult::from(found)
7296    }
7297
7298    /// [437] Wenn in dieser SG4 das STS+E01++A04 / A23 (Status der Antwort) vorhanden
7299    fn evaluate_437(&self, ctx: &EvaluationContext) -> ConditionResult {
7300        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
7301        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
7302        let found = sts_segs.iter().any(|s| {
7303            s.elements
7304                .first()
7305                .and_then(|e| e.first())
7306                .is_some_and(|v| v == "E01")
7307                && s.elements
7308                    .get(2)
7309                    .and_then(|e| e.first())
7310                    .is_some_and(|v| ["A04", "A23"].contains(&v.as_str()))
7311        });
7312        ConditionResult::from(found)
7313    }
7314
7315    /// [438] Wenn im selben CCI im DE7059 der Code Z39 (Code der Zählzeit) vorhanden ist
7316    fn evaluate_438(&self, ctx: &EvaluationContext) -> ConditionResult {
7317        // HAND-EDITED: "im selben CCI" — the CCI the field is in (its own segment
7318        // in tree validation), else the enclosing SG10's CCI.
7319        let is_z39 = |elements: &[Vec<String>]| {
7320            elements
7321                .first()
7322                .and_then(|e| e.first())
7323                .is_some_and(|v| v == "Z39")
7324        };
7325        if let Some(own) = ctx.resolved_segment.filter(|_| {
7326            ctx.scoped_find_segments_in("SG10", "CCI")
7327                .iter()
7328                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
7329        }) {
7330            return ConditionResult::from(is_z39(own));
7331        }
7332        ConditionResult::from(
7333            ctx.scoped_find_segments_in("SG10", "CCI")
7334                .iter()
7335                .any(|s| is_z39(&s.elements)),
7336        )
7337    }
7338
7339    /// [440] Wenn in demselben RFF der Code Z35 (vorgelagerte Netzlokation) im DE1153 vorhanden
7340    fn evaluate_440(&self, ctx: &EvaluationContext) -> ConditionResult {
7341        // Check: RFF with qualifier Z35 (vorgelagerte Netzlokation) exists
7342        ctx.has_qualifier("RFF", 0, "Z35")
7343    }
7344
7345    /// [441] Wenn im SG8+SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) für die in diesem RFF DE1154 genannte Gerätenummer eines Zählers das RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden ist
7346    fn evaluate_441(&self, ctx: &EvaluationContext) -> ConditionResult {
7347        // Check: SG8 SEQ+Z03/ZF5 device without RFF+Z14 (SMGW reference)
7348        ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
7349    }
7350
7351    /// [442] Wenn in demselben RFF der Code Z34 (vorgelagerte Messlokation) im DE1153 vorhanden
7352    fn evaluate_442(&self, ctx: &EvaluationContext) -> ConditionResult {
7353        // Check: RFF with qualifier Z34 (vorgelagerte Messlokation) exists
7354        ctx.has_qualifier("RFF", 0, "Z34")
7355    }
7356
7357    /// [444] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das PIA+5 (Produkt-Daten der Netzlokation) nicht vorhanden
7358    // REVIEW: Checks whether any SG8 instance carrying a 'Produkt-Daten der Netzlokation' SEQ qualifier (Z60, ZE0, ZG8, ZG9) lacks PIA+5. Iterates each qualifier separately using any_group_has_qualifier_without which checks: SEQ+code present AND PIA+5 absent in same SG8. Returns True on first match. Correctly handles the multi-qualifier case via OR iteration. (medium confidence)
7359    fn evaluate_444(&self, ctx: &EvaluationContext) -> ConditionResult {
7360        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7361        // scope reaches one; the message as before otherwise.
7362        if let Some(scope) = ctx.enclosing_scope("SG8") {
7363            return ConditionResult::from(
7364                ctx.find_segments_at(scope, "SEQ")
7365                    .iter()
7366                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7367                    && !ctx
7368                        .find_segments_at(scope, "PIA")
7369                        .iter()
7370                        .any(|s| s.get_element(0) == "5"),
7371            );
7372        }
7373        for code in ["Z60", "ZE0", "ZG8", "ZG9"] {
7374            if matches!(
7375                ctx.any_group_has_qualifier_without("SEQ", 0, code, "PIA", 0, "5", &["SG4", "SG8"]),
7376                ConditionResult::True
7377            ) {
7378                return ConditionResult::True;
7379            }
7380        }
7381        ConditionResult::False
7382    }
7383
7384    /// [445] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das SG10 CCI+11 (Details zum Produkt der Netzlokation) nicht vorhanden
7385    // REVIEW: Iterates all SG8 instances and checks whether any SG10 child group contains CCI with elements[0][0]=="11" (Details zum Produkt der Netzlokation). Returns True when an SG8 is found that lacks such a CCI+11 in its SG10 children. Navigator fallback uses filtered_parent_child_has_qualifier negated per qualifier. Approximation: the navigator path does not filter SG8 instances by Produkt-Daten-Netzlokation SEQ qualifier due to absence of find_segments_in_group; checks all SG8 instances instead. (medium confidence)
7386    fn evaluate_445(&self, ctx: &EvaluationContext) -> ConditionResult {
7387        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7388        // scope reaches one; the message as before otherwise.
7389        if let Some(scope) = ctx.enclosing_scope("SG8") {
7390            return ConditionResult::from(
7391                ctx.find_segments_at(scope, "SEQ")
7392                    .iter()
7393                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7394                    && !ctx
7395                        .find_segments_at(scope, "CCI")
7396                        .iter()
7397                        .any(|s| s.get_element(0) == "11"),
7398            );
7399        }
7400        let nav = match ctx.navigator {
7401            Some(n) => n,
7402            None => {
7403                let has_cci11 = ["Z60", "ZE0", "ZG8", "ZG9"].iter().any(|code| {
7404                    matches!(
7405                        ctx.filtered_parent_child_has_qualifier(
7406                            &["SG4", "SG8"],
7407                            "SEQ",
7408                            0,
7409                            code,
7410                            "SG10",
7411                            "CCI",
7412                            0,
7413                            "11",
7414                        ),
7415                        ConditionResult::True
7416                    )
7417                });
7418                return ConditionResult::from(!has_cci11);
7419            }
7420        };
7421
7422        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7423        for i in 0..sg8_count {
7424            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7425            let has_cci11 = (0..sg10_count).any(|j| {
7426                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7427                ccis.iter().any(|s| {
7428                    s.elements
7429                        .first()
7430                        .and_then(|e: &Vec<String>| e.first())
7431                        .is_some_and(|v: &String| v == "11")
7432                })
7433            });
7434            if !has_cci11 {
7435                return ConditionResult::True;
7436            }
7437        }
7438        ConditionResult::False
7439    }
7440
7441    /// [446] Wenn in derselben SG8 das CCI+Z17 (Stromverbrauchsart) vorhanden
7442    fn evaluate_446(&self, ctx: &EvaluationContext) -> ConditionResult {
7443        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7444        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7445        let found = cci_segs.iter().any(|s| {
7446            s.elements
7447                .first()
7448                .and_then(|e| e.first())
7449                .is_some_and(|v| v == "Z17")
7450        });
7451        ConditionResult::from(found)
7452    }
7453
7454    /// [447] Wenn in derselben SG8 das CCI+Z50 (Stromerzeugungsart) vorhanden
7455    fn evaluate_447(&self, ctx: &EvaluationContext) -> ConditionResult {
7456        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7457        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7458        let found = cci_segs.iter().any(|s| {
7459            s.elements
7460                .first()
7461                .and_then(|e| e.first())
7462                .is_some_and(|v| v == "Z50")
7463        });
7464        ConditionResult::from(found)
7465    }
7466
7467    /// [448] Wenn in derselben SG8 das CCI+Z56 (Speicher) vorhanden
7468    fn evaluate_448(&self, ctx: &EvaluationContext) -> ConditionResult {
7469        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7470        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7471        let found = cci_segs.iter().any(|s| {
7472            s.elements
7473                .first()
7474                .and_then(|e| e.first())
7475                .is_some_and(|v| v == "Z56")
7476        });
7477        ConditionResult::from(found)
7478    }
7479
7480    /// [449] Wenn in derselben SG8 das SG10 CCI+6++ZA9 CAV=ZG3 (Aggreg.verantw. ÜNB und Lokation im Regelbetrieb) vorhanden
7481    fn evaluate_449(&self, ctx: &EvaluationContext) -> ConditionResult {
7482        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7483        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7484        let has_cci = cci_segs.iter().any(|s| {
7485            s.elements
7486                .first()
7487                .and_then(|e| e.first())
7488                .is_some_and(|v| v == "6")
7489                && s.elements
7490                    .get(2)
7491                    .and_then(|e| e.first())
7492                    .is_some_and(|v| v == "ZA9")
7493        });
7494        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
7495        let has_cav = cav_segs.iter().any(|s| {
7496            s.elements
7497                .first()
7498                .and_then(|e| e.first())
7499                .is_some_and(|v| v == "ZG3")
7500        });
7501        ConditionResult::from(has_cci && has_cav)
7502    }
7503
7504    /// [450] Wenn in demselben RFF der Code Z32 (Netzlokation) im DE1153 vorhanden
7505    fn evaluate_450(&self, ctx: &EvaluationContext) -> ConditionResult {
7506        // Check: RFF with qualifier Z32 (Netzlokation) exists
7507        ctx.has_qualifier("RFF", 0, "Z32")
7508    }
7509
7510    /// [451] Wenn in demselben RFF der Code Z18 (Marktlokation) im DE1153 vorhanden
7511    fn evaluate_451(&self, ctx: &EvaluationContext) -> ConditionResult {
7512        // Check: RFF with qualifier Z18 (Marktlokation) exists
7513        ctx.has_qualifier("RFF", 0, "Z18")
7514    }
7515
7516    /// [452] Wenn in demselben RFF der Code Z19 (Messlokation) im DE1153 vorhanden
7517    fn evaluate_452(&self, ctx: &EvaluationContext) -> ConditionResult {
7518        // Check: RFF with qualifier Z19 (Messlokation) exists
7519        ctx.has_qualifier("RFF", 0, "Z19")
7520    }
7521
7522    /// [453] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Auslöser „Bei Schwellwertunter-/ -überschreitung" mit selbständiger Änderungsmöglichke...
7523    fn evaluate_453(&self, ctx: &EvaluationContext) -> ConditionResult {
7524        ctx.external.evaluate("code_list_membership_check")
7525    }
7526
7527    /// [454] Das Bilanzierungsbeginn Datum muss kleiner sein als das Bilanzierungsende Datum in der SG8 „Datenstand des ÜNB", SG 9 Energiemenge DZÜ Anteil
7528    fn evaluate_454(&self, _ctx: &EvaluationContext) -> ConditionResult {
7529        // Check: begin date < end date (structural — always true for valid messages)
7530        ConditionResult::True
7531    }
7532
7533    /// [455] Das Bilanzierungsende-Datum muss größer sein, als das Bilanzierungsbeginn Datum in der SG8 „Datenstand des ÜNB" , SG 9 Energiemenge DZÜ Anteil
7534    fn evaluate_455(&self, _ctx: &EvaluationContext) -> ConditionResult {
7535        // Check: end date > begin date (structural — always true for valid messages)
7536        ConditionResult::True
7537    }
7538
7539    /// [456] Wenn in demselben RFF der Code Z37 (Technische Ressource) im DE1153 vorhanden
7540    fn evaluate_456(&self, ctx: &EvaluationContext) -> ConditionResult {
7541        // Check: RFF with qualifier Z37 (Technische Ressource) exists
7542        ctx.has_qualifier("RFF", 0, "Z37")
7543    }
7544
7545    /// [457] Wenn bei der Bestellung das Messprodukten 9991000001498 bestellt wurde
7546    fn evaluate_457(&self, ctx: &EvaluationContext) -> ConditionResult {
7547        // Check: PIA+5 with Messprodukt 9991000001498 exists
7548        let pia_segs = ctx.find_segments("PIA");
7549        let found = pia_segs.iter().any(|s| {
7550            s.elements
7551                .first()
7552                .and_then(|e| e.first())
7553                .is_some_and(|v| v == "5")
7554                && s.elements
7555                    .get(1)
7556                    .and_then(|e| e.first())
7557                    .is_some_and(|v| v == "9991000001498")
7558        });
7559        ConditionResult::from(found)
7560    }
7561
7562    /// [458] Wenn bei der Bestellung das Konfigurationsprodukte 9991000000739 bestellt wurde
7563    fn evaluate_458(&self, ctx: &EvaluationContext) -> ConditionResult {
7564        // Check: PIA+5 with Konfigurationsprodukt 9991000000739 exists
7565        let pia_segs = ctx.find_segments("PIA");
7566        let found = pia_segs.iter().any(|s| {
7567            s.elements
7568                .first()
7569                .and_then(|e| e.first())
7570                .is_some_and(|v| v == "5")
7571                && s.elements
7572                    .get(1)
7573                    .and_then(|e| e.first())
7574                    .is_some_and(|v| v == "9991000000739")
7575        });
7576        ConditionResult::from(found)
7577    }
7578
7579    /// [459] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"CLS-Direkt aus dem SMGW\" bestellt wurde
7580    fn evaluate_459(&self, ctx: &EvaluationContext) -> ConditionResult {
7581        ctx.external.evaluate("code_list_membership_check")
7582    }
7583
7584    /// [460] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"aus dem SMGW\" bestellt wurde
7585    fn evaluate_460(&self, ctx: &EvaluationContext) -> ConditionResult {
7586        ctx.external.evaluate("code_list_membership_check")
7587    }
7588
7589    /// [461] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden
7590    fn evaluate_461(&self, ctx: &EvaluationContext) -> ConditionResult {
7591        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7592        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7593        let found = cci_segs.iter().any(|s| {
7594            s.elements
7595                .first()
7596                .and_then(|e| e.first())
7597                .is_some_and(|v| v == "Z17")
7598        });
7599        ConditionResult::from(found)
7600    }
7601
7602    /// [462] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden
7603    fn evaluate_462(&self, ctx: &EvaluationContext) -> ConditionResult {
7604        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7605        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7606        let found = cci_segs.iter().any(|s| {
7607            s.elements
7608                .first()
7609                .and_then(|e| e.first())
7610                .is_some_and(|v| v == "Z17")
7611        });
7612        ConditionResult::from(found)
7613    }
7614
7615    /// [463] Wenn in derselben SG10 das CCI+Z61++ZF9 (Kunde erfüllt die Voraussetzung nach EnFG) vorhanden
7616    fn evaluate_463(&self, ctx: &EvaluationContext) -> ConditionResult {
7617        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7618        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7619        let found = cci_segs.iter().any(|s| {
7620            s.elements
7621                .first()
7622                .and_then(|e| e.first())
7623                .is_some_and(|v| v == "Z61")
7624                && s.elements
7625                    .get(2)
7626                    .and_then(|e| e.first())
7627                    .is_some_and(|v| v == "ZF9")
7628        });
7629        ConditionResult::from(found)
7630    }
7631
7632    /// [465] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z38 (Referenz auf die der Technischen Ressource zugeordnete Steuerbare Ressource) vorhanden ist
7633    fn evaluate_465(&self, ctx: &EvaluationContext) -> ConditionResult {
7634        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7635        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7636        let found = rff_segs.iter().any(|s| {
7637            s.elements
7638                .first()
7639                .and_then(|e| e.first())
7640                .is_some_and(|v| v == "Z38")
7641        });
7642        ConditionResult::from(found)
7643    }
7644
7645    /// [466] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z32 (Referenz auf die der Technischen Ressource zugeordneten Netzlokation) vorhanden ist
7646    fn evaluate_466(&self, ctx: &EvaluationContext) -> ConditionResult {
7647        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7648        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7649        let found = rff_segs.iter().any(|s| {
7650            s.elements
7651                .first()
7652                .and_then(|e| e.first())
7653                .is_some_and(|v| v == "Z32")
7654        });
7655        ConditionResult::from(found)
7656    }
7657
7658    /// [467] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden ist
7659    fn evaluate_467(&self, ctx: &EvaluationContext) -> ConditionResult {
7660        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7661        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7662        let found = segs.iter().any(|s| {
7663            s.elements
7664                .first()
7665                .and_then(|e| e.first())
7666                .is_some_and(|v| v == "ZG8")
7667        });
7668        ConditionResult::from(found)
7669    }
7670
7671    /// [468] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH0 (Inbetriebsetzung der TR nach 2023) vorhanden ist
7672    fn evaluate_468(&self, ctx: &EvaluationContext) -> ConditionResult {
7673        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7674        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7675        let found = segs.iter().any(|s| {
7676            s.elements
7677                .first()
7678                .and_then(|e| e.first())
7679                .is_some_and(|v| v == "ZH0")
7680        });
7681        ConditionResult::from(found)
7682    }
7683
7684    /// [469] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden ist
7685    fn evaluate_469(&self, ctx: &EvaluationContext) -> ConditionResult {
7686        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7687        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7688        let found = segs.iter().any(|s| {
7689            s.elements
7690                .first()
7691                .and_then(|e| e.first())
7692                .is_some_and(|v| v == "ZH1")
7693        });
7694        ConditionResult::from(found)
7695    }
7696
7697    /// [470] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZH5 (Wechsel in das in das § 14a EnWG-Modell gem. Festlegung BK6-22-300 wurde durchgeführt) vorhanden ist
7698    fn evaluate_470(&self, ctx: &EvaluationContext) -> ConditionResult {
7699        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7700        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7701        let found = segs.iter().any(|s| {
7702            s.elements
7703                .first()
7704                .and_then(|e| e.first())
7705                .is_some_and(|v| v == "ZH5")
7706        });
7707        ConditionResult::from(found)
7708    }
7709
7710    /// [471] Wenn im selben SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: Gültige Daten/ Keine Daten) im DE1156 (Zeitraum-ID) eine Zeitraum ID genannt ist, die kleiner ist als in einem anderen SG6 RFF+Z49/ ...
7711    fn evaluate_471(&self, ctx: &EvaluationContext) -> ConditionResult {
7712        // Check: SG6 RFF+Z49/Z53 has Zeitraum-ID > 1 in DE1156
7713        let found = ctx.find_segments("RFF").iter().any(|s| {
7714            let qual = s
7715                .elements
7716                .first()
7717                .and_then(|e| e.first())
7718                .map(|v| v.as_str());
7719            matches!(qual, Some("Z49") | Some("Z53"))
7720                && s.elements
7721                    .first()
7722                    .and_then(|e| e.get(2))
7723                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7724        });
7725        ConditionResult::from(found)
7726    }
7727
7728    /// [472] Wenn im selben SG6 RFF+Z48/ Z55 (Verwendungszeitraum der Daten: Erwartete Daten/ Keine Daten erwartet) im DE1156 (Zeitraum-ID) eine Zeitraum ID genannt ist, die kleiner ist als in einem anderen SG6...
7729    fn evaluate_472(&self, ctx: &EvaluationContext) -> ConditionResult {
7730        // Check: SG6 RFF+Z48/Z55 has Zeitraum-ID > 1 in DE1156
7731        let found = ctx.find_segments("RFF").iter().any(|s| {
7732            let qual = s
7733                .elements
7734                .first()
7735                .and_then(|e| e.first())
7736                .map(|v| v.as_str());
7737            matches!(qual, Some("Z48") | Some("Z55"))
7738                && s.elements
7739                    .first()
7740                    .and_then(|e| e.get(2))
7741                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7742        });
7743        ConditionResult::from(found)
7744    }
7745
7746    /// [473] Wenn im selben SG6 RFF+Z47/ Z54 (Verwendungszeitraum der Daten: Im System vorhandene Daten/ Im System keine Daten vorhanden) im DE1156 (Zeitraum-ID) eine Zeitraum ID genannt ist, die kleiner ist al...
7747    fn evaluate_473(&self, ctx: &EvaluationContext) -> ConditionResult {
7748        // Check: SG6 RFF+Z47/Z54 has Zeitraum-ID > 1 in DE1156
7749        let found = ctx.find_segments("RFF").iter().any(|s| {
7750            let qual = s
7751                .elements
7752                .first()
7753                .and_then(|e| e.first())
7754                .map(|v| v.as_str());
7755            matches!(qual, Some("Z47") | Some("Z54"))
7756                && s.elements
7757                    .first()
7758                    .and_then(|e| e.get(2))
7759                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7760        });
7761        ConditionResult::from(found)
7762    }
7763
7764    /// [474] Wenn SG4 STS+7++ZC8 (Transaktionsgrund \"Beendigung der Zuordnung\") vorhanden
7765    fn evaluate_474(&self, ctx: &EvaluationContext) -> ConditionResult {
7766        let sts_segs = ctx.find_segments("STS");
7767        let found = sts_segs.iter().any(|s| {
7768            s.elements
7769                .first()
7770                .and_then(|e| e.first())
7771                .is_some_and(|v| v == "7")
7772                && s.elements
7773                    .get(2)
7774                    .and_then(|e| e.first())
7775                    .is_some_and(|v| ["ZC8"].contains(&v.as_str()))
7776        });
7777        ConditionResult::from(found)
7778    }
7779
7780    /// [475] Wenn SG4 STS+7++ZH1 (Transaktionsgrund \"Aufhebung einer zukünftigen Zuordnung wegen Stilllegung\") vorhanden
7781    fn evaluate_475(&self, ctx: &EvaluationContext) -> ConditionResult {
7782        let sts_segs = ctx.find_segments("STS");
7783        let found = sts_segs.iter().any(|s| {
7784            s.elements
7785                .first()
7786                .and_then(|e| e.first())
7787                .is_some_and(|v| v == "7")
7788                && s.elements
7789                    .get(2)
7790                    .and_then(|e| e.first())
7791                    .is_some_and(|v| ["ZH1"].contains(&v.as_str()))
7792        });
7793        ConditionResult::from(found)
7794    }
7795
7796    /// [476] Wenn SG4 STS+7++xxx+ZW0 (Transaktionsgrundergänzung Geschäftsvorfall 1) vorhanden
7797    fn evaluate_476(&self, ctx: &EvaluationContext) -> ConditionResult {
7798        let sts_segs = ctx.find_segments("STS");
7799        let found = sts_segs.iter().any(|s| {
7800            s.elements
7801                .first()
7802                .and_then(|e| e.first())
7803                .is_some_and(|v| v == "7")
7804                && s.elements
7805                    .get(3)
7806                    .and_then(|e| e.first())
7807                    .is_some_and(|v| ["ZW0"].contains(&v.as_str()))
7808        });
7809        ConditionResult::from(found)
7810    }
7811
7812    /// [477] Wenn SG4 STS+7++xxx+ZW1 (Transaktionsgrundergänzung Geschäftsvorfall 2) vorhanden
7813    fn evaluate_477(&self, ctx: &EvaluationContext) -> ConditionResult {
7814        let sts_segs = ctx.find_segments("STS");
7815        let found = sts_segs.iter().any(|s| {
7816            s.elements
7817                .first()
7818                .and_then(|e| e.first())
7819                .is_some_and(|v| v == "7")
7820                && s.elements
7821                    .get(3)
7822                    .and_then(|e| e.first())
7823                    .is_some_and(|v| ["ZW1"].contains(&v.as_str()))
7824        });
7825        ConditionResult::from(found)
7826    }
7827
7828    /// [478] Wenn SG4 STS+7++xxx+ZW2 (Transaktionsgrundergänzung Geschäftsvorfall 3) vorhanden
7829    fn evaluate_478(&self, ctx: &EvaluationContext) -> ConditionResult {
7830        let sts_segs = ctx.find_segments("STS");
7831        let found = sts_segs.iter().any(|s| {
7832            s.elements
7833                .first()
7834                .and_then(|e| e.first())
7835                .is_some_and(|v| v == "7")
7836                && s.elements
7837                    .get(3)
7838                    .and_then(|e| e.first())
7839                    .is_some_and(|v| ["ZW2"].contains(&v.as_str()))
7840        });
7841        ConditionResult::from(found)
7842    }
7843
7844    /// [479] Wenn SG4 STS+7++xxx+ZW3 (Transaktionsgrundergänzung Erzeugende Marktlokation) vorhanden
7845    fn evaluate_479(&self, ctx: &EvaluationContext) -> ConditionResult {
7846        let sts_segs = ctx.find_segments("STS");
7847        let found = sts_segs.iter().any(|s| {
7848            s.elements
7849                .first()
7850                .and_then(|e| e.first())
7851                .is_some_and(|v| v == "7")
7852                && s.elements
7853                    .get(3)
7854                    .and_then(|e| e.first())
7855                    .is_some_and(|v| ["ZW3"].contains(&v.as_str()))
7856        });
7857        ConditionResult::from(found)
7858    }
7859
7860    /// [480] Wenn SG4 STS+7++xxx+ZW4 (Transaktionsgrundergänzung Verbrauchende Marktlokation) vorhanden
7861    fn evaluate_480(&self, ctx: &EvaluationContext) -> ConditionResult {
7862        let sts_segs = ctx.find_segments("STS");
7863        let found = sts_segs.iter().any(|s| {
7864            s.elements
7865                .first()
7866                .and_then(|e| e.first())
7867                .is_some_and(|v| v == "7")
7868                && s.elements
7869                    .get(3)
7870                    .and_then(|e| e.first())
7871                    .is_some_and(|v| ["ZW4"].contains(&v.as_str()))
7872        });
7873        ConditionResult::from(found)
7874    }
7875
7876    /// [481] Wenn SG4 STS+7++xxx+ZW5 (Transaktionsgrundergänzung Tranche) vorhanden
7877    fn evaluate_481(&self, ctx: &EvaluationContext) -> ConditionResult {
7878        let sts_segs = ctx.find_segments("STS");
7879        let found = sts_segs.iter().any(|s| {
7880            s.elements
7881                .first()
7882                .and_then(|e| e.first())
7883                .is_some_and(|v| v == "7")
7884                && s.elements
7885                    .get(3)
7886                    .and_then(|e| e.first())
7887                    .is_some_and(|v| ["ZW5"].contains(&v.as_str()))
7888        });
7889        ConditionResult::from(found)
7890    }
7891
7892    /// [483] Wenn SG4 STS+7++xxx+ZW7 (Transaktionsgrundergänzung Gemessene Marktlokation) vorhanden
7893    fn evaluate_483(&self, ctx: &EvaluationContext) -> ConditionResult {
7894        let sts_segs = ctx.find_segments("STS");
7895        let found = sts_segs.iter().any(|s| {
7896            s.elements
7897                .first()
7898                .and_then(|e| e.first())
7899                .is_some_and(|v| v == "7")
7900                && s.elements
7901                    .get(3)
7902                    .and_then(|e| e.first())
7903                    .is_some_and(|v| ["ZW7"].contains(&v.as_str()))
7904        });
7905        ConditionResult::from(found)
7906    }
7907
7908    /// [484] Wenn SG4 STS+7++xxx+ZW8 (Transaktionsgrundergänzung Fall1) vorhanden
7909    fn evaluate_484(&self, ctx: &EvaluationContext) -> ConditionResult {
7910        let sts_segs = ctx.find_segments("STS");
7911        let found = sts_segs.iter().any(|s| {
7912            s.elements
7913                .first()
7914                .and_then(|e| e.first())
7915                .is_some_and(|v| v == "7")
7916                && s.elements
7917                    .get(3)
7918                    .and_then(|e| e.first())
7919                    .is_some_and(|v| ["ZW8"].contains(&v.as_str()))
7920        });
7921        ConditionResult::from(found)
7922    }
7923
7924    /// [487] Wenn SG4 STS+7++xxx+ZX1 (Transaktionsgrundergänzung Fall4) vorhanden
7925    fn evaluate_487(&self, ctx: &EvaluationContext) -> ConditionResult {
7926        let sts_segs = ctx.find_segments("STS");
7927        let found = sts_segs.iter().any(|s| {
7928            s.elements
7929                .first()
7930                .and_then(|e| e.first())
7931                .is_some_and(|v| v == "7")
7932                && s.elements
7933                    .get(3)
7934                    .and_then(|e| e.first())
7935                    .is_some_and(|v| ["ZX1"].contains(&v.as_str()))
7936        });
7937        ConditionResult::from(found)
7938    }
7939
7940    /// [489] Nur bei dem ältesten Zeitraum welcher mit SG6 RFF+Z49 (Verwendungszeitraum der Daten: Gültige Daten) beschrieben ist
7941    fn evaluate_489(&self, ctx: &EvaluationContext) -> ConditionResult {
7942        // Check: this is the oldest Zeitraum (lowest Zeitraum-ID) with RFF+Z49
7943        let rff_z49 = ctx.find_segments_with_qualifier("RFF", 0, "Z49");
7944        if rff_z49.is_empty() {
7945            return ConditionResult::Unknown;
7946        }
7947        // Approximate: condition applies when RFF+Z49 exists
7948        ConditionResult::True
7949    }
7950
7951    /// [490] Wenn Wert in diesem DE, an der Stelle CCYYMMDD ein Datum aus dem angegeben Zeitraum der Tabelle Kapitel 3.5 „Prozesszeitpunkt bei MESZ mit UTC" ist
7952    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
7953        ctx.format_check("DTM", 0, 1, is_mesz_utc)
7954    }
7955
7956    /// [491] Wenn Wert in diesem DE, an der Stelle CCYYMMDD ein Datum aus dem angegeben Zeitraum der Tabelle Kapitel 3.6 „Prozesszeitpunkt bei MEZ mit UTC"" ist
7957    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
7958        ctx.format_check("DTM", 0, 1, is_mez_utc)
7959    }
7960
7961    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
7962    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
7963        // Document date must be <= now. Format 303 is CCYYMMDDHHMM — fixed-width,
7964        // so lexicographic comparison works directly.
7965        let val = ctx.resolved_value.or_else(|| {
7966            ctx.find_segments_with_qualifier("DTM", 0, "137")
7967                .first()
7968                .and_then(|s| s.elements.first())
7969                .and_then(|e| e.get(1))
7970                .map(|v| v.as_str())
7971        });
7972        let Some(dtm_val) = val else {
7973            return ConditionResult::Unknown;
7974        };
7975        if dtm_val.len() < 12 {
7976            return ConditionResult::Unknown;
7977        }
7978        let now_str = crate::eval::format_validators::utc_now_ccyymmddhhmm();
7979        ConditionResult::from(&dtm_val[..12] <= now_str.as_str())
7980    }
7981
7982    /// [500] Hinweis: Code ist gemäß der Kategorie der zu stornierenden Meldung zu wählen
7983    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
7984        ConditionResult::True
7985    }
7986
7987    /// [501] Hinweis: Es sind zur Unterstützung die Daten des LFA anzugeben
7988    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
7989        ConditionResult::True
7990    }
7991
7992    /// [502] Hinweis: Es wird der Betrachtungsmonat der DZR bzw. BAS angegeben
7993    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
7994        ConditionResult::True
7995    }
7996
7997    /// [503] Hinweis: Angabe des BGM DE1004 aus der ORDERS
7998    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
7999        ConditionResult::True
8000    }
8001
8002    /// [504] Hinweis: Je Profil (ggf. inkl. Profilschar) ein Vorgang erforderlich
8003    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
8004        ConditionResult::True
8005    }
8006
8007    /// [505] Hinweis: Je zugeordneter ID und je EEG-Überführungszeitreihe ein Vorgang erforderlich
8008    fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
8009        ConditionResult::True
8010    }
8011
8012    /// [506] Hinweis: Für jeden Code im LOC+Z16 (Marktlokation) sollen die entsprechenden Kundendaten des LF angegeben werden, falls bekannt
8013    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
8014        ConditionResult::True
8015    }
8016
8017    /// [507] Hinweis: Ursprünglich vom NB bestätigtes Beginndatum
8018    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
8019        ConditionResult::True
8020    }
8021
8022    /// [508] Hinweis: Ausschließlich kME sind über die NNE abrechenbar
8023    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
8024        ConditionResult::True
8025    }
8026
8027    /// [509] Hinweis: Handelt sich um ein mME oder iMS ist das RFF 1154 immer mit NEIN anzugeben
8028    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
8029        ConditionResult::True
8030    }
8031
8032    /// [510] Hinweis: Zu verwenden bei der Abmeldung der ESV
8033    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
8034        ConditionResult::True
8035    }
8036
8037    /// [511] Hinweis: Es sind alle Bilanzierungsgebiete anzugeben in denen das Profil verwendet wird
8038    fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
8039        ConditionResult::True
8040    }
8041
8042    /// [512] Hinweis: Es ist informativ die bisherige Veräußerungsform des LFA an der erzeugenden Marktlokation anzugeben
8043    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
8044        ConditionResult::True
8045    }
8046
8047    /// [513] Hinweis: Es ist das nächstmögliche Datum ab dem DTM+471 aus der entsprechenden Anfrage zu ermitteln
8048    fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
8049        ConditionResult::True
8050    }
8051
8052    /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
8053    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
8054        ConditionResult::True
8055    }
8056
8057    /// [515] Hinweis: Für den ZP der LieferantensummenZR anzugeben
8058    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
8059        ConditionResult::True
8060    }
8061
8062    /// [516] Hinweis: Es ist der Änderungszeitpunkt anzugeben an dem das Zuordnungsende des NBA und der Zuordnungsbeginn des NBN zu einer Marktlokation fallen
8063    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
8064        ConditionResult::True
8065    }
8066
8067    /// [517] Hinweis: Anzuwenden wenn einer Marktlokation eine Paket-ID zugeordnet wurde und diese wieder gelöscht werden soll, da sie nicht unter den genannten NB-Wechsel fällt
8068    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
8069        ConditionResult::True
8070    }
8071
8072    /// [518] Hinweis: Es sind alle Altlieferanten anzugeben, an die eine Abmeldeanfrage gesendet wird
8073    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
8074        ConditionResult::True
8075    }
8076
8077    /// [519] Hinweis: Wenn in der Anmeldung der Code ZAP vorhanden war, so ist dieser auch in der Antwort zu verwenden
8078    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
8079        ConditionResult::True
8080    }
8081
8082    /// [520] Hinweis: Bei der Verwendung des Codes ZAP handelt es sich immer um eine gemessene Marktlokation
8083    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
8084        ConditionResult::True
8085    }
8086
8087    /// [521] Hinweis: Wenn im zweiten DE 9013 des STS+7 (Transaktionsgrund) der Code ZAP vorhanden ist, so ist das hier angegebene Datum als Zuordnungsbeginn bei der Bildung der \"Ruhenden MaLo zu verstehen. Be...
8088    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
8089        ConditionResult::True
8090    }
8091
8092    /// [522] Hinweis: Es ist der NBN anzugeben
8093    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
8094        ConditionResult::True
8095    }
8096
8097    /// [523] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
8098    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
8099        ConditionResult::True
8100    }
8101
8102    /// [524] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, für die der LF nicht die gemeldete Ansicht des NB teilt
8103    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
8104        ConditionResult::True
8105    }
8106
8107    /// [525] Hinweis: Es sind nur die OBIS-Kennzahlen erlaubt, die im vorherigen Stammdatenaustausch zu dieser Marktlokation vom MSB zu diesem Zeitpunkt übermittelt wurden
8108    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
8109        ConditionResult::True
8110    }
8111
8112    /// [526] Hinweis: Wenn der Marktlokation keine Technische Ressource in der Lokationsbündelstruktur zugeordnet wurde
8113    fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
8114        ConditionResult::True
8115    }
8116
8117    /// [527] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben
8118    fn evaluate_527(&self, _ctx: &EvaluationContext) -> ConditionResult {
8119        ConditionResult::True
8120    }
8121
8122    /// [528] Hinweis: Es ist das Datum/ Daten aus der Anfrage zu verwenden
8123    fn evaluate_528(&self, _ctx: &EvaluationContext) -> ConditionResult {
8124        ConditionResult::True
8125    }
8126
8127    /// [529] Hinweis: Für zusätzliche nicht im Markt standardisierte Identifikatoren wie z.B. eine Netzbetreiber-Projektnummer
8128    fn evaluate_529(&self, _ctx: &EvaluationContext) -> ConditionResult {
8129        ConditionResult::True
8130    }
8131
8132    /// [530] Hinweis: Es sind alle an der Lokation vorhandenen Daten, die mit dieser Segmentgruppe übermittelt werden und zum Datum „Änderung zum" Gültigkeit haben, anzugeben. Dies kann zur Folge haben, ...
8133    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
8134        ConditionResult::True
8135    }
8136
8137    /// [531] Hinweis: Es ist das Jahr anzugeben in dem die nächste Netznutzungsabrechnung erfolgt
8138    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
8139        ConditionResult::True
8140    }
8141
8142    /// [532] Hinweis: Kritische Daten (gemäß GPKE)
8143    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
8144        ConditionResult::True
8145    }
8146
8147    /// [533] Hinweis: Es ist die MP-ID des Lieferanten anzugeben
8148    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
8149        ConditionResult::True
8150    }
8151
8152    /// [555] Die Anwendungsfälle für die Durchführung der BDEW-Anwendungshilfe „Marktprozesse Netzbetreiberwechsel Sparte Strom" sind ab dem 01.08.2025 für Netzbetreiberwechsel ab dem 01.01.2026 zu verw...
8153    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
8154        // TODO: implement
8155        ConditionResult::Unknown
8156    }
8157
8158    /// [556] Hinweis: Wenn keine Korrespondenzanschrift des Endverbrauchers/ Kunden vorliegt, ist die Anschrift der Marktlokation zu übermitteln
8159    fn evaluate_556(&self, _ctx: &EvaluationContext) -> ConditionResult {
8160        ConditionResult::True
8161    }
8162
8163    /// [558] Hinweis: Diese Information kann freiwillig ausgetauscht werden
8164    fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
8165        ConditionResult::True
8166    }
8167
8168    /// [559] Hinweis: Die Korrespondenzanschrift des Endverbrauchers/Kunden wird nicht zur Identifikation genutzt
8169    fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
8170        ConditionResult::True
8171    }
8172
8173    /// [563] Hinweis: Für die ID der LieferantensummenZR
8174    fn evaluate_563(&self, _ctx: &EvaluationContext) -> ConditionResult {
8175        ConditionResult::True
8176    }
8177
8178    /// [566] Hinweis: Altlieferant
8179    fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
8180        ConditionResult::True
8181    }
8182
8183    /// [567] Hinweis: Neulieferant
8184    fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
8185        ConditionResult::True
8186    }
8187
8188    /// [568] Hinweis: Lieferant der LieferantensummenZR
8189    fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
8190        ConditionResult::True
8191    }
8192
8193    /// [569] Hinweis: Dritter Nutzer
8194    fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
8195        ConditionResult::True
8196    }
8197
8198    /// [570] Hinweis: Netzbetreiber Alt
8199    fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
8200        ConditionResult::True
8201    }
8202
8203    /// [572] Hinweis: Kundenname aus Anmeldung Lieferant neu
8204    fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
8205        ConditionResult::True
8206    }
8207
8208    /// [576] Hinweis: Stammdaten des bisherigen Messstellenbetreibers
8209    fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
8210        ConditionResult::True
8211    }
8212
8213    /// [577] Hinweis: Wird mit dem ursprünglich vom NB bestätigten Beginnzeitpunkt gefüllt
8214    fn evaluate_577(&self, _ctx: &EvaluationContext) -> ConditionResult {
8215        ConditionResult::True
8216    }
8217
8218    /// [579] Hinweis: Auslösender Marktpartner (LFN bei STS+7++ZH0/ZG9, NB bei STS+7++ZH1)
8219    fn evaluate_579(&self, _ctx: &EvaluationContext) -> ConditionResult {
8220        ConditionResult::True
8221    }
8222
8223    /// [580] Hinweis:  Ersatzbelieferung gibt es nur bei  - Marktlokationen in der  Niederspannung, die kein Haushaltskunde gem. EnWG sind und die nicht mehr der gesetzlichen Ersatzversorgung (drei Monate) unte...
8224    fn evaluate_580(&self, _ctx: &EvaluationContext) -> ConditionResult {
8225        ConditionResult::True
8226    }
8227
8228    /// [581] Hinweis: Es ist der Zeitpunkt anzugeben, zu welchem der Vertrag am Tag des Versandes der Antwort noch kündbar ist.
8229    fn evaluate_581(&self, _ctx: &EvaluationContext) -> ConditionResult {
8230        ConditionResult::True
8231    }
8232
8233    /// [586] Hinweis: Die Messlokationsadresse ist der Messlokation zugeordnet, welche in SG8 SEQ+Z18/ ZF3 (Daten der Messlokation) mit CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokatio...
8234    fn evaluate_586(&self, _ctx: &EvaluationContext) -> ConditionResult {
8235        ConditionResult::True
8236    }
8237
8238    /// [590] Hinweis: Für den Empfang des Steuerbefehls
8239    fn evaluate_590(&self, _ctx: &EvaluationContext) -> ConditionResult {
8240        ConditionResult::True
8241    }
8242
8243    /// [594] Hinweis: Es ist der ZPB des ZP der NGZ und die ZPB der NZR anzugeben
8244    fn evaluate_594(&self, _ctx: &EvaluationContext) -> ConditionResult {
8245        ConditionResult::True
8246    }
8247
8248    /// [599] Hinweis: Es ist der Name und die Adresse des Ablesekartenempfängers für die Messlokation anzugeben, welche in SG8 SEQ+Z18 (Daten der Messlokation) mit CCI+Z01++Z82 (Verwendungsumfang: ID der proz...
8249    fn evaluate_599(&self, _ctx: &EvaluationContext) -> ConditionResult {
8250        ConditionResult::True
8251    }
8252
8253    /// [601] Hinweis: Es ist die ID der Marktlokation und alle Identifikatoren der Messlokationen anzugeben. Sowie wenn vorhanden die Tranche/n der Marklokation
8254    fn evaluate_601(&self, _ctx: &EvaluationContext) -> ConditionResult {
8255        ConditionResult::True
8256    }
8257
8258    /// [606] Hinweis: In diesem Segment bzw. SG sind die Daten aus der Sicht des NB anzugeben
8259    fn evaluate_606(&self, _ctx: &EvaluationContext) -> ConditionResult {
8260        ConditionResult::True
8261    }
8262
8263    /// [609] Hinweis: In diesem Segment bzw. SG sind die zwischen NB, LF und ÜNB im Rahmen der Marktkommunikation ausgetauschten Daten aus der Sicht des ÜNB anzugeben
8264    fn evaluate_609(&self, _ctx: &EvaluationContext) -> ConditionResult {
8265        ConditionResult::True
8266    }
8267
8268    /// [611] Hinweis: Wenn das Unternehmen NB die Aufgaben der Marktrolle LF wahrnimmt, ist die MP-ID des Unternehmens NB in der Marktrolle LF anzugeben
8269    fn evaluate_611(&self, _ctx: &EvaluationContext) -> ConditionResult {
8270        ConditionResult::True
8271    }
8272
8273    /// [614] Hinweis: Es werden nur die OBIS Kennzahlen übermittelt die für die Bilanzierung relevant sind
8274    fn evaluate_614(&self, _ctx: &EvaluationContext) -> ConditionResult {
8275        ConditionResult::True
8276    }
8277
8278    /// [617] Hinweis: Mehrere Vorgänge für eine Marktlokation bei Wechsel bilanzierungsrelevanter Stammdaten innerhalb des Bilanzierungsmonats
8279    fn evaluate_617(&self, _ctx: &EvaluationContext) -> ConditionResult {
8280        ConditionResult::True
8281    }
8282
8283    /// [618] Hinweis: Bisheriges Datum des MSBA. Sollte der MSBN ein abweichendes Datum verwenden wollen, so teilt er das neue Datum mit einer Stammdatenänderung mit
8284    fn evaluate_618(&self, _ctx: &EvaluationContext) -> ConditionResult {
8285        ConditionResult::True
8286    }
8287
8288    /// [619] Hinweis: Für die selbständige Änderungsmöglichkeit der Schwellwertunter- / -über-schreitung
8289    fn evaluate_619(&self, _ctx: &EvaluationContext) -> ConditionResult {
8290        ConditionResult::True
8291    }
8292
8293    /// [621] Hinweis: Es ist der MSB anzugeben, welcher ab dem Zeitpunkt der Lokation zugeordnet ist, der in DTM+76 (Datum zum geplanten Leistungsbeginn) genannt ist.
8294    fn evaluate_621(&self, _ctx: &EvaluationContext) -> ConditionResult {
8295        ConditionResult::True
8296    }
8297
8298    /// [622] Hinweis: Falls die OBIS-Kennzahl für mehrere Marktrollen relevant ist, so muss die Segmentgruppe pro Marktrolle wiederholt werden
8299    fn evaluate_622(&self, _ctx: &EvaluationContext) -> ConditionResult {
8300        ConditionResult::True
8301    }
8302
8303    /// [623] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben, die zur Ermittlung der Energiemenge der im Vorgang genannten Marktlokation benötigt werden
8304    fn evaluate_623(&self, _ctx: &EvaluationContext) -> ConditionResult {
8305        ConditionResult::True
8306    }
8307
8308    /// [630] Hinweis: Wenn die Liste abgelehnt wird, ist kein Vorgang enthalten
8309    fn evaluate_630(&self, _ctx: &EvaluationContext) -> ConditionResult {
8310        ConditionResult::True
8311    }
8312
8313    /// [631] Hinweis: Es ist die Listennummer aus der Lieferanten- bzw. Bilanzierungsgebietsclearingliste zu verwenden
8314    fn evaluate_631(&self, _ctx: &EvaluationContext) -> ConditionResult {
8315        ConditionResult::True
8316    }
8317
8318    /// [632] Hinweis: Es ist die Listennummer aus der Lieferanten-ausfallarbeits-clearingliste zu verwenden
8319    fn evaluate_632(&self, _ctx: &EvaluationContext) -> ConditionResult {
8320        ConditionResult::True
8321    }
8322
8323    /// [637] Hinweis: Bei Verpflichtungsanfrage
8324    fn evaluate_637(&self, _ctx: &EvaluationContext) -> ConditionResult {
8325        ConditionResult::True
8326    }
8327
8328    /// [638] Hinweis: Bei Aufforderung zur Übernahme der einzelnen Messlokation durch den gMSB
8329    fn evaluate_638(&self, _ctx: &EvaluationContext) -> ConditionResult {
8330        ConditionResult::True
8331    }
8332
8333    /// [639] Hinweis: Wenn Antwort auf Deaktivierung
8334    fn evaluate_639(&self, _ctx: &EvaluationContext) -> ConditionResult {
8335        ConditionResult::True
8336    }
8337
8338    /// [640] Hinweis: Wenn Antwort auf Aktivierung
8339    fn evaluate_640(&self, _ctx: &EvaluationContext) -> ConditionResult {
8340        ConditionResult::True
8341    }
8342
8343    /// [641] Hinweis: Wenn Einzelanforderung vorliegt
8344    fn evaluate_641(&self, _ctx: &EvaluationContext) -> ConditionResult {
8345        ConditionResult::True
8346    }
8347
8348    /// [642] Hinweis: Wenn Abo vorliegt
8349    fn evaluate_642(&self, _ctx: &EvaluationContext) -> ConditionResult {
8350        ConditionResult::True
8351    }
8352
8353    /// [643] Hinweis: Nachfolgender Netzbetreiber
8354    fn evaluate_643(&self, _ctx: &EvaluationContext) -> ConditionResult {
8355        ConditionResult::True
8356    }
8357
8358    /// [645] Hinweis: Es ist der Bilanzkreis des LF anzugeben
8359    fn evaluate_645(&self, _ctx: &EvaluationContext) -> ConditionResult {
8360        ConditionResult::True
8361    }
8362
8363    /// [646] Hinweis: Es ist der RD-Bilanzkreis des ANB anzugeben
8364    fn evaluate_646(&self, _ctx: &EvaluationContext) -> ConditionResult {
8365        ConditionResult::True
8366    }
8367
8368    /// [647] Hinweis: Es ist der RD-Bilanzkreis des anfNB anzugeben
8369    fn evaluate_647(&self, _ctx: &EvaluationContext) -> ConditionResult {
8370        ConditionResult::True
8371    }
8372
8373    /// [648] Hinweis: Soll ein Produkt ab dem Datum \"Änderung zum\" nicht mehr vorhanden sein, wird dies durch nicht angeben des PIA+5 ausgedrückt
8374    fn evaluate_648(&self, _ctx: &EvaluationContext) -> ConditionResult {
8375        ConditionResult::True
8376    }
8377
8378    /// [651] Hinweis: Es sind die Marktlokationen anzugeben, zu welchen die hier genannte OBIS benötigt wird
8379    fn evaluate_651(&self, _ctx: &EvaluationContext) -> ConditionResult {
8380        ConditionResult::True
8381    }
8382
8383    /// [653] Hinweis: Es sind alle Netzlokationen, Marktlokationen, Tranchen und Messlokationen zu nennen, die durch die Bestätigung der Abmeldung der prozessual behandelten Messlokation nicht mehr dem MSB zug...
8384    fn evaluate_653(&self, _ctx: &EvaluationContext) -> ConditionResult {
8385        ConditionResult::True
8386    }
8387
8388    /// [655] Hinweis: Es ist die Listennummer aus der DZÜ Liste zu verwenden
8389    fn evaluate_655(&self, _ctx: &EvaluationContext) -> ConditionResult {
8390        ConditionResult::True
8391    }
8392
8393    /// [659] Hinweis: Bei Tranchen ist nur der Meldepunkt der Tranche anzugeben
8394    fn evaluate_659(&self, _ctx: &EvaluationContext) -> ConditionResult {
8395        ConditionResult::True
8396    }
8397
8398    /// [660] Hinweis: Es ist die ID der BK-Summenzeitreihe Aggregationsebene BG anzugeben
8399    fn evaluate_660(&self, _ctx: &EvaluationContext) -> ConditionResult {
8400        ConditionResult::True
8401    }
8402
8403    /// [662] Hinweis: Es sind die Daten NGZ und die Daten der NZR anzugeben
8404    fn evaluate_662(&self, _ctx: &EvaluationContext) -> ConditionResult {
8405        ConditionResult::True
8406    }
8407
8408    /// [663] Hinweis: Es ist die ID der Marktlokation und die ZPB des ZP der NGZ anzugeben
8409    fn evaluate_663(&self, _ctx: &EvaluationContext) -> ConditionResult {
8410        ConditionResult::True
8411    }
8412
8413    /// [664] Hinweis: Es ist das BG des NB (LPB) anzugeben
8414    fn evaluate_664(&self, _ctx: &EvaluationContext) -> ConditionResult {
8415        ConditionResult::True
8416    }
8417
8418    /// [665] Hinweis: Wenn ein Zähler mit einem SMGW parametriert ist werden an dem Zähler keine OBIS-Kennzahlen angegeben Hier gibt es nur OBIS Kennzahlen vom SMGW
8419    fn evaluate_665(&self, _ctx: &EvaluationContext) -> ConditionResult {
8420        ConditionResult::True
8421    }
8422
8423    /// [667] Hinweis: Für Zeiten die bilanziert wurden, aber inzwischen keine Aggregationsverantwortung beim ÜNB vorliegt, ist eine SG8 mit SG8 - SG10 CCI+6=ZA8 oder SG 10 CCI+6 = ZA9 CAV=ZG4 und SG8 - SG9 QT...
8424    fn evaluate_667(&self, _ctx: &EvaluationContext) -> ConditionResult {
8425        ConditionResult::True
8426    }
8427
8428    /// [668] Hinweis: Dieses Segment wird nach Abschluss der Einführung der Lokationsbündelstruktur zum 01.10.2025 aus der UTILMD entfernt
8429    fn evaluate_668(&self, _ctx: &EvaluationContext) -> ConditionResult {
8430        ConditionResult::True
8431    }
8432
8433    /// [670] Hinweis: Es sind alle Netzlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8434    fn evaluate_670(&self, _ctx: &EvaluationContext) -> ConditionResult {
8435        ConditionResult::True
8436    }
8437
8438    /// [671] Hinweis: Es sind alle Marktlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8439    fn evaluate_671(&self, _ctx: &EvaluationContext) -> ConditionResult {
8440        ConditionResult::True
8441    }
8442
8443    /// [672] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8444    fn evaluate_672(&self, _ctx: &EvaluationContext) -> ConditionResult {
8445        ConditionResult::True
8446    }
8447
8448    /// [673] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8449    fn evaluate_673(&self, _ctx: &EvaluationContext) -> ConditionResult {
8450        ConditionResult::True
8451    }
8452
8453    /// [674] Hinweis: Es sind alle Tranchen, zu nennen, die dem gleichen Lokationsbündel angehören
8454    fn evaluate_674(&self, _ctx: &EvaluationContext) -> ConditionResult {
8455        ConditionResult::True
8456    }
8457
8458    /// [675] Hinweis: Es sind alle Messlokationen, zu nennen, die Lokationsbündel angehören
8459    fn evaluate_675(&self, _ctx: &EvaluationContext) -> ConditionResult {
8460        ConditionResult::True
8461    }
8462
8463    /// [677] Hinweis: Es sind alle Netzlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8464    fn evaluate_677(&self, _ctx: &EvaluationContext) -> ConditionResult {
8465        ConditionResult::True
8466    }
8467
8468    /// [678] Hinweis: Es sind alle Marktlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8469    fn evaluate_678(&self, _ctx: &EvaluationContext) -> ConditionResult {
8470        ConditionResult::True
8471    }
8472
8473    /// [679] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8474    fn evaluate_679(&self, _ctx: &EvaluationContext) -> ConditionResult {
8475        ConditionResult::True
8476    }
8477
8478    /// [680] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8479    fn evaluate_680(&self, _ctx: &EvaluationContext) -> ConditionResult {
8480        ConditionResult::True
8481    }
8482
8483    /// [682] Hinweis: Es sind alle Messlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8484    fn evaluate_682(&self, _ctx: &EvaluationContext) -> ConditionResult {
8485        ConditionResult::True
8486    }
8487
8488    /// [683] Hinweis: Es sind alle ID der Netzlokationen, welche der im SG5 LOC+Z16 angegebenen Marktlokation aktuell und / oder zukünftig vorgelagert sind, anzugeben
8489    fn evaluate_683(&self, _ctx: &EvaluationContext) -> ConditionResult {
8490        ConditionResult::True
8491    }
8492
8493    /// [684] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16/ Z22 angegebenen Marktlokation aktuell und / oder zukünftig zugehören, anzugeben
8494    fn evaluate_684(&self, _ctx: &EvaluationContext) -> ConditionResult {
8495        ConditionResult::True
8496    }
8497
8498    /// [685] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen aktuell und / oder zukünftig zugeordnet sind, anzugeben.
8499    fn evaluate_685(&self, _ctx: &EvaluationContext) -> ConditionResult {
8500        ConditionResult::True
8501    }
8502
8503    /// [687] Hinweis: Es sind alle Messlokationen zu nennen, die für die Energiemengenermittlung aktuell und / oder zukünftig der im LOC+Z16/ Z22 angegebenen Marktlokation notwendig sind
8504    fn evaluate_687(&self, _ctx: &EvaluationContext) -> ConditionResult {
8505        ConditionResult::True
8506    }
8507
8508    /// [688] Hinweis: Es sind alle ID der Netzlokationen, welche der im LOC+Z16 angegebenen Marktlokation vorgelagert sind, anzugeben
8509    fn evaluate_688(&self, _ctx: &EvaluationContext) -> ConditionResult {
8510        ConditionResult::True
8511    }
8512
8513    /// [689] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16 angegebenen Marktlokation zugehören, anzugeben
8514    fn evaluate_689(&self, _ctx: &EvaluationContext) -> ConditionResult {
8515        ConditionResult::True
8516    }
8517
8518    /// [690] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen zugeordnet sind, anzugeben
8519    fn evaluate_690(&self, _ctx: &EvaluationContext) -> ConditionResult {
8520        ConditionResult::True
8521    }
8522
8523    /// [693] Hinweis: Es sind die Daten NGZ anzugeben
8524    fn evaluate_693(&self, _ctx: &EvaluationContext) -> ConditionResult {
8525        ConditionResult::True
8526    }
8527
8528    /// [694] Hinweis: Wenn in einer Marktlokation eine ID für eine Technischen Ressource vergeben wird, müssen für alle ggf. weitere Technische Ressourcen in der Marktlokation ID vergeben werden. Hintergrund...
8529    fn evaluate_694(&self, _ctx: &EvaluationContext) -> ConditionResult {
8530        ConditionResult::True
8531    }
8532
8533    /// [695] Hinweis: Verwendung, wenn Rolle LF einem Unternehmen NB zugeordnet ist
8534    fn evaluate_695(&self, _ctx: &EvaluationContext) -> ConditionResult {
8535        ConditionResult::True
8536    }
8537
8538    /// [696] Hinweis: Verwendung, nur wenn die Rolle LF nicht einem Unternehmen NB zugeordnet ist
8539    fn evaluate_696(&self, _ctx: &EvaluationContext) -> ConditionResult {
8540        ConditionResult::True
8541    }
8542
8543    /// [698] Hinweis: Für eine erzeugende Marktlokation muss für jede Technische Ressource eine ID der Technischen Ressourcen vergeben werden. Dies ist notwendig um die Nennleistung der Technische Ressource b...
8544    fn evaluate_698(&self, _ctx: &EvaluationContext) -> ConditionResult {
8545        ConditionResult::True
8546    }
8547
8548    /// [699] Hinweis: Es ist die ID der Steuerbare Ressource zu nennen über die die Technische Ressource gesteuert wird
8549    fn evaluate_699(&self, _ctx: &EvaluationContext) -> ConditionResult {
8550        ConditionResult::True
8551    }
8552
8553    /// [700] Hinweis: Es ist die ID der Netzlokation zu nennen über die die Technische Ressource gesteuert wird
8554    fn evaluate_700(&self, _ctx: &EvaluationContext) -> ConditionResult {
8555        ConditionResult::True
8556    }
8557
8558    /// [704] Hinweis: Segment ist zu verwenden, wenn es in Anfrage vorhanden war
8559    fn evaluate_704(&self, _ctx: &EvaluationContext) -> ConditionResult {
8560        ConditionResult::True
8561    }
8562
8563    /// [705] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Änderung\" durchgeführt wird
8564    fn evaluate_705(&self, _ctx: &EvaluationContext) -> ConditionResult {
8565        ConditionResult::True
8566    }
8567
8568    /// [706] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Bestellung einer Änderung von Stammdaten...\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8569    fn evaluate_706(&self, ctx: &EvaluationContext) -> ConditionResult {
8570        ctx.external.evaluate("data_clearing_required")
8571    }
8572
8573    /// [707] Hinweis: für weitere Details siehe Kapitel \"SG6 Verwendungszeitraum der Daten\"
8574    fn evaluate_707(&self, _ctx: &EvaluationContext) -> ConditionResult {
8575        ConditionResult::True
8576    }
8577
8578    /// [708] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des E/G zur Marktlokation\" durchgeführt wird
8579    fn evaluate_708(&self, _ctx: &EvaluationContext) -> ConditionResult {
8580        ConditionResult::True
8581    }
8582
8583    /// [709] Hinweis: Wenn die Aktion \"Zuordnung des E/G zur Marktlokation aufgrund fehlender Antwort\" durchgeführt wird
8584    fn evaluate_709(&self, _ctx: &EvaluationContext) -> ConditionResult {
8585        ConditionResult::True
8586    }
8587
8588    /// [710] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des LFN zur Marktlokation bzw. Tranche\" durchgeführt wird
8589    fn evaluate_710(&self, _ctx: &EvaluationContext) -> ConditionResult {
8590        ConditionResult::True
8591    }
8592
8593    /// [711] Hinweis: Wenn die Aktion \"Zuordnung des LFN zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8594    fn evaluate_711(&self, _ctx: &EvaluationContext) -> ConditionResult {
8595        ConditionResult::True
8596    }
8597
8598    /// [712] Hinweis: Wenn die Aktion \"Ankündigung der Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche\" durchgeführt wird
8599    fn evaluate_712(&self, _ctx: &EvaluationContext) -> ConditionResult {
8600        ConditionResult::True
8601    }
8602
8603    /// [713] Hinweis: Wenn die Aktion \"Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8604    fn evaluate_713(&self, _ctx: &EvaluationContext) -> ConditionResult {
8605        ConditionResult::True
8606    }
8607
8608    /// [714] Hinweis: Es ist bei einer unterjährigen Gebietsübernahme möglich hier den bisherigen NB zu nennen um aufzuzeigen, dass die im PIA+5 dieser SG8 genannten Gruppenartikel-ID bzw. Artikel-ID auf die...
8609    fn evaluate_714(&self, _ctx: &EvaluationContext) -> ConditionResult {
8610        ConditionResult::True
8611    }
8612
8613    /// [715] Hinweis: Dieser Transaktionsgrund darf für die betroffene Lokation nur angewendet werden wenn zuvor vom LF eine Anfrage Abr.-Daten BK-Abr. in die Vergangenheit erfolgte oder eine Korrektur zuvor m...
8614    fn evaluate_715(&self, _ctx: &EvaluationContext) -> ConditionResult {
8615        ConditionResult::True
8616    }
8617
8618    /// [716] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Abrechnungsdaten\" durchgeführt wird
8619    fn evaluate_716(&self, _ctx: &EvaluationContext) -> ConditionResult {
8620        ConditionResult::True
8621    }
8622
8623    /// [717] Hinweis: Wenn die Aktion des Sequenzdiagramms \"Bestellung einer Änderung von Abrechnungsdaten\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8624    fn evaluate_717(&self, ctx: &EvaluationContext) -> ConditionResult {
8625        ctx.external.evaluate("data_clearing_required")
8626    }
8627
8628    /// [718] Hinweis: Es sind alle Tranchen der im SG5 LOC+Z16 (Marktlokation) genannten Marktlokation anzugeben, die für die im SG6 (Verwendungszeitraum der Daten) genannten Zeiträume vorhanden sind
8629    fn evaluate_718(&self, _ctx: &EvaluationContext) -> ConditionResult {
8630        ConditionResult::True
8631    }
8632
8633    /// [719] Hinweis: Dieser Transaktionsgrund darf für die betroffene Lokation nur angewendet werden wenn der LF auf eine Anfrage mit diesem Transaktionsgrund antwortet oder eine Anfrage Abr.-Daten BK-Abr. in...
8634    fn evaluate_719(&self, _ctx: &EvaluationContext) -> ConditionResult {
8635        ConditionResult::True
8636    }
8637
8638    /// [902] Format: Möglicher Wert: ≥ 0
8639    fn evaluate_902(&self, _ctx: &EvaluationContext) -> ConditionResult {
8640        // Format condition — informational, always applies
8641        ConditionResult::True
8642    }
8643
8644    /// [910] Format: Möglicher Wert: < 0 oder ≥ 0
8645    fn evaluate_910(&self, _ctx: &EvaluationContext) -> ConditionResult {
8646        // Format condition — informational, always applies
8647        ConditionResult::True
8648    }
8649
8650    /// [914] Format: Möglicher Wert: > 0
8651    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
8652        ctx.format_check("SEQ", 1, 0, |val| validate_numeric(val, ">", 0.0))
8653    }
8654
8655    /// [922] Format: TR-ID
8656    fn evaluate_922(&self, _ctx: &EvaluationContext) -> ConditionResult {
8657        // Format condition — informational, always applies
8658        ConditionResult::True
8659    }
8660
8661    /// [926] Format: Möglicher Wert: 0
8662    fn evaluate_926(&self, _ctx: &EvaluationContext) -> ConditionResult {
8663        // Format condition — informational, always applies
8664        ConditionResult::True
8665    }
8666
8667    /// [930] Format: max. 2 Nachkommastellen
8668    fn evaluate_930(&self, _ctx: &EvaluationContext) -> ConditionResult {
8669        // Format condition — informational, always applies
8670        ConditionResult::True
8671    }
8672
8673    /// [931] Format: ZZZ = +00
8674    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
8675        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
8676    }
8677
8678    /// [932] Format: HHMM = 2200
8679    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
8680        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
8681    }
8682
8683    /// [933] Format: HHMM = 2300
8684    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
8685        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
8686    }
8687
8688    /// [937] Format: keine Nachkommastelle
8689    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
8690        ctx.format_check("SEQ", 1, 0, |val| validate_max_decimal_places(val, 0))
8691    }
8692
8693    /// [938] Format: Möglicher Wert: <= 10
8694    fn evaluate_938(&self, _ctx: &EvaluationContext) -> ConditionResult {
8695        ConditionResult::True
8696    }
8697
8698    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
8699    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
8700        ctx.format_check("COM", 0, 0, validate_email)
8701    }
8702
8703    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
8704    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
8705        ctx.format_check("COM", 0, 0, validate_phone)
8706    }
8707
8708    /// [942] Format: n1-n2-n1-n3
8709    fn evaluate_942(&self, _ctx: &EvaluationContext) -> ConditionResult {
8710        // Format condition — informational, always applies
8711        ConditionResult::True
8712    }
8713
8714    /// [943] Format: n1-n2-n1
8715    fn evaluate_943(&self, _ctx: &EvaluationContext) -> ConditionResult {
8716        // Format condition — informational, always applies
8717        ConditionResult::True
8718    }
8719
8720    /// [946] Format: max. 11 Nachkommastellen
8721    fn evaluate_946(&self, _ctx: &EvaluationContext) -> ConditionResult {
8722        // Format condition — informational, always applies
8723        ConditionResult::True
8724    }
8725
8726    /// [948] Format: n1-n2-n1-n8-n2
8727    fn evaluate_948(&self, _ctx: &EvaluationContext) -> ConditionResult {
8728        // Format condition — informational, always applies
8729        ConditionResult::True
8730    }
8731
8732    /// [950] Format: Marktlokations-ID
8733    fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
8734        // Format condition — informational, always applies
8735        ConditionResult::True
8736    }
8737
8738    /// [951] Format: Zählpunktbezeichnung
8739    fn evaluate_951(&self, _ctx: &EvaluationContext) -> ConditionResult {
8740        // Format condition — informational, always applies
8741        ConditionResult::True
8742    }
8743
8744    /// [952] Format: Gerätenummer nach DIN 43863-5
8745    fn evaluate_952(&self, _ctx: &EvaluationContext) -> ConditionResult {
8746        // Format condition — informational, always applies
8747        ConditionResult::True
8748    }
8749
8750    /// [955] Format: Möglicher Wert: < 100
8751    fn evaluate_955(&self, _ctx: &EvaluationContext) -> ConditionResult {
8752        // Format condition — informational, always applies
8753        ConditionResult::True
8754    }
8755
8756    /// [957] Format: n1-n2-n1-n8
8757    fn evaluate_957(&self, _ctx: &EvaluationContext) -> ConditionResult {
8758        // Format condition — informational, always applies
8759        ConditionResult::True
8760    }
8761
8762    /// [960] Format: Netzlokations-ID
8763    fn evaluate_960(&self, _ctx: &EvaluationContext) -> ConditionResult {
8764        // Format condition — informational, always applies
8765        ConditionResult::True
8766    }
8767
8768    /// [961] Format: SR-ID
8769    fn evaluate_961(&self, _ctx: &EvaluationContext) -> ConditionResult {
8770        // Format condition — informational, always applies
8771        ConditionResult::True
8772    }
8773
8774    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
8775    fn evaluate_967(&self, _ctx: &EvaluationContext) -> ConditionResult {
8776        // Format condition — informational, always applies
8777        ConditionResult::True
8778    }
8779
8780    /// [2001] Segmentgruppe ist mindestens zweimal je SG4 IDE+24 (Vorgang) anzugeben
8781    fn evaluate_2001(&self, ctx: &EvaluationContext) -> ConditionResult {
8782        // Cardinality: at least twice per SG4 IDE+24
8783        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8784        ConditionResult::from(ide_count > 0)
8785    }
8786
8787    /// [2002] Für jede Produktpaket-ID im SG8 SEQ+Z79 (Erforderliches Produkt) DE1050 genau einmal anzugeben
8788    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
8789        let seq_segs = ctx.find_segments("SEQ");
8790        let has_z79 = seq_segs.iter().any(|s| {
8791            s.elements
8792                .first()
8793                .and_then(|e| e.first())
8794                .is_some_and(|v| v == "Z79")
8795        });
8796        ConditionResult::from(has_z79)
8797    }
8798
8799    /// [2003] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) ab dem Zeitpunkt aus dem SG4 DTM+92 (Beginn zum) untergeordnet ist
8800    fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
8801        // TODO: implement
8802        ConditionResult::Unknown
8803    }
8804
8805    /// [2004] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8806    // REVIEW: Collects Zeitraum-IDs from SG6 RFF+Z49 DE1156 (elements[0][2]) by correlating qualifier (elements[0][0]) and ID (elements[0][2]) vectors via shared SG6 instance index. Then collects all SEQ DE1050 (elements[1][0]) references from SG8 instances. For each Zeitraum-ID from SG6 RFF+Z49, counts matching SG8 SEQ references; returns False if any count != 1 (violates 'exactly once' cardinality). Returns Unknown if no RFF+Z49 Zeitraum-IDs exist. (medium confidence)
8807    fn evaluate_2004(&self, ctx: &EvaluationContext) -> ConditionResult {
8808        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
8809        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
8810
8811        let zeitraum_ids: Vec<String> = rff_qualifiers
8812            .iter()
8813            .filter(|(_, qual)| qual.as_str() == "Z49")
8814            .filter_map(|(idx, _)| {
8815                rff_zeitraum_ids
8816                    .iter()
8817                    .find(|(i, v)| i == idx && !v.is_empty())
8818                    .map(|(_, v)| v.clone())
8819            })
8820            .collect();
8821
8822        if zeitraum_ids.is_empty() {
8823            return ConditionResult::Unknown;
8824        }
8825
8826        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
8827
8828        for zid in &zeitraum_ids {
8829            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
8830            if count != 1 {
8831                return ConditionResult::False;
8832            }
8833        }
8834
8835        ConditionResult::True
8836    }
8837
8838    /// [2005] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8839    // REVIEW: Same logic as 2004 but enforces 'at least once' cardinality (count >= 1) instead of 'exactly once'. Collects Zeitraum-IDs from SG6 RFF+Z49 DE1156 and checks that each has at least one matching SG8 SEQ DE1050 reference. Returns False if any Zeitraum-ID has zero matching SG8 references. (medium confidence)
8840    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
8841        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
8842        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
8843
8844        let zeitraum_ids: Vec<String> = rff_qualifiers
8845            .iter()
8846            .filter(|(_, qual)| qual.as_str() == "Z49")
8847            .filter_map(|(idx, _)| {
8848                rff_zeitraum_ids
8849                    .iter()
8850                    .find(|(i, v)| i == idx && !v.is_empty())
8851                    .map(|(_, v)| v.clone())
8852            })
8853            .collect();
8854
8855        if zeitraum_ids.is_empty() {
8856            return ConditionResult::Unknown;
8857        }
8858
8859        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
8860
8861        for zid in &zeitraum_ids {
8862            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
8863            if count == 0 {
8864                return ConditionResult::False;
8865            }
8866        }
8867
8868        ConditionResult::True
8869    }
8870
8871    /// [2010] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [266] ∧ [479] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID ...
8872    fn evaluate_2010(&self, _ctx: &EvaluationContext) -> ConditionResult {
8873        // TODO: implement
8874        ConditionResult::Unknown
8875    }
8876
8877    /// [2011] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 / Z53 (Verwendungszeitraum der Daten: "Gültige Daten" / "Keine Daten") aus der Anfragennachricht aus SG6 RFF+TN ...
8878    /// EXTERNAL: Requires context from outside the message.
8879    // REVIEW: Condition requires checking Zeitraum-IDs from DE1156 of SG6 RFF+Z49/Z53 ('Verwendungszeitraum der Daten: Gültige Daten / Keine Daten') that originated in the REQUEST message referenced by SG6 RFF+TN DE1154. The referenced request message is a separate EDIFACT interchange not present in the current message being validated. The cardinality constraint ('exactly once per Zeitraum-ID from the request') is therefore an external business context check that depends on data from a prior message in the business process. (medium confidence)
8880    fn evaluate_2011(&self, ctx: &EvaluationContext) -> ConditionResult {
8881        ctx.external
8882            .evaluate("request_message_zeitraum_ids_present")
8883    }
8884
8885    /// [2012] Segmentgruppe ist genau einmal für die Angabe der Informativen Daten der Marktlokation anzugeben
8886    fn evaluate_2012(&self, _ctx: &EvaluationContext) -> ConditionResult {
8887        // TODO: implement
8888        ConditionResult::Unknown
8889    }
8890
8891    /// [2013] Mindesten einmal anzugeben wenn kein SG6 RFF+Z54 (Verwendungszeitraum der Daten: Im System keine Daten vorhanden) vorhanden
8892    fn evaluate_2013(&self, ctx: &EvaluationContext) -> ConditionResult {
8893        // Check: condition applies when no RFF+Z54 exists
8894        ctx.lacks_qualifier("RFF", 0, "Z54")
8895    }
8896
8897    /// [2014] Mindesten einmal anzugeben wenn kein SG6 RFF+Z47 (Verwendungszeitraum der Daten: Im System vorhandene Daten) vorhanden
8898    fn evaluate_2014(&self, ctx: &EvaluationContext) -> ConditionResult {
8899        // Check: condition applies when no RFF+Z47 exists
8900        ctx.lacks_qualifier("RFF", 0, "Z47")
8901    }
8902
8903    /// [2015] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) untergeordnet ist
8904    fn evaluate_2015(&self, _ctx: &EvaluationContext) -> ConditionResult {
8905        // TODO: implement
8906        ConditionResult::Unknown
8907    }
8908
8909    /// [2016] Je SG5 LOC+Z16 (Marktlokation) ist genau einmal die Segmentgruppe anzugeben
8910    fn evaluate_2016(&self, ctx: &EvaluationContext) -> ConditionResult {
8911        // Cardinality: exactly once per SG5 LOC+Z16 (Marktlokation)
8912        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
8913        ConditionResult::from(loc_count > 0)
8914    }
8915
8916    /// [2017] Je SG5 LOC+Z17 (Messlokation) ist genau einmal die Segmentgruppe anzugeben
8917    fn evaluate_2017(&self, ctx: &EvaluationContext) -> ConditionResult {
8918        // Cardinality: exactly once per SG5 LOC+Z17 (Messlokation)
8919        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
8920        ConditionResult::from(loc_count > 0)
8921    }
8922
8923    /// [2018] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [266] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfol...
8924    fn evaluate_2018(&self, _ctx: &EvaluationContext) -> ConditionResult {
8925        // TODO: implement
8926        ConditionResult::Unknown
8927    }
8928
8929    /// [2060] Wenn SG10 CAV+NZR ist die Segmentgruppe genau zwei Mal je IDE+24 anzugeben
8930    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
8931        // Check: CAV+NZR exists (then segment group exactly twice per IDE+24)
8932        let found = ctx.find_segments("CAV").iter().any(|s| {
8933            s.elements
8934                .first()
8935                .and_then(|e| e.first())
8936                .is_some_and(|v| v == "NZR")
8937        });
8938        ConditionResult::from(found)
8939    }
8940
8941    /// [2061] Segment bzw. Segmentgruppe ist genau einmal je SG4 IDE (Vorgang) anzugeben
8942    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
8943        // Cardinality: exactly once per SG4 IDE (always true for single-transaction messages)
8944        let ide_count = ctx.find_segments("IDE").len();
8945        ConditionResult::from(ide_count > 0)
8946    }
8947
8948    /// [2071] Für die ID der LieferantensummenZR einmal je SG4 IDE+24 (Vorgang)
8949    fn evaluate_2071(&self, ctx: &EvaluationContext) -> ConditionResult {
8950        // Cardinality: LieferantensummenZR once per SG4 IDE+24
8951        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8952        ConditionResult::from(ide_count > 0)
8953    }
8954
8955    /// [2073] Für die ID der BilanzkreissummenZR einmal je SG4 IDE+24 (Vorgang)
8956    fn evaluate_2073(&self, ctx: &EvaluationContext) -> ConditionResult {
8957        // Cardinality: BilanzkreissummenZR once per SG4 IDE+24
8958        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8959        ConditionResult::from(ide_count > 0)
8960    }
8961
8962    /// [2075] Für die ID der Zeitreihen (nicht bei EEG-EUZ und EUZ der AAÜZ) in der Clearingliste einmal je SG4 IDE+24 (Vorgang)
8963    fn evaluate_2075(&self, ctx: &EvaluationContext) -> ConditionResult {
8964        // Cardinality: Zeitreihen (not EEG-EUZ/AAÜZ) once per SG4 IDE+24
8965        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8966        ConditionResult::from(ide_count > 0)
8967    }
8968
8969    /// [2080] Segmentgruppe ist max. zweimal je SG4 IDE+24 (Vorgang) anzugeben
8970    fn evaluate_2080(&self, ctx: &EvaluationContext) -> ConditionResult {
8971        // Cardinality: max twice per SG4 IDE+24
8972        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8973        ConditionResult::from(ide_count > 0)
8974    }
8975
8976    /// [2095] Je SG5 LOC+Z15 (MaBiS-Zählpunkt) ist genau einmal die Segmentgruppe anzugeben
8977    fn evaluate_2095(&self, ctx: &EvaluationContext) -> ConditionResult {
8978        // Cardinality: exactly once per SG5 LOC+Z15 (MaBiS-Zählpunkt)
8979        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z15").len();
8980        ConditionResult::from(loc_count > 0)
8981    }
8982
8983    /// [2096] Segmentgruppe ist genau zweimal je SG4 IDE anzugeben
8984    fn evaluate_2096(&self, ctx: &EvaluationContext) -> ConditionResult {
8985        // Cardinality: exactly twice per SG4 IDE
8986        let ide_count = ctx.find_segments("IDE").len();
8987        ConditionResult::from(ide_count > 0)
8988    }
8989
8990    /// [2119] Je SG8 SEQ+Z13/ ZG0 (Smartmeter-Gateway) ist genau einmal die Segmentgruppe anzugeben
8991    fn evaluate_2119(&self, ctx: &EvaluationContext) -> ConditionResult {
8992        // Cardinality: exactly once per SG8 SEQ+Z13/ZG0 (Smartmeter-Gateway)
8993        let found = ctx.find_segments("SEQ").iter().any(|s| {
8994            s.elements
8995                .first()
8996                .and_then(|e| e.first())
8997                .is_some_and(|v| v == "Z13" || v == "ZG0")
8998        });
8999        ConditionResult::from(found)
9000    }
9001
9002    /// [2140] Für die ID der LieferantensummenZR einmal je SG4 IDE+Z01 (Liste)
9003    fn evaluate_2140(&self, ctx: &EvaluationContext) -> ConditionResult {
9004        // Cardinality: LieferantensummenZR once per SG4 IDE+Z01
9005        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9006        ConditionResult::from(ide_count > 0)
9007    }
9008
9009    /// [2182] Segmentgruppe ist genau einmal je SG8 SEQ+Z01/ Z80/ Z81/ Z98 (Daten der Marktlokation/ Erwartete Daten der Marktlokation/ Im System vorhandene Daten der Marktlokation) anzugeben
9010    fn evaluate_2182(&self, ctx: &EvaluationContext) -> ConditionResult {
9011        // Cardinality: exactly once per SG8 SEQ+Z01/Z80/Z81/Z98
9012        let found = ctx.find_segments("SEQ").iter().any(|s| {
9013            s.elements
9014                .first()
9015                .and_then(|e| e.first())
9016                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9017        });
9018        ConditionResult::from(found)
9019    }
9020
9021    /// [2183] Segmentgruppe ist genau zweimal je SG8 SEQ+Z01/ Z80/ Z81/ Z98 (Daten der Marktlokation/ Erwartete Daten der Marktlokation/ Im System vorhandene Daten der Marktlokation) anzugeben
9022    fn evaluate_2183(&self, ctx: &EvaluationContext) -> ConditionResult {
9023        // Cardinality: exactly twice per SG8 SEQ+Z01/Z80/Z81/Z98
9024        let found = ctx.find_segments("SEQ").iter().any(|s| {
9025            s.elements
9026                .first()
9027                .and_then(|e| e.first())
9028                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9029        });
9030        ConditionResult::from(found)
9031    }
9032
9033    /// [2207] Für die ID der Lieferanten-ausfall-arbeits-summen-zeitreihe einmal je SG4 IDE+Z01 (Liste)
9034    fn evaluate_2207(&self, ctx: &EvaluationContext) -> ConditionResult {
9035        // Cardinality: Lieferantenausfallarbeitssummenzeitreihe once per SG4 IDE+Z01
9036        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9037        ConditionResult::from(ide_count > 0)
9038    }
9039
9040    /// [2225] Einmal für jede Marktlokation bzw. Tranche für die der LF nicht die gemeldete Ansicht des NB teilt
9041    fn evaluate_2225(&self, _ctx: &EvaluationContext) -> ConditionResult {
9042        // TODO: implement
9043        ConditionResult::Unknown
9044    }
9045
9046    /// [2236] Code einmal je SG4 IDE+24 (Vorgang)
9047    fn evaluate_2236(&self, ctx: &EvaluationContext) -> ConditionResult {
9048        // Cardinality: code once per SG4 IDE+24
9049        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9050        ConditionResult::from(ide_count > 0)
9051    }
9052
9053    /// [2252] Einmal für jede Marktlokation bzw. Tranche, die in der Lieferantenausfallarbeits-summenzeitreihe berücksichtigt wurde
9054    fn evaluate_2252(&self, _ctx: &EvaluationContext) -> ConditionResult {
9055        // TODO: implement
9056        ConditionResult::Unknown
9057    }
9058
9059    /// [2261] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9060    fn evaluate_2261(&self, ctx: &EvaluationContext) -> ConditionResult {
9061        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9062        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9063        ConditionResult::from(loc_count > 0)
9064    }
9065
9066    /// [2284] Für jede Messlokations-ID im SG5 LOC+Z17 (Messlokation) DE3225 genau einmal anzugeben
9067    fn evaluate_2284(&self, ctx: &EvaluationContext) -> ConditionResult {
9068        // Cardinality: exactly once per LOC+Z17 (Messlokation) ID
9069        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9070        ConditionResult::from(loc_count > 0)
9071    }
9072
9073    /// [2286] Für jede SEQ+ZF3 (Daten der Messlokation) mindestens einmal anzugeben
9074    fn evaluate_2286(&self, ctx: &EvaluationContext) -> ConditionResult {
9075        // Cardinality: at least once per SEQ+ZF3 (Daten der Messlokation)
9076        let found = !ctx.find_segments_with_qualifier("SEQ", 0, "ZF3").is_empty();
9077        ConditionResult::from(found)
9078    }
9079
9080    /// [2287] Für jede SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) mindestens einmal anzugeben
9081    fn evaluate_2287(&self, ctx: &EvaluationContext) -> ConditionResult {
9082        // Cardinality: at least once per SEQ+Z03/ZF5 (Zähleinrichtungsdaten)
9083        let found = ctx.find_segments("SEQ").iter().any(|s| {
9084            s.elements
9085                .first()
9086                .and_then(|e| e.first())
9087                .is_some_and(|v| v == "Z03" || v == "ZF5")
9088        });
9089        ConditionResult::from(found)
9090    }
9091
9092    /// [2288] Einmal für jede Zeitreihe, die in der DZR berücksichtigt wurde
9093    fn evaluate_2288(&self, _ctx: &EvaluationContext) -> ConditionResult {
9094        // TODO: implement
9095        ConditionResult::Unknown
9096    }
9097
9098    /// [2307] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225 genau einmal anzugeben
9099    fn evaluate_2307(&self, ctx: &EvaluationContext) -> ConditionResult {
9100        // Cardinality: exactly once per LOC+Z21 (Tranche) ID
9101        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9102        ConditionResult::from(loc_count > 0)
9103    }
9104
9105    /// [2308] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225, mindestens einmal anzugeben
9106    fn evaluate_2308(&self, ctx: &EvaluationContext) -> ConditionResult {
9107        // Cardinality: at least once per LOC+Z16 (Marktlokation) ID
9108        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9109        ConditionResult::from(loc_count > 0)
9110    }
9111
9112    /// [2309] Für jede ID im SG5 LOC+Z17 (Messlokation) DE3225 mindestens einmal anzugeben
9113    fn evaluate_2309(&self, ctx: &EvaluationContext) -> ConditionResult {
9114        // Cardinality: at least once per LOC+Z17 (Messlokation) ID
9115        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9116        ConditionResult::from(loc_count > 0)
9117    }
9118
9119    /// [2310] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225 genau einmal anzugeben
9120    fn evaluate_2310(&self, ctx: &EvaluationContext) -> ConditionResult {
9121        // Cardinality: exactly once per LOC+Z16 (Marktlokation) ID
9122        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9123        ConditionResult::from(loc_count > 0)
9124    }
9125
9126    /// [2311] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9127    fn evaluate_2311(&self, ctx: &EvaluationContext) -> ConditionResult {
9128        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9129        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9130        ConditionResult::from(loc_count > 0)
9131    }
9132
9133    /// [2312] Wenn der Objektcode \"9992000001256\" (Netzlokation) im DE1154 des selben RFF+Z33 nicht vorhanden ist, ist das RFF+Z33 in derselben SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des...
9134    fn evaluate_2312(&self, ctx: &EvaluationContext) -> ConditionResult {
9135        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9136        // Check: RFF+Z33 DE1154 does NOT contain Objektcode 9992000001256 (Netzlokation)
9137        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
9138        let has_netzlokation = rff_z33.iter().any(|s| {
9139            s.elements
9140                .first()
9141                .and_then(|e| e.get(1))
9142                .is_some_and(|v| v == "9992000001256")
9143        });
9144        ConditionResult::from(!has_netzlokation)
9145    }
9146
9147    /// [2313] Je SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels) genau einmal anzugeben
9148    fn evaluate_2313(&self, ctx: &EvaluationContext) -> ConditionResult {
9149        // Check: SG8 with SEQ+Z58/ZC9/ZD0/ZD6 exists (cardinality constraint)
9150        let found = ctx.find_segments("SEQ").iter().any(|s| {
9151            s.elements
9152                .first()
9153                .and_then(|e| e.first())
9154                .is_some_and(|v| ["Z58", "ZC9", "ZD0", "ZD6"].contains(&v.as_str()))
9155        });
9156        ConditionResult::from(found)
9157    }
9158
9159    /// [2317] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MIW/MPW/MUW vorhanden, ist das Segment mindestens zweimal anzugeben
9160    fn evaluate_2317(&self, ctx: &EvaluationContext) -> ConditionResult {
9161        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9162        // Check: SG10 CCI+++Z25 has CAV+MIW/MPW/MUW (Wandler types)
9163        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9164            s.elements
9165                .first()
9166                .and_then(|e| e.first())
9167                .is_some_and(|v| ["MIW", "MPW", "MUW"].contains(&v.as_str()))
9168        });
9169        ConditionResult::from(has_cav)
9170    }
9171
9172    /// [2318] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MBW (Blockstromwandler) vorhanden, ist das Segment genau einmal anzugeben
9173    fn evaluate_2318(&self, ctx: &EvaluationContext) -> ConditionResult {
9174        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9175        // Check: SG10 CCI+++Z25 has CAV+MBW (Blockstromwandler)
9176        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9177            s.elements
9178                .first()
9179                .and_then(|e| e.first())
9180                .is_some_and(|v| v == "MBW")
9181        });
9182        ConditionResult::from(has_cav)
9183    }
9184
9185    /// [2344] Einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
9186    fn evaluate_2344(&self, _ctx: &EvaluationContext) -> ConditionResult {
9187        // TODO: implement
9188        ConditionResult::Unknown
9189    }
9190
9191    /// [2350] Für jedes SMGW das im SEQ+Z13/ ZG0 (Smartmeter-Gateway) SG10 CCI+++Z75 CAV+Z30 (Gerätenummer) genannt ist, mindestens einmal je SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) das mit SG8 RFF+Z14 (Referenz...
9192    fn evaluate_2350(&self, _ctx: &EvaluationContext) -> ConditionResult {
9193        // TODO: implement
9194        ConditionResult::Unknown
9195    }
9196
9197    /// [2351] Einmal je ZP einer BK-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufgenommen wurde
9198    fn evaluate_2351(&self, _ctx: &EvaluationContext) -> ConditionResult {
9199        // TODO: implement
9200        ConditionResult::Unknown
9201    }
9202
9203    /// [2352] Einmal je ZP einer BG-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufge-nommen wurde
9204    fn evaluate_2352(&self, _ctx: &EvaluationContext) -> ConditionResult {
9205        // TODO: implement
9206        ConditionResult::Unknown
9207    }
9208
9209    /// [2356] Je SG5 LOC+Z18 (Netzlokation) ist genau einmal die Segmentgruppe anzugeben
9210    fn evaluate_2356(&self, ctx: &EvaluationContext) -> ConditionResult {
9211        // Cardinality: exactly once per SG5 LOC+Z18 (Netzlokation)
9212        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9213        ConditionResult::from(loc_count > 0)
9214    }
9215
9216    /// [2357] Je SG5 LOC+Z20 (Technische Ressource) ist genau einmal die Segmentgruppe anzugeben
9217    fn evaluate_2357(&self, ctx: &EvaluationContext) -> ConditionResult {
9218        // Cardinality: exactly once per SG5 LOC+Z20 (Technische Ressource)
9219        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z20").len();
9220        ConditionResult::from(loc_count > 0)
9221    }
9222
9223    /// [2358] Je SG5 LOC+Z19 (Steuerbare Ressource) ist genau einmal die Segmentgruppe anzugeben
9224    fn evaluate_2358(&self, ctx: &EvaluationContext) -> ConditionResult {
9225        // Cardinality: exactly once per SG5 LOC+Z19 (Steuerbare Ressource)
9226        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z19").len();
9227        ConditionResult::from(loc_count > 0)
9228    }
9229
9230    /// [2359] Für jede ID im SG5 LOC+Z16 / Z17 / Z20 (Marktlokation / Messlokation / Technische Ressource) DE3225 mindestens einmal anzugeben
9231    fn evaluate_2359(&self, ctx: &EvaluationContext) -> ConditionResult {
9232        // Cardinality: at least once per LOC+Z16/Z17/Z20 ID
9233        let found = ctx.find_segments("LOC").iter().any(|s| {
9234            s.elements
9235                .first()
9236                .and_then(|e| e.first())
9237                .is_some_and(|v| ["Z16", "Z17", "Z20"].contains(&v.as_str()))
9238        });
9239        ConditionResult::from(found)
9240    }
9241
9242    /// [2360] Für jede ID im SG5 LOC+Z18 (Netzlokation) DE3225 mindestens einmal anzugeben
9243    fn evaluate_2360(&self, ctx: &EvaluationContext) -> ConditionResult {
9244        // Cardinality: at least once per LOC+Z18 (Netzlokation) ID
9245        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9246        ConditionResult::from(loc_count > 0)
9247    }
9248
9249    /// [2361] Für jede ID im SG5 LOC+Z17 / Z20 / Z22 (Messlokation / Technische Ressource / ruhende Marktlokation) DE3225 mindestens einmal anzugeben
9250    fn evaluate_2361(&self, ctx: &EvaluationContext) -> ConditionResult {
9251        // Cardinality: at least once per LOC+Z17/Z20/Z22 ID
9252        let found = ctx.find_segments("LOC").iter().any(|s| {
9253            s.elements
9254                .first()
9255                .and_then(|e| e.first())
9256                .is_some_and(|v| ["Z17", "Z20", "Z22"].contains(&v.as_str()))
9257        });
9258        ConditionResult::from(found)
9259    }
9260}
9261
9262// HAND-EDITED: helpers for [223] / [265].
9263
9264use mig_types::segment::OwnedSegment;
9265
9266/// One SG8 repetition: its SEQ qualifier, the Marktlokation IDs of its
9267/// RFF+Z18, and its SG10s as `(CCIs, CAVs)`.
9268struct Sg8Block {
9269    seq: String,
9270    malo_refs: Vec<String>,
9271    sg10: Vec<(Vec<OwnedSegment>, Vec<OwnedSegment>)>,
9272}
9273
9274fn component(seg: &OwnedSegment, element: usize, comp: usize) -> Option<&str> {
9275    seg.elements
9276        .get(element)
9277        .and_then(|e| e.get(comp))
9278        .map(String::as_str)
9279}
9280
9281/// The message's SG8 repetitions, from the group navigator when there is one,
9282/// else from the segments in message order: SEQ opens an SG8, CCI opens an
9283/// SG10 within it that takes the CAVs after it. (`validate_pid_with_conditions`
9284/// evaluates BO4E input without a navigator.) `None` without either.
9285fn sg8_blocks(ctx: &EvaluationContext) -> Option<Vec<Sg8Block>> {
9286    let malo_refs = |rffs: &[OwnedSegment]| -> Vec<String> {
9287        rffs.iter()
9288            .filter(|s| component(s, 0, 0) == Some("Z18"))
9289            .filter_map(|s| component(s, 0, 1).map(str::to_string))
9290            .collect()
9291    };
9292    if let Some(nav) = ctx.navigator() {
9293        let sg8 = ["SG4", "SG8"];
9294        return Some(
9295            (0..nav.group_instance_count(&sg8))
9296                .map(|i| Sg8Block {
9297                    seq: nav
9298                        .find_segments_in_group("SEQ", &sg8, i)
9299                        .first()
9300                        .and_then(|s| component(s, 0, 0).map(str::to_string))
9301                        .unwrap_or_default(),
9302                    malo_refs: malo_refs(&nav.find_segments_in_group("RFF", &sg8, i)),
9303                    sg10: (0..nav.child_group_instance_count(&sg8, i, "SG10"))
9304                        .map(|j| {
9305                            (
9306                                nav.find_segments_in_child_group("CCI", &sg8, i, "SG10", j),
9307                                nav.find_segments_in_child_group("CAV", &sg8, i, "SG10", j),
9308                            )
9309                        })
9310                        .collect(),
9311                })
9312                .collect(),
9313        );
9314    }
9315    if ctx.segments.is_empty() {
9316        return None;
9317    }
9318    let mut blocks: Vec<Sg8Block> = Vec::new();
9319    let mut rffs: Vec<OwnedSegment> = Vec::new();
9320    for seg in ctx.segments {
9321        match seg.id.as_str() {
9322            "SEQ" => {
9323                if let Some(b) = blocks.last_mut() {
9324                    b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9325                }
9326                rffs.clear();
9327                blocks.push(Sg8Block {
9328                    seq: component(seg, 0, 0).unwrap_or_default().to_string(),
9329                    malo_refs: Vec::new(),
9330                    sg10: Vec::new(),
9331                });
9332            }
9333            // Segments after the SG8s end the last one.
9334            "NAD" | "UNT" | "IDE" => {
9335                if let Some(b) = blocks.last_mut() {
9336                    if b.malo_refs.is_empty() {
9337                        b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9338                    }
9339                }
9340                rffs.clear();
9341                if seg.id == "IDE" {
9342                    continue;
9343                }
9344                break;
9345            }
9346            "RFF" if !blocks.is_empty() => rffs.push(seg.clone()),
9347            "CCI" => {
9348                if let Some(b) = blocks.last_mut() {
9349                    b.sg10.push((vec![seg.clone()], Vec::new()));
9350                }
9351            }
9352            "CAV" => {
9353                if let Some((_, cavs)) = blocks.last_mut().and_then(|b| b.sg10.last_mut()) {
9354                    cavs.push(seg.clone());
9355                }
9356            }
9357            _ => {}
9358        }
9359    }
9360    if let Some(b) = blocks.last_mut() {
9361        if b.malo_refs.is_empty() {
9362            b.malo_refs = malo_refs(&rffs);
9363        }
9364    }
9365    Some(blocks)
9366}