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        let nav = match ctx.navigator {
2680            Some(n) => n,
2681            None => return ConditionResult::Unknown,
2682        };
2683        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2684        for i in 0..sg8_count {
2685            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2686            let mut has_za6_e14 = false;
2687            let mut has_za6_e02 = false;
2688            for j in 0..sg10_count {
2689                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2690                let has_cci_za6 = ccis.iter().any(|s| {
2691                    s.elements
2692                        .get(2)
2693                        .and_then(|e: &Vec<String>| e.first())
2694                        .is_some_and(|v: &String| v == "ZA6")
2695                });
2696                if !has_cci_za6 {
2697                    continue;
2698                }
2699                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2700                for cav in &cavs {
2701                    if let Some(code) = cav.elements.first().and_then(|e: &Vec<String>| e.first()) {
2702                        if code == "E14" {
2703                            has_za6_e14 = true;
2704                        }
2705                        if code == "E02" {
2706                            has_za6_e02 = true;
2707                        }
2708                    }
2709                }
2710            }
2711            if has_za6_e14 && has_za6_e02 {
2712                return ConditionResult::True;
2713            }
2714        }
2715        ConditionResult::False
2716    }
2717
2718    /// [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
2719    // 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)
2720    fn evaluate_91(&self, ctx: &EvaluationContext) -> ConditionResult {
2721        match self.evaluate_90(ctx) {
2722            ConditionResult::True => ConditionResult::False,
2723            ConditionResult::False => ConditionResult::True,
2724            ConditionResult::Unknown => ConditionResult::Unknown,
2725        }
2726    }
2727
2728    /// [92] Wenn Wert innerhalb SG bzw. Segment geändert wird
2729    fn evaluate_92(&self, _ctx: &EvaluationContext) -> ConditionResult {
2730        // TODO: implement
2731        ConditionResult::Unknown
2732    }
2733
2734    /// [93] Erlaubte Codes aus der PRICAT BGM+Z32 (Preisblatt Messstellenbetrieb) des verantwortlichen MSB
2735    fn evaluate_93(&self, _ctx: &EvaluationContext) -> ConditionResult {
2736        // TODO: implement
2737        ConditionResult::Unknown
2738    }
2739
2740    /// [94] Wenn ein Segment innerhalb der SG vorhanden
2741    fn evaluate_94(&self, _ctx: &EvaluationContext) -> ConditionResult {
2742        // Structural: segment exists within SG (always true for valid messages)
2743        ConditionResult::True
2744    }
2745
2746    /// [95] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) CAV+ZE5 (E-Mobilität) vorhanden
2747    fn evaluate_95(&self, ctx: &EvaluationContext) -> ConditionResult {
2748        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
2749        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
2750        let has_cci = cci_segs.iter().any(|s| {
2751            s.elements
2752                .first()
2753                .and_then(|e| e.first())
2754                .is_some_and(|v| v == "Z17")
2755        });
2756        let cav_segs = ctx.scoped_find_segments_in("SG10", "CAV");
2757        let has_cav = cav_segs.iter().any(|s| {
2758            s.elements
2759                .first()
2760                .and_then(|e| e.first())
2761                .is_some_and(|v| ["ZE5"].contains(&v.as_str()))
2762        });
2763        ConditionResult::from(has_cci && has_cav)
2764    }
2765
2766    /// [96] Wenn SG4 STS+7++xxx+ZAP (Transaktionsgrundergänzung ruhende Marktlokation) vorhanden
2767    fn evaluate_96(&self, ctx: &EvaluationContext) -> ConditionResult {
2768        let sts_segs = ctx.find_segments("STS");
2769        let found = sts_segs.iter().any(|s| {
2770            s.elements
2771                .first()
2772                .and_then(|e| e.first())
2773                .is_some_and(|v| v == "7")
2774                && s.elements
2775                    .get(3)
2776                    .and_then(|e| e.first())
2777                    .is_some_and(|v| ["ZAP"].contains(&v.as_str()))
2778        });
2779        ConditionResult::from(found)
2780    }
2781
2782    /// [97] Wenn in einem SG10 CAV+ZH9 DE7110 der Code der Produkteigenschaft (Wertebereich) 9991000002420 (Marktprämie) vorhanden ist
2783    fn evaluate_97(&self, ctx: &EvaluationContext) -> ConditionResult {
2784        // Check: CAV+ZH9 has code 9991000002420 (Marktprämie)
2785        let cav_segs = ctx.find_segments("CAV");
2786        let found = cav_segs.iter().any(|s| {
2787            s.elements
2788                .first()
2789                .and_then(|e| e.first())
2790                .is_some_and(|v| v == "ZH9")
2791                && s.elements
2792                    .first()
2793                    .and_then(|e| e.get(1))
2794                    .is_some_and(|v| v == "9991000002420")
2795        });
2796        ConditionResult::from(found)
2797    }
2798
2799    /// [98] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle NB
2800    fn evaluate_98(&self, ctx: &EvaluationContext) -> ConditionResult {
2801        ctx.external.evaluate("sender_role_check")
2802    }
2803
2804    /// [99] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle ÜNB
2805    fn evaluate_99(&self, ctx: &EvaluationContext) -> ConditionResult {
2806        ctx.external.evaluate("sender_role_check")
2807    }
2808
2809    /// [100] Wenn SG10 CAV+TLS/ TES/ BIT/ GET/ GAT/ SOT/ WNT/ WFT/ WAT vorhanden
2810    fn evaluate_100(&self, ctx: &EvaluationContext) -> ConditionResult {
2811        let cav_segs = ctx.find_segments("CAV");
2812        let found = cav_segs.iter().any(|s| {
2813            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2814                [
2815                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
2816                ]
2817                .contains(&v.as_str())
2818            })
2819        });
2820        ConditionResult::from(found)
2821    }
2822
2823    /// [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
2824    fn evaluate_101(&self, ctx: &EvaluationContext) -> ConditionResult {
2825        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2826        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
2827        let found = cci_segs.iter().any(|s| {
2828            s.elements
2829                .get(2)
2830                .and_then(|e| e.first())
2831                .is_some_and(|v| v == "ZA6")
2832        });
2833        ConditionResult::from(found)
2834    }
2835
2836    /// [102] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+Z36 (TEP mit Ref.messung) in dieser SG8 vorhanden
2837    fn evaluate_102(&self, ctx: &EvaluationContext) -> ConditionResult {
2838        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2839        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
2840        let found = cci_segs.iter().any(|s| {
2841            s.elements
2842                .get(2)
2843                .and_then(|e| e.first())
2844                .is_some_and(|v| v == "ZA6")
2845        });
2846        ConditionResult::from(found)
2847    }
2848
2849    /// [103] Wenn SG9 QTY+265 (Arbeit / Leistung für tagesparameterabhängig e Marktlokation: Veranschlagte Jahresmenge Gesamt) in dieser SG8 vorhanden
2850    fn evaluate_103(&self, ctx: &EvaluationContext) -> ConditionResult {
2851        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2852        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2853        let found = qty_segs.iter().any(|s| {
2854            s.elements
2855                .first()
2856                .and_then(|e| e.first())
2857                .is_some_and(|v| v == "265")
2858        });
2859        ConditionResult::from(found)
2860    }
2861
2862    /// [104] Wenn SG9 QTY+Z08 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: angepasste elektrische Arbeit nach Anhang D) in dieser SG8 vorhanden
2863    fn evaluate_104(&self, ctx: &EvaluationContext) -> ConditionResult {
2864        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2865        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2866        let found = qty_segs.iter().any(|s| {
2867            s.elements
2868                .first()
2869                .and_then(|e| e.first())
2870                .is_some_and(|v| v == "Z08")
2871        });
2872        ConditionResult::from(found)
2873    }
2874
2875    /// [105] Wenn SG9 QTY+Z10 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: Leistung der Marktlokation) in dieser SG8 vorhanden
2876    fn evaluate_105(&self, ctx: &EvaluationContext) -> ConditionResult {
2877        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2878        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2879        let found = qty_segs.iter().any(|s| {
2880            s.elements
2881                .first()
2882                .and_then(|e| e.first())
2883                .is_some_and(|v| v == "Z10")
2884        });
2885        ConditionResult::from(found)
2886    }
2887
2888    /// [106] Wenn in dieser SG8 SEQ+Z01 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
2889    fn evaluate_106(&self, ctx: &EvaluationContext) -> ConditionResult {
2890        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2891        // Check: in SG8 with SEQ+Z01, SG10 has CCI+++ZA6 and CAV+E02
2892        ctx.this_group_parent_child_has_qualifier(
2893            &["SG4", "SG8"],
2894            "SEQ",
2895            0,
2896            "Z01",
2897            "SG10",
2898            "CAV",
2899            0,
2900            "E02",
2901        )
2902    }
2903
2904    /// [107] Wenn in derselben SG8 das SG10 CAV+SLS/ SES/ BIP/ GEP/ GAP/ SOP/ WNP/ WFP/ WAP vorhanden
2905    fn evaluate_107(&self, ctx: &EvaluationContext) -> ConditionResult {
2906        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2907        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
2908        let found = cav_segs.iter().any(|s| {
2909            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2910                [
2911                    "SLS", "SES", "BIP", "GEP", "GAP", "SOP", "WNP", "WFP", "WAP",
2912                ]
2913                .contains(&v.as_str())
2914            })
2915        });
2916        ConditionResult::from(found)
2917    }
2918
2919    /// [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
2920    // HAND-EDITED — do not let generate-conditions regenerate this function
2921    // without first confirming prompt.rs B6 SCOPE RULES section is in the
2922    // system prompt. Regeneration without that guidance would emit
2923    // ctx.has_qualifier (message-wide) and silently reintroduce the
2924    // per-instance bug this function fixes.
2925    // See: docs/plans/2026-04-23-ahb-codelist-and-per-instance-plan.md B7.
2926    fn evaluate_108(&self, ctx: &EvaluationContext) -> ConditionResult {
2927        // [108] Wenn in derselben SG8 ein PIA+Z02 mit einem Code aus
2928        // 1-08-1/2/3/4/5 vorhanden. Scope-aware: narrows to the enclosing
2929        // SG8 instance when GroupScope is set; falls back to message-wide.
2930        const PREFIXES: &[&str] = &["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"];
2931        let found = ctx
2932            .scoped_find_segments_in("SG8", "PIA")
2933            .iter()
2934            .filter(|s| {
2935                s.elements
2936                    .first()
2937                    .and_then(|e| e.first())
2938                    .is_some_and(|q| q == "Z02")
2939            })
2940            .any(|s| {
2941                s.elements
2942                    .get(1)
2943                    .and_then(|e| e.first())
2944                    .is_some_and(|v| PREFIXES.iter().any(|p| v.starts_with(p)))
2945            });
2946        ConditionResult::from(found)
2947    }
2948
2949    /// [110] Wenn SG10 CAV+LGS/ EGS/ BIL/ GEL/ GAL/ SOL/ WNL/ WFL / WAL vorhanden
2950    fn evaluate_110(&self, ctx: &EvaluationContext) -> ConditionResult {
2951        let cav_segs = ctx.find_segments("CAV");
2952        let found = cav_segs.iter().any(|s| {
2953            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2954                [
2955                    "LGS", "EGS", "BIL", "GEL", "GAL", "SOL", "WNL", "WFL", "WAL",
2956                ]
2957                .contains(&v.as_str())
2958            })
2959        });
2960        ConditionResult::from(found)
2961    }
2962
2963    /// [111] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) nicht vorhanden
2964    fn evaluate_111(&self, ctx: &EvaluationContext) -> ConditionResult {
2965        ctx.any_group_has_qualifier_without("SEQ", 0, "Z59", "PIA", 0, "5", &["SG4", "SG8"])
2966    }
2967
2968    /// [112] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das SG10 CCI+11 (Details zum Produkt der Marktlokation) nicht vorhanden
2969    fn evaluate_112(&self, ctx: &EvaluationContext) -> ConditionResult {
2970        let result = ctx.filtered_parent_child_has_qualifier(
2971            &["SG4", "SG8"],
2972            "SEQ",
2973            0,
2974            "Z59",
2975            "SG10",
2976            "CCI",
2977            0,
2978            "11",
2979        );
2980        match result {
2981            ConditionResult::True => ConditionResult::False,
2982            ConditionResult::False => ConditionResult::True,
2983            ConditionResult::Unknown => ConditionResult::Unknown,
2984        }
2985    }
2986
2987    /// [113] Wenn es sich um eine Antwort auf die Bestellung einer Zählzeit handelt
2988    fn evaluate_113(&self, ctx: &EvaluationContext) -> ConditionResult {
2989        // Antwort auf Bestellung einer Zählzeit — depends on PID context
2990        ctx.external.evaluate("counting_time_order_response")
2991    }
2992
2993    /// [114] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E06 (Niederspannung) in dieser SG vorhanden
2994    // 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)
2995    fn evaluate_114(&self, ctx: &EvaluationContext) -> ConditionResult {
2996        let nav = match ctx.navigator {
2997            Some(n) => n,
2998            None => {
2999                return match (
3000                    ctx.has_qualifier("CCI", 0, "E03"),
3001                    ctx.has_qualifier("CAV", 0, "E06"),
3002                ) {
3003                    (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
3004                    (ConditionResult::False, _) | (_, ConditionResult::False) => {
3005                        ConditionResult::False
3006                    }
3007                    _ => ConditionResult::Unknown,
3008                };
3009            }
3010        };
3011        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3012        for i in 0..sg8_count {
3013            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3014            for j in 0..sg10_count {
3015                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3016                let has_cci_e03 = ccis.iter().any(|s| {
3017                    s.elements
3018                        .first()
3019                        .and_then(|e: &Vec<String>| e.first())
3020                        .is_some_and(|v: &String| v == "E03")
3021                });
3022                if has_cci_e03 {
3023                    let cavs =
3024                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3025                    if cavs.iter().any(|s| {
3026                        s.elements
3027                            .first()
3028                            .and_then(|e: &Vec<String>| e.first())
3029                            .is_some_and(|v: &String| v == "E06")
3030                    }) {
3031                        return ConditionResult::True;
3032                    }
3033                }
3034            }
3035        }
3036        ConditionResult::False
3037    }
3038
3039    /// [115] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E05 (Mittelspannung) in dieser SG vorhanden
3040    fn evaluate_115(&self, ctx: &EvaluationContext) -> ConditionResult {
3041        let cci_segs = ctx.find_segments("CCI");
3042        let has_cci = cci_segs.iter().any(|s| {
3043            s.elements
3044                .first()
3045                .and_then(|e| e.first())
3046                .is_some_and(|v| v == "E03")
3047        });
3048        let cav_segs = ctx.find_segments("CAV");
3049        let has_cav = cav_segs.iter().any(|s| {
3050            s.elements
3051                .first()
3052                .and_then(|e| e.first())
3053                .is_some_and(|v| v == "E05")
3054        });
3055        ConditionResult::from(has_cci && has_cav)
3056    }
3057
3058    /// [116] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E04 (Hochspannung) in dieser SG vorhanden
3059    fn evaluate_116(&self, ctx: &EvaluationContext) -> ConditionResult {
3060        let cci_segs = ctx.find_segments("CCI");
3061        let has_cci = cci_segs.iter().any(|s| {
3062            s.elements
3063                .first()
3064                .and_then(|e| e.first())
3065                .is_some_and(|v| v == "E03")
3066        });
3067        let cav_segs = ctx.find_segments("CAV");
3068        let has_cav = cav_segs.iter().any(|s| {
3069            s.elements
3070                .first()
3071                .and_then(|e| e.first())
3072                .is_some_and(|v| v == "E04")
3073        });
3074        ConditionResult::from(has_cci && has_cav)
3075    }
3076
3077    /// [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 (...
3078    // 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)
3079    fn evaluate_117(&self, ctx: &EvaluationContext) -> ConditionResult {
3080        let nav = match ctx.navigator {
3081            Some(n) => n,
3082            None => return ConditionResult::Unknown,
3083        };
3084        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3085        for i in 0..sg8_count {
3086            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3087            for j in 0..sg10_count {
3088                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3089                let has_cci_za6 = ccis.iter().any(|s| {
3090                    s.elements
3091                        .get(2)
3092                        .and_then(|e: &Vec<String>| e.first())
3093                        .is_some_and(|v: &String| v == "ZA6")
3094                });
3095                if has_cci_za6 {
3096                    let cavs =
3097                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3098                    if cavs.iter().any(|s| {
3099                        s.elements
3100                            .first()
3101                            .and_then(|e: &Vec<String>| e.first())
3102                            .is_some_and(|v: &String| v == "E14")
3103                    }) {
3104                        return ConditionResult::True;
3105                    }
3106                }
3107            }
3108        }
3109        ConditionResult::False
3110    }
3111
3112    /// [118] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/ SEP) vorhanden
3113    fn evaluate_118(&self, ctx: &EvaluationContext) -> ConditionResult {
3114        let has_cci = ctx.filtered_parent_child_has_qualifier(
3115            &["SG4", "SG8"],
3116            "SEQ",
3117            0,
3118            "Z98",
3119            "SG10",
3120            "CCI",
3121            2,
3122            "ZA6",
3123        );
3124        if has_cci != ConditionResult::True {
3125            return has_cci;
3126        }
3127        ctx.filtered_parent_child_has_qualifier(
3128            &["SG4", "SG8"],
3129            "SEQ",
3130            0,
3131            "Z98",
3132            "SG10",
3133            "CAV",
3134            0,
3135            "E02",
3136        )
3137    }
3138
3139    /// [119] Wenn in der SG8 SEQ+Z01/ Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
3140    fn evaluate_119(&self, ctx: &EvaluationContext) -> ConditionResult {
3141        // Check: in SG8 with SEQ+Z01 or Z98, SG10 has CCI+++ZA6 and CAV+E02
3142        let r1 = ctx.filtered_parent_child_has_qualifier(
3143            &["SG4", "SG8"],
3144            "SEQ",
3145            0,
3146            "Z01",
3147            "SG10",
3148            "CAV",
3149            0,
3150            "E02",
3151        );
3152        if r1 == ConditionResult::True {
3153            return ConditionResult::True;
3154        }
3155        ctx.filtered_parent_child_has_qualifier(
3156            &["SG4", "SG8"],
3157            "SEQ",
3158            0,
3159            "Z98",
3160            "SG10",
3161            "CAV",
3162            0,
3163            "E02",
3164        )
3165    }
3166
3167    /// [120] Wenn von NB Abtretungserklärung benötigt wird
3168    fn evaluate_120(&self, _ctx: &EvaluationContext) -> ConditionResult {
3169        // TODO: implement
3170        ConditionResult::Unknown
3171    }
3172
3173    /// [121] Wenn in dem SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) das SG8 RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden
3174    fn evaluate_121(&self, ctx: &EvaluationContext) -> ConditionResult {
3175        let z03 =
3176            ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"]);
3177        if z03 == ConditionResult::True {
3178            return ConditionResult::True;
3179        }
3180        ctx.any_group_has_qualifier_without("SEQ", 0, "ZF5", "RFF", 0, "Z14", &["SG4", "SG8"])
3181    }
3182
3183    /// [122] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++E03 (Spannungsebene der Marktlokation) CAV+E06 (Niederspannung) vorhanden
3184    // 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)
3185    fn evaluate_122(&self, ctx: &EvaluationContext) -> ConditionResult {
3186        // Check: in SG8 with SEQ+Z98, SG10 child has CCI+++E03 (Spannungsebene, elements[2][0]="E03")
3187        let has_cci_e03 = ctx.filtered_parent_child_has_qualifier(
3188            &["SG4", "SG8"],
3189            "SEQ",
3190            0,
3191            "Z98",
3192            "SG10",
3193            "CCI",
3194            2,
3195            "E03",
3196        );
3197        if has_cci_e03 != ConditionResult::True {
3198            return has_cci_e03;
3199        }
3200        // Also verify CAV+E06 (Niederspannung, elements[0][0]="E06") exists in the same SG8 scope
3201        ctx.any_group_has_qualifier("CAV", 0, "E06", &["SG4", "SG8"])
3202    }
3203
3204    /// [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...
3205    // 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)
3206    fn evaluate_123(&self, ctx: &EvaluationContext) -> ConditionResult {
3207        struct Sg8Z20Entry {
3208            mg_id: Option<String>,
3209        }
3210        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3211        let mut current: Option<Sg8Z20Entry> = None;
3212        for seg in ctx.segments {
3213            match seg.id.as_str() {
3214                "SEQ" => {
3215                    if let Some(entry) = current.take() {
3216                        entries.push(entry);
3217                    }
3218                    if seg
3219                        .elements
3220                        .first()
3221                        .and_then(|e: &Vec<String>| e.first())
3222                        .is_some_and(|v: &String| v == "Z20")
3223                    {
3224                        current = Some(Sg8Z20Entry { mg_id: None });
3225                    }
3226                }
3227                "RFF" if current.is_some() => {
3228                    if seg
3229                        .elements
3230                        .first()
3231                        .and_then(|e: &Vec<String>| e.first())
3232                        .is_some_and(|v: &String| v == "MG")
3233                    {
3234                        if let Some(ref mut entry) = current {
3235                            if entry.mg_id.is_none() {
3236                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3237                            }
3238                        }
3239                    }
3240                }
3241                _ => {}
3242            }
3243        }
3244        if let Some(entry) = current {
3245            entries.push(entry);
3246        }
3247        let ids: Vec<&str> = entries
3248            .iter()
3249            .filter_map(|e| e.mg_id.as_deref().filter(|s| !s.is_empty()))
3250            .collect();
3251        for i in 0..ids.len() {
3252            for j in (i + 1)..ids.len() {
3253                if ids[i] == ids[j] {
3254                    return ConditionResult::True;
3255                }
3256            }
3257        }
3258        ConditionResult::False
3259    }
3260
3261    /// [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...
3262    // 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)
3263    fn evaluate_124(&self, ctx: &EvaluationContext) -> ConditionResult {
3264        struct Sg8Z20Entry {
3265            zeitraum_id: String,
3266            mg_id: Option<String>,
3267        }
3268        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3269        let mut current: Option<Sg8Z20Entry> = None;
3270        for seg in ctx.segments {
3271            match seg.id.as_str() {
3272                "SEQ" => {
3273                    if let Some(entry) = current.take() {
3274                        entries.push(entry);
3275                    }
3276                    if seg
3277                        .elements
3278                        .first()
3279                        .and_then(|e: &Vec<String>| e.first())
3280                        .is_some_and(|v: &String| v == "Z20")
3281                    {
3282                        let zeitraum_id = seg
3283                            .elements
3284                            .get(1)
3285                            .and_then(|e: &Vec<String>| e.first())
3286                            .cloned()
3287                            .unwrap_or_default();
3288                        current = Some(Sg8Z20Entry {
3289                            zeitraum_id,
3290                            mg_id: None,
3291                        });
3292                    }
3293                }
3294                "RFF" if current.is_some() => {
3295                    if seg
3296                        .elements
3297                        .first()
3298                        .and_then(|e: &Vec<String>| e.first())
3299                        .is_some_and(|v: &String| v == "MG")
3300                    {
3301                        if let Some(ref mut entry) = current {
3302                            if entry.mg_id.is_none() {
3303                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3304                            }
3305                        }
3306                    }
3307                }
3308                _ => {}
3309            }
3310        }
3311        if let Some(entry) = current {
3312            entries.push(entry);
3313        }
3314        for i in 0..entries.len() {
3315            for j in (i + 1)..entries.len() {
3316                let ei = &entries[i];
3317                let ej = &entries[j];
3318                if !ei.zeitraum_id.is_empty()
3319                    && ei.zeitraum_id == ej.zeitraum_id
3320                    && ei.mg_id.is_some()
3321                    && !ei.mg_id.as_deref().unwrap_or("").is_empty()
3322                    && ei.mg_id == ej.mg_id
3323                {
3324                    return ConditionResult::True;
3325                }
3326            }
3327        }
3328        ConditionResult::False
3329    }
3330
3331    /// [125] Wenn in derselben SG8 SEQ+Z76/ ZC5/ ZC6 (Messstellenbetriebsabrechnungsdaten der Marktlokation) im DE4347 des PIA Z02 (Gruppenartikel-ID / Artikel-ID) vorhanden
3332    fn evaluate_125(&self, ctx: &EvaluationContext) -> ConditionResult {
3333        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3334        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
3335        let found = seq_segs.iter().any(|s| {
3336            s.elements
3337                .first()
3338                .and_then(|e| e.first())
3339                .is_some_and(|v| ["Z76", "ZC5", "ZC6"].contains(&v.as_str()))
3340        });
3341        ConditionResult::from(found)
3342    }
3343
3344    /// [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...
3345    fn evaluate_126(&self, ctx: &EvaluationContext) -> ConditionResult {
3346        // Check: RFF+Z49 or Z53 repetition count determines value
3347        // Approximate: check if RFF+Z49 or Z53 exists (counter is structural)
3348        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z49").len()
3349            + ctx.find_segments_with_qualifier("RFF", 0, "Z53").len();
3350        ConditionResult::from(count > 0)
3351    }
3352
3353    /// [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...
3354    fn evaluate_127(&self, ctx: &EvaluationContext) -> ConditionResult {
3355        // Check: RFF+Z48 or Z55 repetition count determines value
3356        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z48").len()
3357            + ctx.find_segments_with_qualifier("RFF", 0, "Z55").len();
3358        ConditionResult::from(count > 0)
3359    }
3360
3361    /// [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...
3362    fn evaluate_128(&self, ctx: &EvaluationContext) -> ConditionResult {
3363        // Check: RFF+Z47 or Z54 repetition count determines value
3364        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z47").len()
3365            + ctx.find_segments_with_qualifier("RFF", 0, "Z54").len();
3366        ConditionResult::from(count > 0)
3367    }
3368
3369    /// [129] Innerhalb eines SG4 IDE müssen alle DE3225 der SG5 LOC+Z16 (Marktlokation) den identischen Wert enthalten
3370    fn evaluate_129(&self, ctx: &EvaluationContext) -> ConditionResult {
3371        // Check: all LOC+Z16 DE3225 values are identical
3372        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z16");
3373        if loc_segs.len() <= 1 {
3374            return ConditionResult::True;
3375        }
3376        let first_val = loc_segs[0].elements.get(1).and_then(|e| e.first());
3377        let all_same = loc_segs
3378            .iter()
3379            .all(|s| s.elements.get(1).and_then(|e| e.first()) == first_val);
3380        ConditionResult::from(all_same)
3381    }
3382
3383    /// [130] Wenn an Messlokation vorhanden
3384    fn evaluate_130(&self, _ctx: &EvaluationContext) -> ConditionResult {
3385        // TODO: implement
3386        ConditionResult::Unknown
3387    }
3388
3389    /// [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...
3390    fn evaluate_131(&self, ctx: &EvaluationContext) -> ConditionResult {
3391        // Check: DTM+Z25 is in SG6 RFF with Zeitraum-ID "1" in DE1156
3392        let has_zeitraum_id_1 = ctx.find_segments("RFF").iter().any(|s| {
3393            s.elements
3394                .first()
3395                .and_then(|e| e.get(2))
3396                .is_some_and(|v| v == "1")
3397        });
3398        ConditionResult::from(has_zeitraum_id_1)
3399    }
3400
3401    /// [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...
3402    // 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)
3403    fn evaluate_132(&self, ctx: &EvaluationContext) -> ConditionResult {
3404        // Condition applies only when RFF in SG6 has Zeitraum-ID "1" in DE1156 (elements[0][2])
3405        let has_zeitraum_id_1 = ctx.find_segments("RFF").iter().any(|s| {
3406            s.elements
3407                .first()
3408                .and_then(|e| e.get(2))
3409                .is_some_and(|v: &String| v == "1")
3410        });
3411        if !has_zeitraum_id_1 {
3412            return ConditionResult::False;
3413        }
3414        // Get message date from DTM+137 (format 303: CCYYMMDDHHmm...)
3415        let msg_date_str = match ctx
3416            .find_segments_with_qualifier("DTM", 0, "137")
3417            .into_iter()
3418            .next()
3419        {
3420            Some(seg) => match seg.elements.first().and_then(|e| e.get(1)) {
3421                Some(v) if v.len() >= 8 => v.clone(),
3422                _ => return ConditionResult::Unknown,
3423            },
3424            None => return ConditionResult::Unknown,
3425        };
3426        // Get DTM+Z25 (Verwendung der Daten ab) value
3427        let usage_date_str = match ctx
3428            .find_segments_with_qualifier("DTM", 0, "Z25")
3429            .into_iter()
3430            .next()
3431        {
3432            Some(seg) => match seg.elements.first().and_then(|e| e.get(1)) {
3433                Some(v) if v.len() >= 12 => v.clone(),
3434                _ => return ConditionResult::Unknown,
3435            },
3436            None => return ConditionResult::Unknown,
3437        };
3438        // Parse YYYYMMDD from DTM+137 value
3439        let year: u32 = match msg_date_str[..4].parse() {
3440            Ok(v) => v,
3441            Err(_) => return ConditionResult::Unknown,
3442        };
3443        let month: u32 = match msg_date_str[4..6].parse() {
3444            Ok(v) => v,
3445            Err(_) => return ConditionResult::Unknown,
3446        };
3447        let day: u32 = match msg_date_str[6..8].parse() {
3448            Ok(v) => v,
3449            Err(_) => return ConditionResult::Unknown,
3450        };
3451        let days_in_month: u32 = match month {
3452            1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
3453            4 | 6 | 9 | 11 => 30,
3454            2 => {
3455                if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) {
3456                    29
3457                } else {
3458                    28
3459                }
3460            }
3461            _ => return ConditionResult::Unknown,
3462        };
3463        let (next_year, next_month, next_day): (u32, u32, u32) = if day >= days_in_month {
3464            if month == 12 {
3465                (year + 1, 1, 1)
3466            } else {
3467                (year, month + 1, 1)
3468            }
3469        } else {
3470            (year, month, day + 1)
3471        };
3472        // Expected DTM+Z25: next day at 00:00 — format 303 prefix YYYYMMDDHHMM = 12 chars
3473        let expected_prefix = format!("{:04}{:02}{:02}0000", next_year, next_month, next_day);
3474        let actual_len = usage_date_str.len().min(12);
3475        let actual_prefix = &usage_date_str[..actual_len];
3476        ConditionResult::from(actual_prefix == expected_prefix.as_str())
3477    }
3478
3479    /// [133] Wenn an der übermittelten Marktlokation / Messlokation vorhanden
3480    fn evaluate_133(&self, _ctx: &EvaluationContext) -> ConditionResult {
3481        // TODO: implement
3482        ConditionResult::Unknown
3483    }
3484
3485    /// [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 ...
3486    // 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)
3487    fn evaluate_134(&self, ctx: &EvaluationContext) -> ConditionResult {
3488        let nav = match ctx.navigator() {
3489            Some(n) => n,
3490            None => return ConditionResult::Unknown,
3491        };
3492        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
3493        // Collect (zeitraum_id, dtm_z25_value, dtm_z26_value) for every Z48/Z55 SG6
3494        let mut slices: Vec<(u32, Option<String>, Option<String>)> = Vec::new();
3495        for i in 0..sg6_count {
3496            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
3497            // RFF C506: [0][0]=qualifier, [0][1]=ref-id, [0][2]=Zeitraum-ID
3498            let maybe_zid = rffs.iter().find_map(|s| {
3499                let qual = s
3500                    .elements
3501                    .first()
3502                    .and_then(|e| e.first())
3503                    .map(|v| v.as_str())?;
3504                if qual == "Z48" || qual == "Z55" {
3505                    s.elements
3506                        .first()
3507                        .and_then(|e| e.get(2))
3508                        .and_then(|v| v.parse::<u32>().ok())
3509                } else {
3510                    None
3511                }
3512            });
3513            if let Some(zid) = maybe_zid {
3514                let dtms = nav.find_segments_in_group("DTM", &["SG4", "SG6"], i);
3515                let z25 = dtms.iter().find_map(|s| {
3516                    if s.elements
3517                        .first()
3518                        .and_then(|e| e.first())
3519                        .map(|v| v.as_str())
3520                        == Some("Z25")
3521                    {
3522                        s.elements.first().and_then(|e| e.get(1)).cloned()
3523                    } else {
3524                        None
3525                    }
3526                });
3527                let z26 = dtms.iter().find_map(|s| {
3528                    if s.elements
3529                        .first()
3530                        .and_then(|e| e.first())
3531                        .map(|v| v.as_str())
3532                        == Some("Z26")
3533                    {
3534                        s.elements.first().and_then(|e| e.get(1)).cloned()
3535                    } else {
3536                        None
3537                    }
3538                });
3539                slices.push((zid, z25, z26));
3540            }
3541        }
3542        if slices.is_empty() {
3543            return ConditionResult::Unknown;
3544        }
3545        // For each slice with Zeitraum-ID > 1: its DTM+Z25 must equal DTM+Z26 of the previous slice
3546        for i in 0..slices.len() {
3547            let (zid, ref z25, _) = slices[i];
3548            if zid <= 1 {
3549                continue;
3550            }
3551            let prev_id = zid - 1;
3552            match slices.iter().find(|(id, _, _)| *id == prev_id) {
3553                Some((_, _, prev_z26)) => match (z25, prev_z26) {
3554                    (Some(curr), Some(prev_end)) => {
3555                        if curr != prev_end {
3556                            return ConditionResult::False;
3557                        }
3558                    }
3559                    _ => return ConditionResult::Unknown,
3560                },
3561                None => return ConditionResult::Unknown,
3562            }
3563        }
3564        ConditionResult::True
3565    }
3566
3567    /// [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...
3568    // 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)
3569    fn evaluate_135(&self, ctx: &EvaluationContext) -> ConditionResult {
3570        let nav = match ctx.navigator() {
3571            Some(n) => n,
3572            None => return ConditionResult::Unknown,
3573        };
3574        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
3575        // Collect (zeitraum_id, dtm_z25_value, dtm_z26_value) for every Z47/Z54 SG6
3576        let mut slices: Vec<(u32, Option<String>, Option<String>)> = Vec::new();
3577        for i in 0..sg6_count {
3578            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
3579            // RFF C506: [0][0]=qualifier, [0][1]=ref-id, [0][2]=Zeitraum-ID
3580            let maybe_zid = rffs.iter().find_map(|s| {
3581                let qual = s
3582                    .elements
3583                    .first()
3584                    .and_then(|e| e.first())
3585                    .map(|v| v.as_str())?;
3586                if qual == "Z47" || qual == "Z54" {
3587                    s.elements
3588                        .first()
3589                        .and_then(|e| e.get(2))
3590                        .and_then(|v| v.parse::<u32>().ok())
3591                } else {
3592                    None
3593                }
3594            });
3595            if let Some(zid) = maybe_zid {
3596                let dtms = nav.find_segments_in_group("DTM", &["SG4", "SG6"], i);
3597                let z25 = dtms.iter().find_map(|s| {
3598                    if s.elements
3599                        .first()
3600                        .and_then(|e| e.first())
3601                        .map(|v| v.as_str())
3602                        == Some("Z25")
3603                    {
3604                        s.elements.first().and_then(|e| e.get(1)).cloned()
3605                    } else {
3606                        None
3607                    }
3608                });
3609                let z26 = dtms.iter().find_map(|s| {
3610                    if s.elements
3611                        .first()
3612                        .and_then(|e| e.first())
3613                        .map(|v| v.as_str())
3614                        == Some("Z26")
3615                    {
3616                        s.elements.first().and_then(|e| e.get(1)).cloned()
3617                    } else {
3618                        None
3619                    }
3620                });
3621                slices.push((zid, z25, z26));
3622            }
3623        }
3624        if slices.is_empty() {
3625            return ConditionResult::Unknown;
3626        }
3627        // For each slice with Zeitraum-ID > 1: its DTM+Z25 must equal DTM+Z26 of the previous slice
3628        for i in 0..slices.len() {
3629            let (zid, ref z25, _) = slices[i];
3630            if zid <= 1 {
3631                continue;
3632            }
3633            let prev_id = zid - 1;
3634            match slices.iter().find(|(id, _, _)| *id == prev_id) {
3635                Some((_, _, prev_z26)) => match (z25, prev_z26) {
3636                    (Some(curr), Some(prev_end)) => {
3637                        if curr != prev_end {
3638                            return ConditionResult::False;
3639                        }
3640                    }
3641                    _ => return ConditionResult::Unknown,
3642                },
3643                None => return ConditionResult::Unknown,
3644            }
3645        }
3646        ConditionResult::True
3647    }
3648
3649    /// [136] Wenn SG10 CCI+++E13 (Zählertyp) CAV+EHZ (elektronischer Haushaltszähler) vorhanden
3650    fn evaluate_136(&self, ctx: &EvaluationContext) -> ConditionResult {
3651        let cci_segs = ctx.find_segments("CCI");
3652        let found = cci_segs.iter().any(|s| {
3653            s.elements
3654                .get(2)
3655                .and_then(|e| e.first())
3656                .is_some_and(|v| v == "E13")
3657        });
3658        ConditionResult::from(found)
3659    }
3660
3661    /// [137] Nicht bei Neuanlage
3662    fn evaluate_137(&self, ctx: &EvaluationContext) -> ConditionResult {
3663        // Check: not a Neuanlage (new registration) process
3664        // STS+7 with transaktionsgrund containing Neuanlage indicators
3665        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
3666        let is_neuanlage = sts_segs.iter().any(|s| {
3667            s.elements
3668                .get(2)
3669                .and_then(|e| e.first())
3670                .is_some_and(|v| v == "E01" || v == "E03")
3671        });
3672        ConditionResult::from(!is_neuanlage)
3673    }
3674
3675    /// [138] Wenn im selben QTY im DE6063 Z35 (Abschlag) vorhanden
3676    fn evaluate_138(&self, ctx: &EvaluationContext) -> ConditionResult {
3677        let qty_segs = ctx.find_segments("QTY");
3678        let found = qty_segs.iter().any(|s| {
3679            s.elements
3680                .first()
3681                .and_then(|e| e.first())
3682                .is_some_and(|v| ["Z35"].contains(&v.as_str()))
3683        });
3684        ConditionResult::from(found)
3685    }
3686
3687    /// [139] Wenn SG10 CAV+IVA (Individuelle Abstimmung) nicht vorhanden
3688    // 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)
3689    fn evaluate_139(&self, ctx: &EvaluationContext) -> ConditionResult {
3690        let nav = match ctx.navigator {
3691            Some(n) => n,
3692            None => return ctx.lacks_qualifier("CAV", 0, "IVA"),
3693        };
3694        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3695        for i in 0..sg8_count {
3696            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3697            for j in 0..sg10_count {
3698                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3699                if cavs.iter().any(|s| {
3700                    s.elements
3701                        .first()
3702                        .and_then(|e: &Vec<String>| e.first())
3703                        .is_some_and(|v: &String| v == "IVA")
3704                }) {
3705                    return ConditionResult::False;
3706                }
3707            }
3708        }
3709        ConditionResult::True
3710    }
3711
3712    /// [140] Wenn im selben QTY im DE6063 Z34/Z35 (Zuschlag/Abschlag) vorhanden
3713    fn evaluate_140(&self, ctx: &EvaluationContext) -> ConditionResult {
3714        let qty_segs = ctx.find_segments("QTY");
3715        let found = qty_segs.iter().any(|s| {
3716            s.elements
3717                .first()
3718                .and_then(|e| e.first())
3719                .is_some_and(|v| ["Z34", "Z35"].contains(&v.as_str()))
3720        });
3721        ConditionResult::from(found)
3722    }
3723
3724    /// [141] Wenn SG10 CAV+MIW/ MPW/ MBW vorhanden
3725    fn evaluate_141(&self, ctx: &EvaluationContext) -> ConditionResult {
3726        let cav_segs = ctx.find_segments("CAV");
3727        let found = cav_segs.iter().any(|s| {
3728            s.elements
3729                .first()
3730                .and_then(|e| e.first())
3731                .is_some_and(|v| ["MIW", "MPW", "MBW"].contains(&v.as_str()))
3732        });
3733        ConditionResult::from(found)
3734    }
3735
3736    /// [142] Wenn SG8 PIA+Z01+:Z04/ Z05 (Berechnung Tagesmitteltemperatur: vom Anbieter zur Verfügung gestellte - / Äquivalente Tagesmitteltemperatur) vorhanden
3737    fn evaluate_142(&self, ctx: &EvaluationContext) -> ConditionResult {
3738        let pia_segs = ctx.find_segments("PIA");
3739        let found = pia_segs.iter().any(|s| {
3740            s.elements
3741                .first()
3742                .and_then(|e| e.first())
3743                .is_some_and(|v| v == "Z01")
3744                && s.elements
3745                    .get(1)
3746                    .and_then(|e| e.get(1))
3747                    .is_some_and(|v| ["Z04", "Z05"].contains(&v.as_str()))
3748        });
3749        ConditionResult::from(found)
3750    }
3751
3752    /// [143] Wenn STS+7++ZX3 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3753    fn evaluate_143(&self, ctx: &EvaluationContext) -> ConditionResult {
3754        let sts_segs = ctx.find_segments("STS");
3755        let found = sts_segs.iter().any(|s| {
3756            s.elements
3757                .first()
3758                .and_then(|e| e.first())
3759                .is_some_and(|v| v == "7")
3760                && s.elements
3761                    .get(2)
3762                    .and_then(|e| e.first())
3763                    .is_some_and(|v| v == "ZX3")
3764        });
3765        ConditionResult::from(found)
3766    }
3767
3768    /// [144] Wenn STS+7++ZAN (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3769    fn evaluate_144(&self, ctx: &EvaluationContext) -> ConditionResult {
3770        let sts_segs = ctx.find_segments("STS");
3771        let found = sts_segs.iter().any(|s| {
3772            s.elements
3773                .first()
3774                .and_then(|e| e.first())
3775                .is_some_and(|v| v == "7")
3776                && s.elements
3777                    .get(2)
3778                    .and_then(|e| e.first())
3779                    .is_some_and(|v| v == "ZAN")
3780        });
3781        ConditionResult::from(found)
3782    }
3783
3784    /// [145] Wenn STS+7++ZX2 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3785    fn evaluate_145(&self, ctx: &EvaluationContext) -> ConditionResult {
3786        let sts_segs = ctx.find_segments("STS");
3787        let found = sts_segs.iter().any(|s| {
3788            s.elements
3789                .first()
3790                .and_then(|e| e.first())
3791                .is_some_and(|v| v == "7")
3792                && s.elements
3793                    .get(2)
3794                    .and_then(|e| e.first())
3795                    .is_some_and(|v| v == "ZX2")
3796        });
3797        ConditionResult::from(found)
3798    }
3799
3800    /// [146] Wenn STS+7++ZA0 (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3801    fn evaluate_146(&self, ctx: &EvaluationContext) -> ConditionResult {
3802        let sts_segs = ctx.find_segments("STS");
3803        let found = sts_segs.iter().any(|s| {
3804            s.elements
3805                .first()
3806                .and_then(|e| e.first())
3807                .is_some_and(|v| v == "7")
3808                && s.elements
3809                    .get(2)
3810                    .and_then(|e| e.first())
3811                    .is_some_and(|v| v == "ZA0")
3812        });
3813        ConditionResult::from(found)
3814    }
3815
3816    /// [147] Wenn in Anfrage vorhanden
3817    fn evaluate_147(&self, _ctx: &EvaluationContext) -> ConditionResult {
3818        // TODO: implement
3819        ConditionResult::Unknown
3820    }
3821
3822    /// [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
3823    fn evaluate_148(&self, ctx: &EvaluationContext) -> ConditionResult {
3824        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3825        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
3826        let found = pia_segs.iter().any(|s| {
3827            s.elements
3828                .first()
3829                .and_then(|e| e.first())
3830                .is_some_and(|v| v == "Z02")
3831                && s.elements
3832                    .get(1)
3833                    .and_then(|e| e.first())
3834                    .is_some_and(|v| ["1-02-0-017", "1-02-0-018"].contains(&v.as_str()))
3835        });
3836        ConditionResult::from(found)
3837    }
3838
3839    /// [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...
3840    // 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)
3841    fn evaluate_149(&self, ctx: &EvaluationContext) -> ConditionResult {
3842        use std::collections::{HashMap, HashSet};
3843        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
3844        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
3845        let pia_quals = ctx.collect_group_values("PIA", 0, 0, &["SG4", "SG8"]);
3846        let pia_prods = ctx.collect_group_values("PIA", 1, 0, &["SG4", "SG8"]);
3847        // Map group index -> Zeitraum-ID for SG8s with SEQ+Z45
3848        let mut idx_to_zeitraum: HashMap<usize, String> = HashMap::new();
3849        for ((gi, q), (_, zid)) in seq_quals.iter().zip(seq_zeitraum.iter()) {
3850            if q == "Z45" && !zid.is_empty() {
3851                idx_to_zeitraum.insert(*gi, zid.clone());
3852            }
3853        }
3854        if idx_to_zeitraum.is_empty() {
3855            return ConditionResult::False;
3856        }
3857        // Map Zeitraum-ID -> set of PIA+Z02 product codes found in the same SG8 group
3858        let mut zeitraum_products: HashMap<String, HashSet<String>> = HashMap::new();
3859        for (i, (gi, qual)) in pia_quals.iter().enumerate() {
3860            if qual == "Z02" {
3861                if let Some(zid) = idx_to_zeitraum.get(gi) {
3862                    if let Some((_, prod)) = pia_prods.get(i) {
3863                        if !prod.is_empty() {
3864                            zeitraum_products
3865                                .entry(zid.clone())
3866                                .or_default()
3867                                .insert(prod.clone());
3868                        }
3869                    }
3870                }
3871            }
3872        }
3873        ConditionResult::from(
3874            zeitraum_products
3875                .values()
3876                .any(|codes| codes.contains("1-02-0-017") && codes.contains("1-02-0-018")),
3877        )
3878    }
3879
3880    /// [150] Wenn in dieser SG8 im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), die Artikel-ID 1-02-0-002 vorhanden
3881    fn evaluate_150(&self, ctx: &EvaluationContext) -> ConditionResult {
3882        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3883        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
3884        let found = pia_segs.iter().any(|s| {
3885            s.elements
3886                .first()
3887                .and_then(|e| e.first())
3888                .is_some_and(|v| v == "Z02")
3889                && s.elements
3890                    .get(1)
3891                    .and_then(|e| e.first())
3892                    .is_some_and(|v| ["1-02-0-002"].contains(&v.as_str()))
3893        });
3894        ConditionResult::from(found)
3895    }
3896
3897    /// [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...
3898    fn evaluate_151(&self, ctx: &EvaluationContext) -> ConditionResult {
3899        ctx.external.evaluate("code_list_membership_check")
3900    }
3901
3902    /// [152] Wenn FTX+Z28 (IP-Range des Absenders) nicht vorhanden
3903    fn evaluate_152(&self, ctx: &EvaluationContext) -> ConditionResult {
3904        let ftx_segs = ctx.find_segments("FTX");
3905        let found = ftx_segs.iter().any(|s| {
3906            s.elements
3907                .first()
3908                .and_then(|e| e.first())
3909                .is_some_and(|v| v == "Z28")
3910        });
3911        ConditionResult::from(!found)
3912    }
3913
3914    /// [153] Wenn FTX+Z27 (IP-Adresse des Absenders) nicht vorhanden
3915    fn evaluate_153(&self, ctx: &EvaluationContext) -> ConditionResult {
3916        let ftx_segs = ctx.find_segments("FTX");
3917        let found = ftx_segs.iter().any(|s| {
3918            s.elements
3919                .first()
3920                .and_then(|e| e.first())
3921                .is_some_and(|v| v == "Z27")
3922        });
3923        ConditionResult::from(!found)
3924    }
3925
3926    /// [156] Wenn SG4 FTX+Z17 (Zieladresse URI) vorhanden
3927    fn evaluate_156(&self, ctx: &EvaluationContext) -> ConditionResult {
3928        let ftx_segs = ctx.find_segments("FTX");
3929        let found = ftx_segs.iter().any(|s| {
3930            s.elements
3931                .first()
3932                .and_then(|e| e.first())
3933                .is_some_and(|v| v == "Z17")
3934        });
3935        ConditionResult::from(found)
3936    }
3937
3938    /// [161] Wenn SG4 STS+7++E03+ZW8 (Transaktionsgrundergänzung Fall 1) vorhanden
3939    fn evaluate_161(&self, ctx: &EvaluationContext) -> ConditionResult {
3940        let sts_segs = ctx.find_segments("STS");
3941        let found = sts_segs.iter().any(|s| {
3942            s.elements
3943                .first()
3944                .and_then(|e| e.first())
3945                .is_some_and(|v| v == "7")
3946                && s.elements
3947                    .get(2)
3948                    .and_then(|e| e.first())
3949                    .is_some_and(|v| v == "E03")
3950                && s.elements
3951                    .get(3)
3952                    .and_then(|e| e.first())
3953                    .is_some_and(|v| v == "ZW8")
3954        });
3955        ConditionResult::from(found)
3956    }
3957
3958    /// [162] Wenn SG4 STS+7++E03+ZW9 (Transaktionsgrundergänzung Fall 2) vorhanden
3959    fn evaluate_162(&self, ctx: &EvaluationContext) -> ConditionResult {
3960        let sts_segs = ctx.find_segments("STS");
3961        let found = sts_segs.iter().any(|s| {
3962            s.elements
3963                .first()
3964                .and_then(|e| e.first())
3965                .is_some_and(|v| v == "7")
3966                && s.elements
3967                    .get(2)
3968                    .and_then(|e| e.first())
3969                    .is_some_and(|v| v == "E03")
3970                && s.elements
3971                    .get(3)
3972                    .and_then(|e| e.first())
3973                    .is_some_and(|v| v == "ZW9")
3974        });
3975        ConditionResult::from(found)
3976    }
3977
3978    /// [163] Wenn SG4 STS+7++E03+ZX0 (Transaktionsgrundergänzung Fall 3) vorhanden
3979    fn evaluate_163(&self, ctx: &EvaluationContext) -> ConditionResult {
3980        let sts_segs = ctx.find_segments("STS");
3981        let found = sts_segs.iter().any(|s| {
3982            s.elements
3983                .first()
3984                .and_then(|e| e.first())
3985                .is_some_and(|v| v == "7")
3986                && s.elements
3987                    .get(2)
3988                    .and_then(|e| e.first())
3989                    .is_some_and(|v| v == "E03")
3990                && s.elements
3991                    .get(3)
3992                    .and_then(|e| e.first())
3993                    .is_some_and(|v| v == "ZX0")
3994        });
3995        ConditionResult::from(found)
3996    }
3997
3998    /// [164] Wenn SG4 STS+7++E03+ZX1 (Transaktionsgrundergänzung Fall 4) vorhanden
3999    fn evaluate_164(&self, ctx: &EvaluationContext) -> ConditionResult {
4000        let sts_segs = ctx.find_segments("STS");
4001        let found = sts_segs.iter().any(|s| {
4002            s.elements
4003                .first()
4004                .and_then(|e| e.first())
4005                .is_some_and(|v| v == "7")
4006                && s.elements
4007                    .get(2)
4008                    .and_then(|e| e.first())
4009                    .is_some_and(|v| v == "E03")
4010                && s.elements
4011                    .get(3)
4012                    .and_then(|e| e.first())
4013                    .is_some_and(|v| v == "ZX1")
4014        });
4015        ConditionResult::from(found)
4016    }
4017
4018    /// [165] Wenn bekannt
4019    fn evaluate_165(&self, _ctx: &EvaluationContext) -> ConditionResult {
4020        // TODO: implement
4021        ConditionResult::Unknown
4022    }
4023
4024    /// [166] Wenn vorhanden
4025    fn evaluate_166(&self, _ctx: &EvaluationContext) -> ConditionResult {
4026        // "If present" — always true when the field is being validated
4027        ConditionResult::True
4028    }
4029
4030    /// [167] Wenn SG5 LOC+Z21 (Tranche) vorhanden
4031    fn evaluate_167(&self, ctx: &EvaluationContext) -> ConditionResult {
4032        let loc_segs = ctx.find_segments("LOC");
4033        let found = loc_segs.iter().any(|s| {
4034            s.elements
4035                .first()
4036                .and_then(|e| e.first())
4037                .is_some_and(|v| v == "Z21")
4038        });
4039        ConditionResult::from(found)
4040    }
4041
4042    /// [170] Wenn Anschrift der Marktlokation vorhanden
4043    fn evaluate_170(&self, _ctx: &EvaluationContext) -> ConditionResult {
4044        // TODO: implement
4045        ConditionResult::Unknown
4046    }
4047
4048    /// [172] Wenn im selben QTY im DE6063 Z37 (Kein Zu- und Abschlag) vorhanden
4049    fn evaluate_172(&self, ctx: &EvaluationContext) -> ConditionResult {
4050        let qty_segs = ctx.find_segments("QTY");
4051        let found = qty_segs.iter().any(|s| {
4052            s.elements
4053                .first()
4054                .and_then(|e| e.first())
4055                .is_some_and(|v| ["Z37"].contains(&v.as_str()))
4056        });
4057        ConditionResult::from(found)
4058    }
4059
4060    /// [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 (...
4061    // 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)
4062    fn evaluate_173(&self, ctx: &EvaluationContext) -> ConditionResult {
4063        // HAND-EDITED: "in derselben SG8" — this SG8, a SEQ+Z58/ZC9/ZD0/ZD6, has an
4064        // RFF+Z33 without DE1156.
4065        let kinds = ctx.scoped_find_segments_in("SG8", "SEQ");
4066        if !kinds
4067            .iter()
4068            .any(|s| matches!(s.get_element(0), "Z58" | "ZC9" | "ZD0" | "ZD6"))
4069        {
4070            return ConditionResult::Unknown;
4071        }
4072        let z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
4073        if z33.is_empty() {
4074            return ConditionResult::Unknown;
4075        }
4076        ConditionResult::from(z33.iter().any(|s| s.get_component(0, 2).is_empty()))
4077    }
4078
4079    /// [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...
4080    fn evaluate_174(&self, ctx: &EvaluationContext) -> ConditionResult {
4081        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4082        // Check: RFF+Z33 has a value in DE1156 (fortlaufende Nummer)
4083        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
4084        let found = rff_z33.iter().any(|s| {
4085            s.elements
4086                .first()
4087                .and_then(|e| e.get(2))
4088                .is_some_and(|v| !v.is_empty())
4089        });
4090        ConditionResult::from(found)
4091    }
4092
4093    /// [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...
4094    // 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)
4095    fn evaluate_175(&self, ctx: &EvaluationContext) -> ConditionResult {
4096        let nav = match ctx.navigator {
4097            Some(n) => n,
4098            None => return ConditionResult::Unknown,
4099        };
4100        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4101        let mut entries: Vec<(usize, String, String)> = Vec::new();
4102        for i in 0..sg8_count {
4103            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4104            if !seqs.iter().any(|s| {
4105                s.elements
4106                    .first()
4107                    .and_then(|e: &Vec<String>| e.first())
4108                    .is_some_and(|v: &String| v == "Z58")
4109            }) {
4110                continue;
4111            }
4112            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4113            for rff in &rffs {
4114                let elems = match rff.elements.first() {
4115                    Some(e) => e,
4116                    None => continue,
4117                };
4118                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4119                    continue;
4120                }
4121                let objektcode = elems.get(1).cloned().unwrap_or_default();
4122                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4123                if !objektcode.is_empty() {
4124                    entries.push((i, objektcode, fort_nr));
4125                }
4126            }
4127        }
4128        for a in 0..entries.len() {
4129            for b in (a + 1)..entries.len() {
4130                if entries[a].0 != entries[b].0
4131                    && entries[a].1 == entries[b].1
4132                    && entries[a].2 == entries[b].2
4133                {
4134                    return ConditionResult::True;
4135                }
4136            }
4137        }
4138        ConditionResult::False
4139    }
4140
4141    /// [176] Wenn in demselben Segment das DE1153 mit dem Code Z34 vorgelagerte Messlokation vorhanden ist
4142    fn evaluate_176(&self, ctx: &EvaluationContext) -> ConditionResult {
4143        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z34"))
4144    }
4145
4146    /// [177] Wenn in demselben Segment das DE1153 mit dem Code Z35 vorgelagerte Netzlokation vorhanden ist
4147    fn evaluate_177(&self, ctx: &EvaluationContext) -> ConditionResult {
4148        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z35"))
4149    }
4150
4151    /// [178] Wenn das LOC+Z18 (Netzlokation) vorhanden ist
4152    fn evaluate_178(&self, ctx: &EvaluationContext) -> ConditionResult {
4153        let loc_segs = ctx.find_segments("LOC");
4154        let found = loc_segs.iter().any(|s| {
4155            s.elements
4156                .first()
4157                .and_then(|e| e.first())
4158                .is_some_and(|v| v == "Z18")
4159        });
4160        ConditionResult::from(found)
4161    }
4162
4163    /// [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...
4164    fn evaluate_179(&self, ctx: &EvaluationContext) -> ConditionResult {
4165        // HAND-EDITED: this SG8's Objektcode (RFF+Z33 DE1154) must be in a
4166        // sibling SG8 with the same SEQ code (Z58/ZC9/ZD0/ZD6). Unknown without
4167        // a group scope: "derselben SG8" cannot be told apart then.
4168        const KINDS: [&str; 4] = ["Z58", "ZC9", "ZD0", "ZD6"];
4169        let Some(siblings) = ctx.sibling_scopes("SG8") else {
4170            return ConditionResult::Unknown;
4171        };
4172        let seq_of = |segs: &[OwnedSegment]| {
4173            segs.iter()
4174                .find(|s| s.id == "SEQ")
4175                .map(|s| s.get_element(0).to_string())
4176        };
4177        let objektcodes = |segs: &[OwnedSegment]| -> Vec<String> {
4178            segs.iter()
4179                .filter(|s| s.id == "RFF" && s.get_element(0) == "Z33")
4180                .map(|s| s.get_component(0, 1).to_string())
4181                .filter(|v| !v.is_empty())
4182                .collect()
4183        };
4184        let own_seq = seq_of(&ctx.scoped_find_segments_in("SG8", "SEQ"));
4185        let Some(own_seq) = own_seq.filter(|q| KINDS.contains(&q.as_str())) else {
4186            return ConditionResult::False;
4187        };
4188        let own = objektcodes(&ctx.scoped_find_segments_in("SG8", "RFF"));
4189        if own.is_empty() {
4190            return ConditionResult::False;
4191        }
4192        let found = siblings.iter().any(|path| {
4193            seq_of(&ctx.find_segments_at(path, "SEQ")).as_deref() == Some(own_seq.as_str())
4194                && objektcodes(&ctx.find_segments_at(path, "RFF"))
4195                    .iter()
4196                    .any(|code| own.contains(code))
4197        });
4198        ConditionResult::from(found)
4199    }
4200
4201    /// [180] Wenn im selben QTY im DE6063 Z46 (Kein Abschlag) vorhanden
4202    fn evaluate_180(&self, ctx: &EvaluationContext) -> ConditionResult {
4203        let qty_segs = ctx.find_segments("QTY");
4204        let found = qty_segs.iter().any(|s| {
4205            s.elements
4206                .first()
4207                .and_then(|e| e.first())
4208                .is_some_and(|v| ["Z46"].contains(&v.as_str()))
4209        });
4210        ConditionResult::from(found)
4211    }
4212
4213    /// [184] Wenn in derselben SG8 SEQ+Z18 (Daten der Messlokation) ein CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
4214    fn evaluate_184(&self, ctx: &EvaluationContext) -> ConditionResult {
4215        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4216        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4217        let found = seq_segs.iter().any(|s| {
4218            s.elements
4219                .first()
4220                .and_then(|e| e.first())
4221                .is_some_and(|v| ["Z18"].contains(&v.as_str()))
4222        });
4223        ConditionResult::from(found)
4224    }
4225
4226    /// [190] Wenn in derselben SG8 SEQ+Z60/ ZG8/ ZG9/ ZE0  (Produkt-Daten der Netzlokation) PIA+5+9991000000721:Z12 vorhanden
4227    fn evaluate_190(&self, ctx: &EvaluationContext) -> ConditionResult {
4228        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4229        // Check: SG8 with SEQ+Z60/ZG8/ZG9/ZE0 has PIA+5 with code 9991000000721
4230        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4231        let found = pia_segs.iter().any(|s| {
4232            s.elements
4233                .first()
4234                .and_then(|e| e.first())
4235                .is_some_and(|v| v == "5")
4236                && s.elements
4237                    .get(1)
4238                    .and_then(|e| e.first())
4239                    .is_some_and(|v| v == "9991000000721")
4240        });
4241        ConditionResult::from(found)
4242    }
4243
4244    /// [191] Wenn im SG4 IDE+Z01 (Identifikation einer Liste) STS+E01 (Status der Antwort der Liste) nicht vorhanden
4245    fn evaluate_191(&self, ctx: &EvaluationContext) -> ConditionResult {
4246        let sts_segs = ctx.find_segments("STS");
4247        let found = sts_segs.iter().any(|s| {
4248            s.elements
4249                .first()
4250                .and_then(|e| e.first())
4251                .is_some_and(|v| v == "E01")
4252        });
4253        ConditionResult::from(!found)
4254    }
4255
4256    /// [192] Wenn SG4 STS+7++XXX+ZW4/ZAP (Transaktionsgrund / Ergänzung: Verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
4257    fn evaluate_192(&self, ctx: &EvaluationContext) -> ConditionResult {
4258        let sts_segs = ctx.find_segments("STS");
4259        let found = sts_segs.iter().any(|s| {
4260            s.elements
4261                .first()
4262                .and_then(|e| e.first())
4263                .is_some_and(|v| v == "7")
4264                && s.elements
4265                    .get(3)
4266                    .and_then(|e| e.first())
4267                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
4268        });
4269        ConditionResult::from(found)
4270    }
4271
4272    /// [193] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E06 (Niederspannung) in dieser SG vorhanden
4273    fn evaluate_193(&self, ctx: &EvaluationContext) -> ConditionResult {
4274        let nav = match ctx.navigator {
4275            Some(n) => n,
4276            None => {
4277                let cavs = ctx.find_segments("CAV");
4278                return ConditionResult::from(cavs.iter().any(|s| {
4279                    s.elements
4280                        .first()
4281                        .and_then(|e: &Vec<String>| e.first())
4282                        .is_some_and(|v: &String| v == "E06")
4283                }));
4284            }
4285        };
4286        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4287        for i in 0..sg8_count {
4288            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4289            for j in 0..sg10_count {
4290                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4291                let has_cci_zb5 = ccis.iter().any(|s| {
4292                    s.elements
4293                        .get(2)
4294                        .and_then(|e: &Vec<String>| e.first())
4295                        .is_some_and(|v: &String| v == "ZB5")
4296                });
4297                if !has_cci_zb5 {
4298                    continue;
4299                }
4300                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4301                if cavs.iter().any(|s| {
4302                    s.elements
4303                        .first()
4304                        .and_then(|e: &Vec<String>| e.first())
4305                        .is_some_and(|v: &String| v == "E06")
4306                }) {
4307                    return ConditionResult::True;
4308                }
4309            }
4310        }
4311        ConditionResult::False
4312    }
4313
4314    /// [194] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E05 (Mittelspannung) in dieser SG vorhanden
4315    fn evaluate_194(&self, ctx: &EvaluationContext) -> ConditionResult {
4316        let cci_segs = ctx.find_segments("CCI");
4317        let has_cci = cci_segs.iter().any(|s| {
4318            s.elements
4319                .iter()
4320                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4321        });
4322        let cav_segs = ctx.find_segments("CAV");
4323        let has_cav = cav_segs.iter().any(|s| {
4324            s.elements
4325                .first()
4326                .and_then(|e| e.first())
4327                .is_some_and(|v| v == "E05")
4328        });
4329        ConditionResult::from(has_cci && has_cav)
4330    }
4331
4332    /// [195] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E04 (Hochspannung) in dieser SG vorhanden
4333    fn evaluate_195(&self, ctx: &EvaluationContext) -> ConditionResult {
4334        let cci_segs = ctx.find_segments("CCI");
4335        let has_cci = cci_segs.iter().any(|s| {
4336            s.elements
4337                .iter()
4338                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4339        });
4340        let cav_segs = ctx.find_segments("CAV");
4341        let has_cav = cav_segs.iter().any(|s| {
4342            s.elements
4343                .first()
4344                .and_then(|e| e.first())
4345                .is_some_and(|v| v == "E04")
4346        });
4347        ConditionResult::from(has_cci && has_cav)
4348    }
4349
4350    /// [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...
4351    // 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)
4352    fn evaluate_196(&self, ctx: &EvaluationContext) -> ConditionResult {
4353        let nav = match ctx.navigator {
4354            Some(n) => n,
4355            None => return ConditionResult::Unknown,
4356        };
4357        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4358        let mut entries: Vec<(usize, String, String, String, String)> = Vec::new();
4359        for i in 0..sg8_count {
4360            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4361            let seq_opt = seqs.iter().find(|s| {
4362                s.elements
4363                    .first()
4364                    .and_then(|e: &Vec<String>| e.first())
4365                    .is_some_and(|v: &String| matches!(v.as_str(), "Z58" | "ZC9" | "ZD0"))
4366            });
4367            let seq = match seq_opt {
4368                Some(s) => s,
4369                None => continue,
4370            };
4371            let seq_code = seq
4372                .elements
4373                .first()
4374                .and_then(|e: &Vec<String>| e.first())
4375                .cloned()
4376                .unwrap_or_default();
4377            let zeitraum_id = seq
4378                .elements
4379                .get(1)
4380                .and_then(|e: &Vec<String>| e.first())
4381                .cloned()
4382                .unwrap_or_default();
4383            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4384            for rff in &rffs {
4385                let elems = match rff.elements.first() {
4386                    Some(e) => e,
4387                    None => continue,
4388                };
4389                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4390                    continue;
4391                }
4392                let objektcode = elems.get(1).cloned().unwrap_or_default();
4393                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4394                if !objektcode.is_empty() {
4395                    entries.push((
4396                        i,
4397                        seq_code.clone(),
4398                        zeitraum_id.clone(),
4399                        objektcode,
4400                        fort_nr,
4401                    ));
4402                }
4403            }
4404        }
4405        for a in 0..entries.len() {
4406            for b in (a + 1)..entries.len() {
4407                if entries[a].0 != entries[b].0
4408                    && entries[a].1 == entries[b].1
4409                    && entries[a].2 == entries[b].2
4410                    && entries[a].3 == entries[b].3
4411                    && entries[a].4 == entries[b].4
4412                {
4413                    return ConditionResult::True;
4414                }
4415            }
4416        }
4417        ConditionResult::False
4418    }
4419
4420    /// [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 (...
4421    // 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)
4422    fn evaluate_197(&self, ctx: &EvaluationContext) -> ConditionResult {
4423        let nav = match ctx.navigator {
4424            Some(n) => n,
4425            None => return ConditionResult::Unknown,
4426        };
4427        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4428        let mut qualifying_malo_ids: Vec<String> = Vec::new();
4429        for i in 0..sg8_count {
4430            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4431            if !seqs.iter().any(|s| {
4432                s.elements
4433                    .first()
4434                    .and_then(|e: &Vec<String>| e.first())
4435                    .is_some_and(|v: &String| v == "Z01")
4436            }) {
4437                continue;
4438            }
4439            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4440            let has_prog = (0..sg10_count).any(|j| {
4441                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4442                let has_za6 = ccis.iter().any(|s| {
4443                    s.elements
4444                        .get(2)
4445                        .and_then(|e: &Vec<String>| e.first())
4446                        .is_some_and(|v: &String| v == "ZA6")
4447                });
4448                if !has_za6 {
4449                    return false;
4450                }
4451                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4452                cavs.iter().any(|s| {
4453                    s.elements
4454                        .first()
4455                        .and_then(|e: &Vec<String>| e.first())
4456                        .is_some_and(|v: &String| v == "E02")
4457                })
4458            });
4459            if !has_prog {
4460                continue;
4461            }
4462            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4463            for rff in &rffs {
4464                let elems = match rff.elements.first() {
4465                    Some(e) => e,
4466                    None => continue,
4467                };
4468                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4469                    continue;
4470                }
4471                if let Some(malo_id) = elems.get(1) {
4472                    if !malo_id.is_empty() {
4473                        qualifying_malo_ids.push(malo_id.clone());
4474                    }
4475                }
4476            }
4477        }
4478        if qualifying_malo_ids.is_empty() {
4479            return ConditionResult::False;
4480        }
4481        for i in 0..sg8_count {
4482            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4483            for rff in &rffs {
4484                let elems = match rff.elements.first() {
4485                    Some(e) => e,
4486                    None => continue,
4487                };
4488                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4489                    continue;
4490                }
4491                if let Some(malo_id) = elems.get(1) {
4492                    if qualifying_malo_ids.contains(malo_id) {
4493                        return ConditionResult::True;
4494                    }
4495                }
4496            }
4497        }
4498        ConditionResult::False
4499    }
4500
4501    /// [198] Wenn SG4 STS+7++E03(Transaktionsgrund: Wechsel) vorhanden
4502    fn evaluate_198(&self, ctx: &EvaluationContext) -> ConditionResult {
4503        let sts_segs = ctx.find_segments("STS");
4504        let found = sts_segs.iter().any(|s| {
4505            s.elements
4506                .first()
4507                .and_then(|e| e.first())
4508                .is_some_and(|v| v == "7")
4509                && s.elements
4510                    .get(2)
4511                    .and_then(|e| e.first())
4512                    .is_some_and(|v| ["E03"].contains(&v.as_str()))
4513        });
4514        ConditionResult::from(found)
4515    }
4516
4517    /// [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
4518    // 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)
4519    fn evaluate_199(&self, ctx: &EvaluationContext) -> ConditionResult {
4520        let nav = match ctx.navigator {
4521            Some(n) => n,
4522            None => {
4523                return ctx.filtered_parent_child_has_qualifier(
4524                    &["SG4", "SG8"],
4525                    "SEQ",
4526                    0,
4527                    "Z01",
4528                    "SG10",
4529                    "CCI",
4530                    2,
4531                    "ZA6",
4532                )
4533            }
4534        };
4535        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4536        for i in 0..sg8_count {
4537            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4538            if !seqs.iter().any(|s| {
4539                s.elements
4540                    .first()
4541                    .and_then(|e: &Vec<String>| e.first())
4542                    .is_some_and(|v: &String| v == "Z01")
4543            }) {
4544                continue;
4545            }
4546            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4547            for j in 0..sg10_count {
4548                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4549                if !ccis.iter().any(|s| {
4550                    s.elements
4551                        .get(2)
4552                        .and_then(|e: &Vec<String>| e.first())
4553                        .is_some_and(|v: &String| v == "ZA6")
4554                }) {
4555                    continue;
4556                }
4557                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4558                if cavs.iter().any(|s| {
4559                    s.elements
4560                        .first()
4561                        .and_then(|e: &Vec<String>| e.first())
4562                        .is_some_and(|v: &String| v == "E02")
4563                }) {
4564                    return ConditionResult::True;
4565                }
4566            }
4567        }
4568        ConditionResult::False
4569    }
4570
4571    /// [201] Wenn die Marktlokation / Tranche nicht den ganzen Bilanzierungsmonat dem gleichen Tupel aus Bilanzkreis, Zeitreihentyp, Spannungsebene und Lieferant zugeordnet ist
4572    fn evaluate_201(&self, _ctx: &EvaluationContext) -> ConditionResult {
4573        // TODO: implement
4574        ConditionResult::Unknown
4575    }
4576
4577    /// [202] Wenn SG8 SEQ+Z78 RFF+Z39 (Keine standardisierte Lokationsbündelstruktur vorhanden), mit identischer Zeitraum-ID im DE1050 wie im DE3224 dieses Segments
4578    // 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)
4579    fn evaluate_202(&self, ctx: &EvaluationContext) -> ConditionResult {
4580        use std::collections::HashSet;
4581        // Collect Zeitraum-IDs from all SG5 LOC segments (DE3224 at elements[1][3])
4582        let loc_zeitraum_ids: HashSet<String> = ctx
4583            .find_segments("LOC")
4584            .into_iter()
4585            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
4586            .filter(|v| !v.is_empty())
4587            .collect();
4588        if loc_zeitraum_ids.is_empty() {
4589            return ConditionResult::False;
4590        }
4591        // Check if any SEQ+Z78 (SG8 Lokationsbündelstruktur) has DE1050 matching a LOC DE3224
4592        let matching_seq = ctx
4593            .find_segments_with_qualifier("SEQ", 0, "Z78")
4594            .into_iter()
4595            .any(|s| {
4596                s.elements
4597                    .get(1)
4598                    .and_then(|e: &Vec<String>| e.first())
4599                    .is_some_and(|v: &String| {
4600                        !v.is_empty() && loc_zeitraum_ids.contains(v.as_str())
4601                    })
4602            });
4603        if !matching_seq {
4604            return ConditionResult::False;
4605        }
4606        // Verify RFF+Z39 also exists (co-occurrence in same SG8 not verified without navigator)
4607        ctx.has_qualifier("RFF", 0, "Z39")
4608    }
4609
4610    /// [203] Wenn STS+7++E06 / Z39 / ZC6 / ZC7/ ZT6/ ZT7 vorhanden
4611    fn evaluate_203(&self, ctx: &EvaluationContext) -> ConditionResult {
4612        let sts_segs = ctx.find_segments("STS");
4613        let found = sts_segs.iter().any(|s| {
4614            s.elements
4615                .first()
4616                .and_then(|e| e.first())
4617                .is_some_and(|v| v == "7")
4618                && s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
4619                    ["E06", "Z39", "ZC6", "ZC7", "ZT6", "ZT7"].contains(&v.as_str())
4620                })
4621        });
4622        ConditionResult::from(found)
4623    }
4624
4625    /// [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...
4626    // 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)
4627    fn evaluate_204(&self, ctx: &EvaluationContext) -> ConditionResult {
4628        // True if any SG8 with Zähleinrichtungsdaten (SEQ+Z03/ZA3/ZA4) lacks RFF+Z14 (Smartmeter-Gateway)
4629        for qual in &["Z03", "ZA3", "ZA4"] {
4630            if matches!(
4631                ctx.any_group_has_qualifier_without(
4632                    "SEQ",
4633                    0,
4634                    qual,
4635                    "RFF",
4636                    0,
4637                    "Z14",
4638                    &["SG4", "SG8"]
4639                ),
4640                ConditionResult::True
4641            ) {
4642                return ConditionResult::True;
4643            }
4644        }
4645        ConditionResult::False
4646    }
4647
4648    /// [205] Wenn SG5 LOC+Z19 (Steuerbare Ressource) vorhanden
4649    fn evaluate_205(&self, ctx: &EvaluationContext) -> ConditionResult {
4650        let loc_segs = ctx.find_segments("LOC");
4651        let found = loc_segs.iter().any(|s| {
4652            s.elements
4653                .first()
4654                .and_then(|e| e.first())
4655                .is_some_and(|v| v == "Z19")
4656        });
4657        ConditionResult::from(found)
4658    }
4659
4660    /// [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
4661    fn evaluate_206(&self, ctx: &EvaluationContext) -> ConditionResult {
4662        let sts_segs = ctx.find_segments("STS");
4663        let found = sts_segs.iter().any(|s| {
4664            s.elements
4665                .first()
4666                .and_then(|e| e.first())
4667                .is_some_and(|v| v == "7")
4668                && s.elements
4669                    .get(2)
4670                    .and_then(|e| e.first())
4671                    .is_some_and(|v| ["ZG5"].contains(&v.as_str()))
4672        });
4673        ConditionResult::from(!found)
4674    }
4675
4676    /// [209] Wenn im selben Segment im DE2379 der Code 303 vorhanden ist
4677    fn evaluate_209(&self, ctx: &EvaluationContext) -> ConditionResult {
4678        ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "303"))
4679    }
4680
4681    /// [210] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
4682    fn evaluate_210(&self, ctx: &EvaluationContext) -> ConditionResult {
4683        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4684        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4685        let found = cci_segs.iter().any(|s| {
4686            s.elements
4687                .get(2)
4688                .and_then(|e| e.first())
4689                .is_some_and(|v| v == "ZA6")
4690        });
4691        ConditionResult::from(found)
4692    }
4693
4694    /// [212] Wenn im selben SG12 NAD DE3124 nicht vorhanden
4695    fn evaluate_212(&self, ctx: &EvaluationContext) -> ConditionResult {
4696        let nad_segs = ctx.find_segments("NAD");
4697        let found = nad_segs.iter().any(|s| {
4698            s.elements
4699                .get(3)
4700                .and_then(|e| e.first())
4701                .is_some_and(|v| !v.is_empty())
4702        });
4703        ConditionResult::from(!found)
4704    }
4705
4706    /// [213] Wenn SG12 NAD+Z09 (Kunde des Lieferanten) vorhanden
4707    fn evaluate_213(&self, ctx: &EvaluationContext) -> ConditionResult {
4708        let nad_segs = ctx.find_segments("NAD");
4709        let found = nad_segs.iter().any(|s| {
4710            s.elements
4711                .first()
4712                .and_then(|e| e.first())
4713                .is_some_and(|v| v == "Z09")
4714        });
4715        ConditionResult::from(found)
4716    }
4717
4718    /// [215] Wenn in derselben SG8 (Zähleinrichtungsdaten) SG10 CCI+++E13 CAV+MME (Zählertyp: mME) vorhanden
4719    fn evaluate_215(&self, ctx: &EvaluationContext) -> ConditionResult {
4720        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8 (its
4721        // SG10s included). Both segments are looked for anywhere in that SG8,
4722        // not within one SG10.
4723        let has_cci_e13 = ctx.scoped_find_segments_in("SG8", "CCI").iter().any(|s| {
4724            s.elements
4725                .get(2)
4726                .and_then(|e| e.first())
4727                .is_some_and(|v| v == "E13")
4728        });
4729        let has_cav_mme = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
4730            s.elements
4731                .first()
4732                .and_then(|e| e.first())
4733                .is_some_and(|v| v == "MME")
4734        });
4735        ConditionResult::from(has_cci_e13 && has_cav_mme)
4736    }
4737
4738    /// [216] Wenn CCI+++Z88 (Netznutzung) CAV+Z74:::Z08 (Netznutzungsvertrag: Direkter Vertrag zwischen Kunden und NB) vorhanden
4739    fn evaluate_216(&self, ctx: &EvaluationContext) -> ConditionResult {
4740        let cci_segs = ctx.find_segments("CCI");
4741        let found = cci_segs.iter().any(|s| {
4742            s.elements
4743                .get(2)
4744                .and_then(|e| e.first())
4745                .is_some_and(|v| v == "Z88")
4746        });
4747        ConditionResult::from(found)
4748    }
4749
4750    /// [217] Wenn SG6 RFF+Z49 (Verwendungszeitraum der Daten: \"Gültige Daten\") vorhanden
4751    fn evaluate_217(&self, ctx: &EvaluationContext) -> ConditionResult {
4752        let rff_segs = ctx.find_segments("RFF");
4753        let found = rff_segs.iter().any(|s| {
4754            s.elements
4755                .first()
4756                .and_then(|e| e.first())
4757                .is_some_and(|v| v == "Z49")
4758        });
4759        ConditionResult::from(found)
4760    }
4761
4762    /// [219] Wenn SG12 NAD+Z65 (Informativer Kunde des Lieferanten) vorhanden
4763    fn evaluate_219(&self, ctx: &EvaluationContext) -> ConditionResult {
4764        let nad_segs = ctx.find_segments("NAD");
4765        let found = nad_segs.iter().any(|s| {
4766            s.elements
4767                .first()
4768                .and_then(|e| e.first())
4769                .is_some_and(|v| v == "Z65")
4770        });
4771        ConditionResult::from(found)
4772    }
4773
4774    /// [220] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) in dieser SG8 vorhanden
4775    fn evaluate_220(&self, ctx: &EvaluationContext) -> ConditionResult {
4776        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4777        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4778        let has_seq = seq_segs.iter().any(|s| {
4779            s.elements
4780                .first()
4781                .and_then(|e| e.first())
4782                .is_some_and(|v| ["Z01", "Z98"].contains(&v.as_str()))
4783        });
4784        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4785        let has_cci = cci_segs.iter().any(|s| {
4786            s.elements
4787                .first()
4788                .and_then(|e| e.first())
4789                .is_some_and(|v| v == "Z30")
4790                && s.elements
4791                    .get(2)
4792                    .and_then(|e| e.first())
4793                    .is_some_and(|v| v == "Z06")
4794        });
4795        ConditionResult::from(has_seq && has_cci)
4796    }
4797
4798    /// [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)
4799    fn evaluate_221(&self, ctx: &EvaluationContext) -> ConditionResult {
4800        // Check: no SG8 with SEQ+Z15 has RFF+Z18 referencing same Marktlokation
4801        let has_seq_z15 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z15").is_empty();
4802        if !has_seq_z15 {
4803            return ConditionResult::True;
4804        }
4805        // Approximate: SEQ+Z15 exists, check if it references via RFF+Z18
4806        ctx.any_group_has_qualifier_without("SEQ", 0, "Z15", "RFF", 0, "Z18", &["SG4", "SG8"])
4807    }
4808
4809    /// [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...
4810    // HAND-EDITED (mako-twin, 55043): the generated version only checked that
4811    // some RFF+Z18 names some LOC+Z16 and dropped the Erzeugung clause, so it
4812    // held for every message with a Marktlokation. The scope never narrows to
4813    // one SG8 (only top-level groups are scoped), so "this SG8" is any SG8
4814    // other than SEQ+Z01, as in the neighbouring conditions.
4815    fn evaluate_223(&self, ctx: &EvaluationContext) -> ConditionResult {
4816        let Some(blocks) = sg8_blocks(ctx) else {
4817            return ConditionResult::Unknown;
4818        };
4819        let loc_ids: Vec<String> = ctx
4820            .find_segments_with_qualifier("LOC", 0, "Z16")
4821            .iter()
4822            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
4823            .collect();
4824        let malo_refs = |b: &Sg8Block| -> Vec<String> {
4825            b.malo_refs
4826                .iter()
4827                .filter(|id| loc_ids.contains(id))
4828                .cloned()
4829                .collect()
4830        };
4831        // Marktlokationen whose SEQ+Z01 carries SG10 CCI+Z30++Z06 (Erzeugung).
4832        let erzeugung: Vec<String> = blocks
4833            .iter()
4834            .filter(|b| b.seq == "Z01")
4835            .filter(|b| {
4836                b.sg10.iter().any(|(ccis, _)| {
4837                    ccis.iter().any(|c| {
4838                        component(c, 0, 0) == Some("Z30") && component(c, 2, 0) == Some("Z06")
4839                    })
4840                })
4841            })
4842            .flat_map(|b| malo_refs(b))
4843            .collect();
4844        let referenced = blocks
4845            .iter()
4846            .filter(|b| b.seq != "Z01")
4847            .any(|b| malo_refs(b).iter().any(|id| erzeugung.contains(id)));
4848        ConditionResult::from(referenced)
4849    }
4850
4851    /// [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)
4852    fn evaluate_224(&self, ctx: &EvaluationContext) -> ConditionResult {
4853        // Check: no SG8 with SEQ+ZE7 has RFF+Z18 referencing same Marktlokation
4854        let has_seq_ze7 = !ctx.find_segments_with_qualifier("SEQ", 0, "ZE7").is_empty();
4855        if !has_seq_ze7 {
4856            return ConditionResult::True;
4857        }
4858        ctx.any_group_has_qualifier_without("SEQ", 0, "ZE7", "RFF", 0, "Z18", &["SG4", "SG8"])
4859    }
4860
4861    /// [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
4862    fn evaluate_227(&self, ctx: &EvaluationContext) -> ConditionResult {
4863        // Check: in SG8 with SEQ+Z98, SG10 has CCI+++ZA6 and CAV+E14 or ETK
4864        let r1 = ctx.filtered_parent_child_has_qualifier(
4865            &["SG4", "SG8"],
4866            "SEQ",
4867            0,
4868            "Z98",
4869            "SG10",
4870            "CAV",
4871            0,
4872            "E14",
4873        );
4874        if r1 == ConditionResult::True {
4875            return ConditionResult::True;
4876        }
4877        ctx.filtered_parent_child_has_qualifier(
4878            &["SG4", "SG8"],
4879            "SEQ",
4880            0,
4881            "Z98",
4882            "SG10",
4883            "CAV",
4884            0,
4885            "ETK",
4886        )
4887    }
4888
4889    /// [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)...
4890    // 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)
4891    fn evaluate_229(&self, ctx: &EvaluationContext) -> ConditionResult {
4892        use std::collections::HashSet;
4893        let target_quals: HashSet<&str> = ["Z58", "ZC9", "ZD0", "ZD6"].iter().cloned().collect();
4894        // Collect per-SG8-instance: SEQ qualifier and Zeitraum-ID
4895        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
4896        let seq_zeitraums = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
4897        // Collect per-SG8-instance: RFF qualifier and DE1154 value
4898        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
4899        let rff_codes = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
4900        // Detect duplicate (seq_qual, zeitraum_id, objektcode) combinations across SG8 instances
4901        let mut seen: HashSet<(String, String, String)> = HashSet::new();
4902        for (idx, seq_qual) in &seq_quals {
4903            if seq_qual.is_empty() || !target_quals.contains(seq_qual.as_str()) {
4904                continue;
4905            }
4906            let zeitraum_id = seq_zeitraums
4907                .iter()
4908                .find(|(i, _)| i == idx)
4909                .map(|(_, v)| v.as_str())
4910                .unwrap_or("");
4911            // Find RFF+Z33 in this SG8 instance (correlation by instance index)
4912            for (ridx, rff_qual) in rff_quals.iter() {
4913                if ridx != idx || rff_qual != "Z33" {
4914                    continue;
4915                }
4916                if let Some((_, obj_code)) = rff_codes.iter().find(|(i, _)| i == ridx) {
4917                    if obj_code.is_empty() {
4918                        continue;
4919                    }
4920                    let key = (seq_qual.clone(), zeitraum_id.to_string(), obj_code.clone());
4921                    if seen.contains(&key) {
4922                        return ConditionResult::True;
4923                    }
4924                    seen.insert(key);
4925                }
4926            }
4927        }
4928        ConditionResult::False
4929    }
4930
4931    /// [232] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z52 (iMS) vorhanden
4932    fn evaluate_232(&self, ctx: &EvaluationContext) -> ConditionResult {
4933        let cci_segs = ctx.find_segments("CCI");
4934        let found = cci_segs.iter().any(|s| {
4935            s.elements
4936                .get(2)
4937                .and_then(|e| e.first())
4938                .is_some_and(|v| v == "Z83")
4939        });
4940        ConditionResult::from(found)
4941    }
4942
4943    /// [233] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle NB
4944    fn evaluate_233(&self, ctx: &EvaluationContext) -> ConditionResult {
4945        ctx.external.evaluate("recipient_role_check")
4946    }
4947
4948    /// [234] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z53 (kME/ mME) vorhanden
4949    fn evaluate_234(&self, ctx: &EvaluationContext) -> ConditionResult {
4950        let cci_segs = ctx.find_segments("CCI");
4951        let found = cci_segs.iter().any(|s| {
4952            s.elements
4953                .get(2)
4954                .and_then(|e| e.first())
4955                .is_some_and(|v| v == "Z83")
4956        });
4957        ConditionResult::from(found)
4958    }
4959
4960    /// [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
4961    fn evaluate_237(&self, ctx: &EvaluationContext) -> ConditionResult {
4962        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4963        // Check: PIA+Z02 with code starting with 1-08 and containing AGS
4964        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4965        let found = pia_segs.iter().any(|s| {
4966            s.elements
4967                .first()
4968                .and_then(|e| e.first())
4969                .is_some_and(|v| v == "Z02")
4970                && s.elements
4971                    .get(1)
4972                    .and_then(|e| e.first())
4973                    .is_some_and(|v| v.starts_with("1-08") && v.contains("AGS"))
4974        });
4975        ConditionResult::from(found)
4976    }
4977
4978    /// [238] Wenn SG4 IDE+24 (Vorgang) nicht vorhanden
4979    fn evaluate_238(&self, ctx: &EvaluationContext) -> ConditionResult {
4980        let ide_segs = ctx.find_segments("IDE");
4981        let found = ide_segs.iter().any(|s| {
4982            s.elements
4983                .first()
4984                .and_then(|e| e.first())
4985                .is_some_and(|v| v == "24")
4986        });
4987        ConditionResult::from(!found)
4988    }
4989
4990    /// [239] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+Z65 (Wärme/Kälte) vorhanden
4991    fn evaluate_239(&self, ctx: &EvaluationContext) -> ConditionResult {
4992        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
4993        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
4994        let found = cci_segs.iter().any(|s| {
4995            s.elements
4996                .first()
4997                .and_then(|e| e.first())
4998                .is_some_and(|v| v == "Z17")
4999        });
5000        ConditionResult::from(found)
5001    }
5002
5003    /// [240] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) CCI+Z22++Z91 (Veräußerungsform der erzeugenden Marktlokation:: Marktprämie) vorhanden
5004    fn evaluate_240(&self, ctx: &EvaluationContext) -> ConditionResult {
5005        let seq_segs = ctx.find_segments("SEQ");
5006        let found = seq_segs.iter().any(|s| {
5007            s.elements
5008                .first()
5009                .and_then(|e| e.first())
5010                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
5011        });
5012        ConditionResult::from(found)
5013    }
5014
5015    /// [241] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle MSB
5016    fn evaluate_241(&self, ctx: &EvaluationContext) -> ConditionResult {
5017        ctx.external.evaluate("recipient_role_check")
5018    }
5019
5020    /// [243] Wenn SG10 CCI+6++ZA8 (Aggreg.verantw. NB) in dieser SG8 vorhanden
5021    fn evaluate_243(&self, ctx: &EvaluationContext) -> ConditionResult {
5022        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5023        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5024        let found = cci_segs.iter().any(|s| {
5025            s.elements
5026                .first()
5027                .and_then(|e| e.first())
5028                .is_some_and(|v| v == "6")
5029                && s.elements
5030                    .get(2)
5031                    .and_then(|e| e.first())
5032                    .is_some_and(|v| v == "ZA8")
5033        });
5034        ConditionResult::from(found)
5035    }
5036
5037    /// [244] Wenn SG10 CCI+6++ZA9 (Aggreg.verantw. ÜNB) in dieser SG8 vorhanden
5038    fn evaluate_244(&self, ctx: &EvaluationContext) -> ConditionResult {
5039        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5040        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5041        let found = cci_segs.iter().any(|s| {
5042            s.elements
5043                .first()
5044                .and_then(|e| e.first())
5045                .is_some_and(|v| v == "6")
5046                && s.elements
5047                    .get(2)
5048                    .and_then(|e| e.first())
5049                    .is_some_and(|v| v == "ZA9")
5050        });
5051        ConditionResult::from(found)
5052    }
5053
5054    /// [248] Wenn die Inbetriebnahme der verbrauchenden Marktlokation innerhalb der letzten drei Jahre war und die Energie der Marktlokation mit iMS ausgestatteten Messlokationen erfolgt
5055    fn evaluate_248(&self, _ctx: &EvaluationContext) -> ConditionResult {
5056        // TODO: implement
5057        ConditionResult::Unknown
5058    }
5059
5060    /// [249] Innerhalb eines SG4 IDE müssen alle DE1131 der SG4 STS+E01 (Status der Antwort) den identischen Wert enthalten
5061    fn evaluate_249(&self, ctx: &EvaluationContext) -> ConditionResult {
5062        // Check: all STS+E01 DE1131 values are identical
5063        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
5064        if sts_segs.len() <= 1 {
5065            return ConditionResult::True;
5066        }
5067        let first_val = sts_segs[0].elements.first().and_then(|e| e.get(1));
5068        let all_same = sts_segs
5069            .iter()
5070            .all(|s| s.elements.first().and_then(|e| e.get(1)) == first_val);
5071        ConditionResult::from(all_same)
5072    }
5073
5074    /// [251] Wenn MP-ID in SG2 NAD+MS (Nachrichtensender) in der Rolle BIKO
5075    fn evaluate_251(&self, ctx: &EvaluationContext) -> ConditionResult {
5076        ctx.external.evaluate("sender_role_check")
5077    }
5078
5079    /// [252] Wenn DE0068 vorhanden
5080    fn evaluate_252(&self, ctx: &EvaluationContext) -> ConditionResult {
5081        let unb_segs = ctx.find_segments("UNB");
5082        let found = unb_segs.iter().any(|s| {
5083            s.elements
5084                .get(4)
5085                .and_then(|e| e.first())
5086                .is_some_and(|v| !v.is_empty())
5087        });
5088        ConditionResult::from(found)
5089    }
5090
5091    /// [254] Es ist der Code \"AUA\" einzutragen
5092    fn evaluate_254(&self, _ctx: &EvaluationContext) -> ConditionResult {
5093        // Check: the code "AUA" is present
5094        ConditionResult::True // AUA is always the required code when this condition applies
5095    }
5096
5097    /// [255] Wenn Marktlokation am Redispatch 2.0 teilnimmt
5098    fn evaluate_255(&self, _ctx: &EvaluationContext) -> ConditionResult {
5099        // TODO: implement
5100        ConditionResult::Unknown
5101    }
5102
5103    /// [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
5104    fn evaluate_256(&self, ctx: &EvaluationContext) -> ConditionResult {
5105        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5106        // Check: PIA+5 has OBIS code matching 1-?:1.8.? or 1-?:2.8.? pattern
5107        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5108        let found = pia_segs.iter().any(|s| {
5109            s.elements
5110                .first()
5111                .and_then(|e| e.first())
5112                .is_some_and(|v| v == "5")
5113                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5114                    (v.contains(":1.8.") || v.contains(":2.8.")) && v.starts_with("1-")
5115                })
5116        });
5117        ConditionResult::from(found)
5118    }
5119
5120    /// [257] Wenn in derselben SG8 SEQ (OBIS-Daten der Marktlokation) das PIA+5+1-b?:1.9.e vorhanden
5121    fn evaluate_257(&self, ctx: &EvaluationContext) -> ConditionResult {
5122        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5123        // Check: PIA+5 has OBIS code matching 1-?:1.9.? pattern
5124        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5125        let found = pia_segs.iter().any(|s| {
5126            s.elements
5127                .first()
5128                .and_then(|e| e.first())
5129                .is_some_and(|v| v == "5")
5130                && s.elements
5131                    .get(1)
5132                    .and_then(|e| e.first())
5133                    .is_some_and(|v| v.contains(":1.9.") && v.starts_with("1-"))
5134        });
5135        ConditionResult::from(found)
5136    }
5137
5138    /// [259] Wenn in diesem PIA der Code im DE7140 mit 1-01, 1-03, 1-05 enthalten ist
5139    fn evaluate_259(&self, ctx: &EvaluationContext) -> ConditionResult {
5140        let pia_segs = ctx.find_segments("PIA");
5141        let found = pia_segs.iter().any(|s| {
5142            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5143                let prefixes = ["1-01", "1-03", "1-05"];
5144                prefixes.iter().any(|p| v.starts_with(p))
5145            })
5146        });
5147        ConditionResult::from(found)
5148    }
5149
5150    /// [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
5151    fn evaluate_261(&self, ctx: &EvaluationContext) -> ConditionResult {
5152        let pia_segs = ctx.find_segments("PIA");
5153        let found = pia_segs.iter().any(|s| {
5154            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5155                let prefixes = [
5156                    "1-07-1", "2", "1-10-1", "2", "4", "5", "6", "7", "8", "9", "1-11-1",
5157                ];
5158                prefixes.iter().any(|p| v.starts_with(p))
5159            })
5160        });
5161        ConditionResult::from(found)
5162    }
5163
5164    /// [262] Wenn Produkt an der Marktlokation vorhanden ist
5165    fn evaluate_262(&self, _ctx: &EvaluationContext) -> ConditionResult {
5166        // TODO: implement
5167        ConditionResult::Unknown
5168    }
5169
5170    /// [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
5171    // HAND-EDITED (mako-twin, 55043): the generated version looked for
5172    // CAV+E14/ETK in any SG10 under SEQ+Z01/Z98, ignoring CCI+++ZA6; the AHB
5173    // asks for CAV+Z36 (TEP m. Referenzmessung) in the SG10 CCI+++ZA6.
5174    fn evaluate_265(&self, ctx: &EvaluationContext) -> ConditionResult {
5175        let Some(blocks) = sg8_blocks(ctx) else {
5176            return ConditionResult::Unknown;
5177        };
5178        let found = blocks
5179            .iter()
5180            .filter(|b| b.seq == "Z01" || b.seq == "Z98")
5181            .any(|b| {
5182                b.sg10.iter().any(|(ccis, cavs)| {
5183                    ccis.iter().any(|c| component(c, 2, 0) == Some("ZA6"))
5184                        && cavs.iter().any(|c| component(c, 0, 0) == Some("Z36"))
5185                })
5186            });
5187        ConditionResult::from(found)
5188    }
5189
5190    /// [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...
5191    // 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)
5192    fn evaluate_266(&self, ctx: &EvaluationContext) -> ConditionResult {
5193        let nav = match ctx.navigator {
5194            Some(n) => n,
5195            None => {
5196                // Fallback: check any SG8+SEQ+Z01 child SG10 has CAV+Z36
5197                return ctx.filtered_parent_child_has_qualifier(
5198                    &["SG4", "SG8"],
5199                    "SEQ",
5200                    0,
5201                    "Z01",
5202                    "SG10",
5203                    "CAV",
5204                    0,
5205                    "Z36",
5206                );
5207            }
5208        };
5209        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5210        for i in 0..sg8_count {
5211            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
5212            for j in 0..sg10_count {
5213                // CCI+++ZA6 means elements[2][0] == "ZA6"
5214                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
5215                let has_cci_za6 = ccis.iter().any(|s| {
5216                    s.elements
5217                        .get(2)
5218                        .and_then(|e: &Vec<String>| e.first())
5219                        .is_some_and(|v: &String| v == "ZA6")
5220                });
5221                if has_cci_za6 {
5222                    let cavs =
5223                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
5224                    if cavs.iter().any(|s| {
5225                        s.elements
5226                            .first()
5227                            .and_then(|e: &Vec<String>| e.first())
5228                            .is_some_and(|v: &String| v == "Z36")
5229                    }) {
5230                        return ConditionResult::True;
5231                    }
5232                }
5233            }
5234        }
5235        ConditionResult::False
5236    }
5237
5238    /// [267] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
5239    fn evaluate_267(&self, ctx: &EvaluationContext) -> ConditionResult {
5240        let seq_segs = ctx.find_segments("SEQ");
5241        let found = seq_segs.iter().any(|s| {
5242            s.elements
5243                .first()
5244                .and_then(|e| e.first())
5245                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
5246        });
5247        ConditionResult::from(!found)
5248    }
5249
5250    /// [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
5251    fn evaluate_268(&self, _ctx: &EvaluationContext) -> ConditionResult {
5252        // Most European countries have postal codes — default to true
5253        ConditionResult::True
5254    }
5255
5256    /// [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
5257    fn evaluate_269(&self, _ctx: &EvaluationContext) -> ConditionResult {
5258        // TODO: implement
5259        ConditionResult::Unknown
5260    }
5261
5262    /// [270] Wenn in diesem PIA der Code im DE7140 mit 1-02, 1-04, 1-06, 1-09 beginnt
5263    fn evaluate_270(&self, ctx: &EvaluationContext) -> ConditionResult {
5264        let pia_segs = ctx.find_segments("PIA");
5265        let found = pia_segs.iter().any(|s| {
5266            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5267                let prefixes = ["1-02", "1-04", "1-06", "1-09"];
5268                prefixes.iter().any(|p| v.starts_with(p))
5269            })
5270        });
5271        ConditionResult::from(found)
5272    }
5273
5274    /// [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 ...
5275    fn evaluate_273(&self, ctx: &EvaluationContext) -> ConditionResult {
5276        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5277        // Check: PIA+5 has OBIS code matching energy quantity patterns in SG8 SEQ+Z20
5278        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5279        let found = pia_segs.iter().any(|s| {
5280            s.elements
5281                .first()
5282                .and_then(|e| e.first())
5283                .is_some_and(|v| v == "5")
5284                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5285                    v.starts_with("1-")
5286                        && (v.contains(":1.8.") || v.contains(":2.8.") || v.contains(":1.29."))
5287                })
5288        });
5289        ConditionResult::from(found)
5290    }
5291
5292    /// [279] Wenn STS+7++xxx+ZW4 / ZAP (Transaktionsgrundergänzung  verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
5293    fn evaluate_279(&self, ctx: &EvaluationContext) -> ConditionResult {
5294        let sts_segs = ctx.find_segments("STS");
5295        let found = sts_segs.iter().any(|s| {
5296            s.elements
5297                .first()
5298                .and_then(|e| e.first())
5299                .is_some_and(|v| v == "7")
5300                && s.elements
5301                    .get(3)
5302                    .and_then(|e| e.first())
5303                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
5304        });
5305        ConditionResult::from(found)
5306    }
5307
5308    /// [280] Nur MP-ID aus Sparte Strom
5309    fn evaluate_280(&self, ctx: &EvaluationContext) -> ConditionResult {
5310        // Check: Marktpartner-ID is from Sparte Strom
5311        // Approximate: check NAD+MS C082 for Strom MP-ID format
5312        ctx.external.evaluate("sector_check")
5313    }
5314
5315    /// [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
5316    fn evaluate_282(&self, ctx: &EvaluationContext) -> ConditionResult {
5317        // Check: PIA DE7140 has one of the specific codes
5318        let pia_segs = ctx.find_segments("PIA");
5319        let found = pia_segs.iter().any(|s| {
5320            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5321                v.starts_with("1-01-6-005")
5322                    || v.starts_with("1-01-9-001")
5323                    || v.starts_with("1-01-9-002")
5324                    || v.starts_with("1-07-3-001")
5325                    || v.starts_with("1-08-1-001")
5326                    || v.starts_with("1-08-3-001")
5327                    || v.starts_with("1-08-4-001")
5328                    || v.starts_with("1-08-4-002")
5329                    || v.starts_with("1-08-4-003")
5330                    || v.starts_with("1-08-5")
5331            })
5332        });
5333        ConditionResult::from(found)
5334    }
5335
5336    /// [284] Erlaubte Codes aus der Codeliste der Gruppenartikel- und Artikel-ID sind in der Spalte UTILMD/Codeverwendung mit X gekennzeichnet
5337    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
5338    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
5339    // Regenerating this function should preserve the shape, but re-verify
5340    // against the committed body if generate-conditions is run.
5341    fn evaluate_284(&self, ctx: &EvaluationContext) -> ConditionResult {
5342        // [284] Erlaubte Codes aus der Codeliste der Gruppenartikel-
5343        // und Artikel-ID mit X in der Spalte UTILMD/Codeverwendung.
5344        //
5345        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
5346        // narrows to the enclosing SG8 instance; otherwise falls back to
5347        // message-wide find_segments. Matches the AHB wording
5348        // "in derselben SG8 im PIA DE7140 ein Code...".
5349        use crate::eval::CodelistColumn;
5350        let any_match = ctx.scoped_find_segments("PIA").iter().any(|s| {
5351            let is_z02 = s
5352                .elements
5353                .first()
5354                .and_then(|e| e.first())
5355                .is_some_and(|v| v == "Z02");
5356            if !is_z02 {
5357                return false;
5358            }
5359            let code = match s.elements.get(1).and_then(|e| e.first()) {
5360                Some(c) if !c.is_empty() => c,
5361                _ => return false,
5362            };
5363            ctx.codelist
5364                .flag(code, CodelistColumn::UtilmdCodeverwendung)
5365                .unwrap_or(false)
5366        });
5367        ConditionResult::from(any_match)
5368    }
5369
5370    /// [285] Wenn in diesem PIA der Code im DE7140 mit 1-08-3-AGS angegeben ist
5371    fn evaluate_285(&self, ctx: &EvaluationContext) -> ConditionResult {
5372        // Check: PIA DE7140 starts with 1-08-3-AGS
5373        let pia_segs = ctx.find_segments("PIA");
5374        let found = pia_segs.iter().any(|s| {
5375            s.elements
5376                .get(1)
5377                .and_then(|e| e.first())
5378                .is_some_and(|v| v.contains("1-08-3") && v.contains("AGS"))
5379        });
5380        ConditionResult::from(found)
5381    }
5382
5383    /// [286] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/2/4/5-AGS-KG
5384    fn evaluate_286(&self, ctx: &EvaluationContext) -> ConditionResult {
5385        // Check: PIA DE7140 matches 1-08-1/2/4/5-AGS-KG pattern
5386        let pia_segs = ctx.find_segments("PIA");
5387        let found = pia_segs.iter().any(|s| {
5388            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5389                (v.starts_with("1-08-1")
5390                    || v.starts_with("1-08-2")
5391                    || v.starts_with("1-08-4")
5392                    || v.starts_with("1-08-5"))
5393                    && v.contains("AGS")
5394                    && v.contains("KG")
5395            })
5396        });
5397        ConditionResult::from(found)
5398    }
5399
5400    /// [287] Erlaubte Codes aus der PRICAT BGM+ Z70 (Preisblatt Netznutzung) des verantwortlichen NB
5401    fn evaluate_287(&self, _ctx: &EvaluationContext) -> ConditionResult {
5402        // TODO: implement
5403        ConditionResult::Unknown
5404    }
5405
5406    /// [288] Wenn in derselben SG8 (OBIS-Daten der Marktlokation) das PIA+5+1-b?:2.9.e vorhanden
5407    fn evaluate_288(&self, ctx: &EvaluationContext) -> ConditionResult {
5408        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5409        // Check: PIA+5 has OBIS code matching 1-?:2.9.? pattern
5410        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5411        let found = pia_segs.iter().any(|s| {
5412            s.elements
5413                .first()
5414                .and_then(|e| e.first())
5415                .is_some_and(|v| v == "5")
5416                && s.elements
5417                    .get(1)
5418                    .and_then(|e| e.first())
5419                    .is_some_and(|v| v.contains(":2.9.") && v.starts_with("1-"))
5420        });
5421        ConditionResult::from(found)
5422    }
5423
5424    /// [291] Wenn in derselben SG8 (Zuordnung Lokation zum Objektcode des Lokationsbündels) das RFF+Z37 vorhanden
5425    fn evaluate_291(&self, ctx: &EvaluationContext) -> ConditionResult {
5426        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5427        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
5428        let found = rff_segs.iter().any(|s| {
5429            s.elements
5430                .first()
5431                .and_then(|e| e.first())
5432                .is_some_and(|v| v == "Z37")
5433        });
5434        ConditionResult::from(found)
5435    }
5436
5437    /// [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...
5438    fn evaluate_292(&self, ctx: &EvaluationContext) -> ConditionResult {
5439        ctx.external.evaluate("code_list_membership_check")
5440    }
5441
5442    /// [293] Wenn SG5 LOC+Z22 (ruhende Marktlokation) vorhanden
5443    fn evaluate_293(&self, ctx: &EvaluationContext) -> ConditionResult {
5444        let loc_segs = ctx.find_segments("LOC");
5445        let found = loc_segs.iter().any(|s| {
5446            s.elements
5447                .first()
5448                .and_then(|e| e.first())
5449                .is_some_and(|v| v == "Z22")
5450        });
5451        ConditionResult::from(found)
5452    }
5453
5454    /// [294] Wenn SG5 LOC+Z18 (Netzlokation) vorhanden
5455    fn evaluate_294(&self, ctx: &EvaluationContext) -> ConditionResult {
5456        let loc_segs = ctx.find_segments("LOC");
5457        let found = loc_segs.iter().any(|s| {
5458            s.elements
5459                .first()
5460                .and_then(|e| e.first())
5461                .is_some_and(|v| v == "Z18")
5462        });
5463        ConditionResult::from(found)
5464    }
5465
5466    /// [295] Wenn SG5 LOC+Z15 (MaBiS-Zählpunkt) nicht vorhanden
5467    fn evaluate_295(&self, ctx: &EvaluationContext) -> ConditionResult {
5468        let loc_segs = ctx.find_segments("LOC");
5469        let found = loc_segs.iter().any(|s| {
5470            s.elements
5471                .first()
5472                .and_then(|e| e.first())
5473                .is_some_and(|v| v == "Z15")
5474        });
5475        ConditionResult::from(!found)
5476    }
5477
5478    /// [296] Wenn SG5 LOC+Z20 (Technische Ressource) vorhanden
5479    fn evaluate_296(&self, ctx: &EvaluationContext) -> ConditionResult {
5480        let loc_segs = ctx.find_segments("LOC");
5481        let found = loc_segs.iter().any(|s| {
5482            s.elements
5483                .first()
5484                .and_then(|e| e.first())
5485                .is_some_and(|v| v == "Z20")
5486        });
5487        ConditionResult::from(found)
5488    }
5489
5490    /// [297] #kv# Wenn SG5 LOC+Z18 (Netzlokation) nicht vorhanden
5491    fn evaluate_297(&self, ctx: &EvaluationContext) -> ConditionResult {
5492        let loc_segs = ctx.find_segments("LOC");
5493        let found = loc_segs.iter().any(|s| {
5494            s.elements
5495                .first()
5496                .and_then(|e| e.first())
5497                .is_some_and(|v| v == "Z18")
5498        });
5499        ConditionResult::from(!found)
5500    }
5501
5502    /// [298] Wenn SG5 LOC+Z20 (Technische Ressource) nicht vorhanden
5503    fn evaluate_298(&self, ctx: &EvaluationContext) -> ConditionResult {
5504        let loc_segs = ctx.find_segments("LOC");
5505        let found = loc_segs.iter().any(|s| {
5506            s.elements
5507                .first()
5508                .and_then(|e| e.first())
5509                .is_some_and(|v| v == "Z20")
5510        });
5511        ConditionResult::from(!found)
5512    }
5513
5514    /// [299] #kv# Wenn SG5 LOC+Z19 (Steuerbare Ressource) nicht vorhanden
5515    fn evaluate_299(&self, ctx: &EvaluationContext) -> ConditionResult {
5516        let loc_segs = ctx.find_segments("LOC");
5517        let found = loc_segs.iter().any(|s| {
5518            s.elements
5519                .first()
5520                .and_then(|e| e.first())
5521                .is_some_and(|v| v == "Z19")
5522        });
5523        ConditionResult::from(!found)
5524    }
5525
5526    /// [300] Wenn SG8 SEQ+Z15/ ZE7 (Daten der Tranche) nicht vorhanden
5527    fn evaluate_300(&self, ctx: &EvaluationContext) -> ConditionResult {
5528        let seq_segs = ctx.find_segments("SEQ");
5529        let found = seq_segs.iter().any(|s| {
5530            s.elements
5531                .first()
5532                .and_then(|e| e.first())
5533                .is_some_and(|v| ["Z15", "ZE7"].contains(&v.as_str()))
5534        });
5535        ConditionResult::from(!found)
5536    }
5537
5538    /// [301] Wenn BGM+E03 (Änderungsmeldung) vorhanden
5539    fn evaluate_301(&self, ctx: &EvaluationContext) -> ConditionResult {
5540        let bgm_segs = ctx.find_segments("BGM");
5541        let found = bgm_segs.iter().any(|s| {
5542            s.elements
5543                .first()
5544                .and_then(|e| e.first())
5545                .is_some_and(|v| v == "E03")
5546        });
5547        ConditionResult::from(found)
5548    }
5549
5550    /// [302] Wenn SG8 SEQ+Z08 (Profilschardaten) nicht vorhanden
5551    fn evaluate_302(&self, ctx: &EvaluationContext) -> ConditionResult {
5552        let seq_segs = ctx.find_segments("SEQ");
5553        let found = seq_segs.iter().any(|s| {
5554            s.elements
5555                .first()
5556                .and_then(|e| e.first())
5557                .is_some_and(|v| ["Z08"].contains(&v.as_str()))
5558        });
5559        ConditionResult::from(!found)
5560    }
5561
5562    /// [303] Wenn SG8 SEQ+Z21 (Profildaten) nicht vorhanden
5563    fn evaluate_303(&self, ctx: &EvaluationContext) -> ConditionResult {
5564        let seq_segs = ctx.find_segments("SEQ");
5565        let found = seq_segs.iter().any(|s| {
5566            s.elements
5567                .first()
5568                .and_then(|e| e.first())
5569                .is_some_and(|v| ["Z21"].contains(&v.as_str()))
5570        });
5571        ConditionResult::from(!found)
5572    }
5573
5574    /// [304] Wenn SG8 SEQ+Z22 (Daten der Summenzeitreihe) vorhanden
5575    fn evaluate_304(&self, ctx: &EvaluationContext) -> ConditionResult {
5576        let seq_segs = ctx.find_segments("SEQ");
5577        let found = seq_segs.iter().any(|s| {
5578            s.elements
5579                .first()
5580                .and_then(|e| e.first())
5581                .is_some_and(|v| ["Z22"].contains(&v.as_str()))
5582        });
5583        ConditionResult::from(found)
5584    }
5585
5586    /// [305] Wenn SG8 SEQ+Z24 (Daten der Überführungszeitreihe) vorhanden
5587    fn evaluate_305(&self, ctx: &EvaluationContext) -> ConditionResult {
5588        let seq_segs = ctx.find_segments("SEQ");
5589        let found = seq_segs.iter().any(|s| {
5590            s.elements
5591                .first()
5592                .and_then(|e| e.first())
5593                .is_some_and(|v| ["Z24"].contains(&v.as_str()))
5594        });
5595        ConditionResult::from(found)
5596    }
5597
5598    /// [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
5599    fn evaluate_306(&self, ctx: &EvaluationContext) -> ConditionResult {
5600        use std::collections::HashSet;
5601        // Collect Zeitraum-IDs from SG5 LOC+Z22 (Ruhende Marktlokation) — DE3224 at elements[1][3]
5602        let loc_z22_ids: HashSet<String> = ctx
5603            .find_segments_with_qualifier("LOC", 0, "Z22")
5604            .into_iter()
5605            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
5606            .filter(|v| !v.is_empty())
5607            .collect();
5608        if loc_z22_ids.is_empty() {
5609            return ConditionResult::False;
5610        }
5611        // Check if any SG8 SEQ+Z58 has DE1050 (elements[1][0]) matching a LOC+Z22 DE3224
5612        let has_match = ctx
5613            .find_segments_with_qualifier("SEQ", 0, "Z58")
5614            .iter()
5615            .any(|s| {
5616                s.elements
5617                    .get(1)
5618                    .and_then(|e: &Vec<String>| e.first())
5619                    .is_some_and(|v: &String| !v.is_empty() && loc_z22_ids.contains(v.as_str()))
5620            });
5621        ConditionResult::from(has_match)
5622    }
5623
5624    /// [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
5625    // 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)
5626    fn evaluate_307(&self, ctx: &EvaluationContext) -> ConditionResult {
5627        let seq_segs = ctx.find_segments_with_qualifier("SEQ", 0, "Z58");
5628        if seq_segs.is_empty() {
5629            return ConditionResult::Unknown;
5630        }
5631        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z22");
5632        let loc_z22_zeitraum_ids: Vec<&str> = loc_segs
5633            .iter()
5634            .filter_map(|s| s.elements.get(1).and_then(|e| e.get(3)).map(|v| v.as_str()))
5635            .filter(|v| !v.is_empty())
5636            .collect();
5637        ConditionResult::from(seq_segs.iter().any(|seq| {
5638            let zid = seq
5639                .elements
5640                .get(1)
5641                .and_then(|e: &Vec<String>| e.first())
5642                .map(|v| v.as_str())
5643                .unwrap_or("");
5644            !zid.is_empty() && !loc_z22_zeitraum_ids.contains(&zid)
5645        }))
5646    }
5647
5648    /// [309] Wenn SG5 LOC+Z22 (ruhende Marktlokation) nicht vorhanden
5649    fn evaluate_309(&self, ctx: &EvaluationContext) -> ConditionResult {
5650        let loc_segs = ctx.find_segments("LOC");
5651        let found = loc_segs.iter().any(|s| {
5652            s.elements
5653                .first()
5654                .and_then(|e| e.first())
5655                .is_some_and(|v| v == "Z22")
5656        });
5657        ConditionResult::from(!found)
5658    }
5659
5660    /// [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...
5661    fn evaluate_314(&self, ctx: &EvaluationContext) -> ConditionResult {
5662        ctx.external.evaluate("code_list_membership_check")
5663    }
5664
5665    /// [315] Wenn BGM+Z88 (Datenclearing) vorhanden
5666    fn evaluate_315(&self, ctx: &EvaluationContext) -> ConditionResult {
5667        ctx.external.evaluate("data_clearing_required")
5668    }
5669
5670    /// [316] Wenn in derselben SG8 das RFF+Z14 (Smartmeter-Gateway) nicht vorhanden
5671    fn evaluate_316(&self, ctx: &EvaluationContext) -> ConditionResult {
5672        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5673        ctx.this_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
5674    }
5675
5676    /// [317] Es ist derselbe Wert wie im DE2380 von DTM+92 (Datum Vertragsbeginn) einzutragen
5677    fn evaluate_317(&self, ctx: &EvaluationContext) -> ConditionResult {
5678        // Check: DTM+92 (Datum Vertragsbeginn) exists — value identity is structural
5679        let found = !ctx.find_segments_with_qualifier("DTM", 0, "92").is_empty();
5680        if found {
5681            ConditionResult::True
5682        } else {
5683            ConditionResult::Unknown
5684        }
5685    }
5686
5687    /// [318] Es ist derselbe Wert wie im DE2380 von DTM+93 (Datum Vertragende) einzutragen
5688    fn evaluate_318(&self, ctx: &EvaluationContext) -> ConditionResult {
5689        // Check: DTM+93 (Datum Vertragende) exists — value identity is structural
5690        let found = !ctx.find_segments_with_qualifier("DTM", 0, "93").is_empty();
5691        if found {
5692            ConditionResult::True
5693        } else {
5694            ConditionResult::Unknown
5695        }
5696    }
5697
5698    /// [321] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
5699    fn evaluate_321(&self, ctx: &EvaluationContext) -> ConditionResult {
5700        let com_segs = ctx.find_segments("COM");
5701        let has_em = com_segs.iter().any(|s| {
5702            s.elements
5703                .first()
5704                .and_then(|e| e.get(1))
5705                .is_some_and(|v| v == "EM")
5706        });
5707        ConditionResult::from(has_em)
5708    }
5709
5710    /// [322] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
5711    fn evaluate_322(&self, ctx: &EvaluationContext) -> ConditionResult {
5712        let com_segs = ctx.find_segments("COM");
5713        let has_phone = com_segs.iter().any(|s| {
5714            s.elements
5715                .first()
5716                .and_then(|e| e.get(1))
5717                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
5718        });
5719        ConditionResult::from(has_phone)
5720    }
5721
5722    /// [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...
5723    fn evaluate_323(&self, ctx: &EvaluationContext) -> ConditionResult {
5724        ctx.external.evaluate("code_list_membership_check")
5725    }
5726
5727    /// [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...
5728    // 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)
5729    fn evaluate_327(&self, ctx: &EvaluationContext) -> ConditionResult {
5730        const THRESHOLD: &str = "202212312300+00";
5731        let seq_zeitraum_ids: Vec<String> = ctx
5732            .find_segments("SEQ")
5733            .iter()
5734            .filter_map(|s| {
5735                s.elements
5736                    .get(1)
5737                    .and_then(|e: &Vec<String>| e.first())
5738                    .cloned()
5739            })
5740            .filter(|v| !v.is_empty())
5741            .collect();
5742        if seq_zeitraum_ids.is_empty() {
5743            return ConditionResult::Unknown;
5744        }
5745        let rff_segs = ctx.find_segments("RFF");
5746        let matching_rff = rff_segs.iter().any(|rff| {
5747            let qual = rff
5748                .elements
5749                .first()
5750                .and_then(|e: &Vec<String>| e.first())
5751                .map(|v| v.as_str())
5752                .unwrap_or("");
5753            if !["Z47", "Z48", "Z49"].contains(&qual) {
5754                return false;
5755            }
5756            let zid = rff
5757                .elements
5758                .first()
5759                .and_then(|e| e.get(2))
5760                .map(|v| v.as_str())
5761                .unwrap_or("");
5762            !zid.is_empty() && seq_zeitraum_ids.iter().any(|id| id.as_str() == zid)
5763        });
5764        if !matching_rff {
5765            return ConditionResult::False;
5766        }
5767        for dtm in ctx.find_segments_with_qualifier("DTM", 0, "Z25") {
5768            let val = dtm
5769                .elements
5770                .first()
5771                .and_then(|e| e.get(1))
5772                .map(|v| v.replace("?+", "+"))
5773                .unwrap_or_default();
5774            if !val.is_empty() && val.as_str() >= THRESHOLD {
5775                return ConditionResult::True;
5776            }
5777        }
5778        ConditionResult::False
5779    }
5780
5781    /// [329] Wenn in dieser SG4 IDE+24 das STS+E01++A03: E_0096 bzw. A04:E_0097 (Status der Antwort) nicht vorhanden
5782    fn evaluate_329(&self, ctx: &EvaluationContext) -> ConditionResult {
5783        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
5784        // Check: STS+E01 does NOT have A03 with E_0096 or A04 with E_0097
5785        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
5786        let has_a03_e0096 = sts_segs.iter().any(|s| {
5787            s.elements
5788                .get(2)
5789                .and_then(|e| e.first())
5790                .is_some_and(|v| v == "A03")
5791                && s.elements
5792                    .get(2)
5793                    .and_then(|e| e.get(1))
5794                    .is_some_and(|v| v == "E_0096")
5795        });
5796        let has_a04_e0097 = sts_segs.iter().any(|s| {
5797            s.elements
5798                .get(2)
5799                .and_then(|e| e.first())
5800                .is_some_and(|v| v == "A04")
5801                && s.elements
5802                    .get(2)
5803                    .and_then(|e| e.get(1))
5804                    .is_some_and(|v| v == "E_0097")
5805        });
5806        ConditionResult::from(!has_a03_e0096 && !has_a04_e0097)
5807    }
5808
5809    /// [331] Wenn im SG8 SEQ+Z98 (Daten der Marktlokation) das SG10 CCI+6++ZA9 (Aggreg. verantw. ÜNB) nicht vorhanden
5810    fn evaluate_331(&self, ctx: &EvaluationContext) -> ConditionResult {
5811        let seq_segs = ctx.find_segments("SEQ");
5812        let has_seq = seq_segs.iter().any(|s| {
5813            s.elements
5814                .first()
5815                .and_then(|e| e.first())
5816                .is_some_and(|v| v == "Z98")
5817        });
5818        let cci_segs = ctx.find_segments("CCI");
5819        let has_cci = cci_segs.iter().any(|s| {
5820            s.elements
5821                .first()
5822                .and_then(|e| e.first())
5823                .is_some_and(|v| v == "6")
5824                && s.elements
5825                    .get(2)
5826                    .and_then(|e| e.first())
5827                    .is_some_and(|v| v == "ZA9")
5828        });
5829        ConditionResult::from(!(has_seq && has_cci))
5830    }
5831
5832    /// [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
5833    // 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)
5834    fn evaluate_332(&self, ctx: &EvaluationContext) -> ConditionResult {
5835        let nav = match ctx.navigator {
5836            Some(n) => n,
5837            None => {
5838                if ctx.find_segments_with_qualifier("SEQ", 0, "Z01").is_empty() {
5839                    return ConditionResult::Unknown;
5840                }
5841                let has_cci_za9 = ctx
5842                    .find_segments_with_qualifier("CCI", 0, "6")
5843                    .iter()
5844                    .any(|s| {
5845                        s.elements
5846                            .get(2)
5847                            .and_then(|e: &Vec<String>| e.first())
5848                            .map(|v| v.as_str())
5849                            == Some("ZA9")
5850                    });
5851                return ConditionResult::from(!has_cci_za9);
5852            }
5853        };
5854        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5855        if sg8_count == 0 {
5856            return ConditionResult::Unknown;
5857        }
5858        for sg8_idx in 0..sg8_count {
5859            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], sg8_idx, "SG10");
5860            let mut has_cci_za9 = false;
5861            for sg10_idx in 0..sg10_count {
5862                let ccis = nav.find_segments_in_child_group(
5863                    "CCI",
5864                    &["SG4", "SG8"],
5865                    sg8_idx,
5866                    "SG10",
5867                    sg10_idx,
5868                );
5869                if ccis.iter().any(|s| {
5870                    s.elements
5871                        .first()
5872                        .and_then(|e: &Vec<String>| e.first())
5873                        .map(|v| v.as_str())
5874                        == Some("6")
5875                        && s.elements
5876                            .get(2)
5877                            .and_then(|e: &Vec<String>| e.first())
5878                            .map(|v| v.as_str())
5879                            == Some("ZA9")
5880                }) {
5881                    has_cci_za9 = true;
5882                    break;
5883                }
5884            }
5885            if !has_cci_za9 {
5886                return ConditionResult::True;
5887            }
5888        }
5889        ConditionResult::False
5890    }
5891
5892    /// [333] Wenn in diesem RFF \"Referenz auf die Lokationsbündelstruktur\" im DE1153 der Code Z31 \"Lokationsbündelstruktur\" vorhanden ist
5893    fn evaluate_333(&self, ctx: &EvaluationContext) -> ConditionResult {
5894        let rff_segs = ctx.find_segments("RFF");
5895        let found = rff_segs.iter().any(|s| {
5896            s.elements
5897                .first()
5898                .and_then(|e| e.first())
5899                .is_some_and(|v| v == "Z31")
5900        });
5901        ConditionResult::from(found)
5902    }
5903
5904    /// [334] Nur MP-ID aus Sparte Gas
5905    fn evaluate_334(&self, ctx: &EvaluationContext) -> ConditionResult {
5906        // Check: Marktpartner-ID is from Sparte Gas
5907        ctx.external.evaluate("sector_check")
5908    }
5909
5910    /// [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...
5911    // 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)
5912    fn evaluate_335(&self, ctx: &EvaluationContext) -> ConditionResult {
5913        // HAND-EDITED: the Zeitraum-ID of *this* SG8's SEQ+Z58 (DE1050), looked
5914        // for in the RFF+Z31 of any SG8 SEQ+Z78 of the message.
5915        let z58_ids: Vec<String> = ctx
5916            .scoped_find_segments_in_with_qualifier("SG8", "SEQ", 0, "Z58")
5917            .iter()
5918            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
5919            .filter(|v| !v.is_empty())
5920            .collect();
5921        if z58_ids.is_empty() {
5922            return ConditionResult::Unknown;
5923        }
5924        if ctx.find_segments_with_qualifier("SEQ", 0, "Z78").is_empty() {
5925            return ConditionResult::False;
5926        }
5927        let rff_z31_ids: Vec<String> = ctx
5928            .find_segments_with_qualifier("RFF", 0, "Z31")
5929            .iter()
5930            .filter_map(|rff| rff.elements.first().and_then(|e| e.get(1)).cloned())
5931            .filter(|v| !v.is_empty())
5932            .collect();
5933        ConditionResult::from(z58_ids.iter().any(|id| rff_z31_ids.contains(id)))
5934    }
5935
5936    /// [336] Wenn in Änderungsmeldung gefüllt
5937    fn evaluate_336(&self, _ctx: &EvaluationContext) -> ConditionResult {
5938        // TODO: implement
5939        ConditionResult::Unknown
5940    }
5941
5942    /// [337] Wenn SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96 vorhanden
5943    fn evaluate_337(&self, ctx: &EvaluationContext) -> ConditionResult {
5944        let cci_segs = ctx.find_segments("CCI");
5945        let has_cci = cci_segs.iter().any(|s| {
5946            s.elements
5947                .get(2)
5948                .and_then(|e| e.first())
5949                .is_some_and(|v| v == "ZB4")
5950        });
5951        let cav_segs = ctx.find_segments("CAV");
5952        let has_cav = cav_segs.iter().any(|s| {
5953            s.elements
5954                .first()
5955                .and_then(|e| e.first())
5956                .is_some_and(|v| ["Z95", "Z96"].contains(&v.as_str()))
5957        });
5958        ConditionResult::from(has_cci && has_cav)
5959    }
5960
5961    /// [338] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96/ Z97/ Z99/ ZA1/ ZA3/ ZA4/ZG7 vorhanden
5962    fn evaluate_338(&self, ctx: &EvaluationContext) -> ConditionResult {
5963        let cci_segs = ctx.find_segments("CCI");
5964        let has_cci = cci_segs.iter().any(|s| {
5965            s.elements
5966                .get(2)
5967                .and_then(|e| e.first())
5968                .is_some_and(|v| v == "ZB4")
5969        });
5970        let cav_segs = ctx.find_segments("CAV");
5971        let has_cav = cav_segs.iter().any(|s| {
5972            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
5973                ["Z95", "Z96", "Z97", "Z99", "ZA1", "ZA3", "ZA4", "ZG7"].contains(&v.as_str())
5974            })
5975        });
5976        ConditionResult::from(has_cci && has_cav)
5977    }
5978
5979    /// [339] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z97/ Z98/ Z99/ ZA0/ ZA1/ ZA2/ ZA3/ ZA6/ ZG7 vorhanden
5980    fn evaluate_339(&self, ctx: &EvaluationContext) -> ConditionResult {
5981        let cci_segs = ctx.find_segments("CCI");
5982        let has_cci = cci_segs.iter().any(|s| {
5983            s.elements
5984                .get(2)
5985                .and_then(|e| e.first())
5986                .is_some_and(|v| v == "ZB4")
5987        });
5988        let cav_segs = ctx.find_segments("CAV");
5989        let has_cav = cav_segs.iter().any(|s| {
5990            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
5991                [
5992                    "Z97", "Z98", "Z99", "ZA0", "ZA1", "ZA2", "ZA3", "ZA6", "ZG7",
5993                ]
5994                .contains(&v.as_str())
5995            })
5996        });
5997        ConditionResult::from(has_cci && has_cav)
5998    }
5999
6000    /// [340] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z97/ Z98/ Z99/ ZA1/ ZA2/ ZA3/ ZA4/ ZA5/ ZA6 vorhanden
6001    fn evaluate_340(&self, ctx: &EvaluationContext) -> ConditionResult {
6002        let cci_segs = ctx.find_segments("CCI");
6003        let has_cci = cci_segs.iter().any(|s| {
6004            s.elements
6005                .get(2)
6006                .and_then(|e| e.first())
6007                .is_some_and(|v| v == "ZB4")
6008        });
6009        let cav_segs = ctx.find_segments("CAV");
6010        let has_cav = cav_segs.iter().any(|s| {
6011            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6012                [
6013                    "Z95", "Z97", "Z98", "Z99", "ZA1", "ZA2", "ZA3", "ZA4", "ZA5", "ZA6",
6014                ]
6015                .contains(&v.as_str())
6016            })
6017        });
6018        ConditionResult::from(has_cci && has_cav)
6019    }
6020
6021    /// [341] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z96/ ZA0/ ZG7 vorhanden
6022    fn evaluate_341(&self, ctx: &EvaluationContext) -> ConditionResult {
6023        let cci_segs = ctx.find_segments("CCI");
6024        let has_cci = cci_segs.iter().any(|s| {
6025            s.elements
6026                .get(2)
6027                .and_then(|e| e.first())
6028                .is_some_and(|v| v == "ZB4")
6029        });
6030        let cav_segs = ctx.find_segments("CAV");
6031        let has_cav = cav_segs.iter().any(|s| {
6032            s.elements
6033                .first()
6034                .and_then(|e| e.first())
6035                .is_some_and(|v| ["Z96", "ZA0", "ZG7"].contains(&v.as_str()))
6036        });
6037        ConditionResult::from(has_cci && has_cav)
6038    }
6039
6040    /// [342] Wenn aktiver MaBiS-ZP für den Betrachtungszeitraum vorhanden
6041    fn evaluate_342(&self, _ctx: &EvaluationContext) -> ConditionResult {
6042        // TODO: implement
6043        ConditionResult::Unknown
6044    }
6045
6046    /// [344] Wenn in dieser SG8 das SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+ZF1 (Netzgangzeitreihe (NGZ)) vorhanden
6047    fn evaluate_344(&self, ctx: &EvaluationContext) -> ConditionResult {
6048        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6049        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6050        let has_cci = cci_segs.iter().any(|s| {
6051            s.elements
6052                .get(2)
6053                .and_then(|e| e.first())
6054                .is_some_and(|v| v == "ZB4")
6055        });
6056        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
6057        let has_cav = cav_segs.iter().any(|s| {
6058            s.elements
6059                .first()
6060                .and_then(|e| e.first())
6061                .is_some_and(|v| ["ZF1"].contains(&v.as_str()))
6062        });
6063        ConditionResult::from(has_cci && has_cav)
6064    }
6065
6066    /// [345] Wenn SG5 LOC+Z17 (Messlokation) vorhanden
6067    fn evaluate_345(&self, ctx: &EvaluationContext) -> ConditionResult {
6068        let loc_segs = ctx.find_segments("LOC");
6069        let found = loc_segs.iter().any(|s| {
6070            s.elements
6071                .first()
6072                .and_then(|e| e.first())
6073                .is_some_and(|v| v == "Z17")
6074        });
6075        ConditionResult::from(found)
6076    }
6077
6078    /// [346] Wenn SG10 CCI+++E13 (Zählertyp) CAV+MME (mME) vorhanden
6079    fn evaluate_346(&self, ctx: &EvaluationContext) -> ConditionResult {
6080        let cci_segs = ctx.find_segments("CCI");
6081        let found = cci_segs.iter().any(|s| {
6082            s.elements
6083                .get(2)
6084                .and_then(|e| e.first())
6085                .is_some_and(|v| v == "E13")
6086        });
6087        ConditionResult::from(found)
6088    }
6089
6090    /// [347] Wenn SG5 LOC+Z16 (Marktlokation) nicht vorhanden
6091    fn evaluate_347(&self, ctx: &EvaluationContext) -> ConditionResult {
6092        let loc_segs = ctx.find_segments("LOC");
6093        let found = loc_segs.iter().any(|s| {
6094            s.elements
6095                .first()
6096                .and_then(|e| e.first())
6097                .is_some_and(|v| v == "Z16")
6098        });
6099        ConditionResult::from(!found)
6100    }
6101
6102    /// [348] Wenn SG5 LOC+Z21 (Tranche) nicht vorhanden
6103    fn evaluate_348(&self, ctx: &EvaluationContext) -> ConditionResult {
6104        let loc_segs = ctx.find_segments("LOC");
6105        let found = loc_segs.iter().any(|s| {
6106            s.elements
6107                .first()
6108                .and_then(|e| e.first())
6109                .is_some_and(|v| v == "Z21")
6110        });
6111        ConditionResult::from(!found)
6112    }
6113
6114    /// [349] Wenn SG5 LOC+Z17 (Messlokation) nicht vorhanden
6115    fn evaluate_349(&self, ctx: &EvaluationContext) -> ConditionResult {
6116        let loc_segs = ctx.find_segments("LOC");
6117        let found = loc_segs.iter().any(|s| {
6118            s.elements
6119                .first()
6120                .and_then(|e| e.first())
6121                .is_some_and(|v| v == "Z17")
6122        });
6123        ConditionResult::from(!found)
6124    }
6125
6126    /// [350] Wenn SG8 SEQ+Z78/ZD5 RFF+Z31 (Referenz auf die Lokationsbündelstruktur) vorhanden
6127    fn evaluate_350(&self, ctx: &EvaluationContext) -> ConditionResult {
6128        let seq_segs = ctx.find_segments("SEQ");
6129        let has_seq = seq_segs.iter().any(|s| {
6130            s.elements
6131                .first()
6132                .and_then(|e| e.first())
6133                .is_some_and(|v| ["Z78", "ZD5"].contains(&v.as_str()))
6134        });
6135        let rff_segs = ctx.find_segments("RFF");
6136        let has_rff = rff_segs.iter().any(|s| {
6137            s.elements
6138                .first()
6139                .and_then(|e| e.first())
6140                .is_some_and(|v| v == "Z31")
6141        });
6142        ConditionResult::from(has_seq && has_rff)
6143    }
6144
6145    /// [351] Wenn SG4 STS+E01++A04/ A05 /A13 /A14 (Status der Antwort) vorhanden
6146    fn evaluate_351(&self, ctx: &EvaluationContext) -> ConditionResult {
6147        let sts_segs = ctx.find_segments("STS");
6148        let found = sts_segs.iter().any(|s| {
6149            s.elements
6150                .first()
6151                .and_then(|e| e.first())
6152                .is_some_and(|v| v == "E01")
6153                && s.elements
6154                    .get(2)
6155                    .and_then(|e| e.first())
6156                    .is_some_and(|v| ["A04", "A05", "A13", "A14"].contains(&v.as_str()))
6157        });
6158        ConditionResult::from(found)
6159    }
6160
6161    /// [352] Wenn SG4 STS++A05/ A06/ A14 /A15 (Status der Antwort) vorhanden
6162    fn evaluate_352(&self, ctx: &EvaluationContext) -> ConditionResult {
6163        let sts_segs = ctx.find_segments("STS");
6164        let found = sts_segs.iter().any(|s| {
6165            s.elements
6166                .get(1)
6167                .and_then(|e| e.first())
6168                .is_some_and(|v| ["A05", "A06", "A14", "A15"].contains(&v.as_str()))
6169        });
6170        ConditionResult::from(found)
6171    }
6172
6173    /// [355] Wenn SG4 STS+E01++A45 (Status der Antwort) vorhanden
6174    fn evaluate_355(&self, ctx: &EvaluationContext) -> ConditionResult {
6175        let sts_segs = ctx.find_segments("STS");
6176        let found = sts_segs.iter().any(|s| {
6177            s.elements
6178                .first()
6179                .and_then(|e| e.first())
6180                .is_some_and(|v| v == "E01")
6181                && s.elements
6182                    .get(2)
6183                    .and_then(|e| e.first())
6184                    .is_some_and(|v| ["A45"].contains(&v.as_str()))
6185        });
6186        ConditionResult::from(found)
6187    }
6188
6189    /// [356] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6190    fn evaluate_356(&self, ctx: &EvaluationContext) -> ConditionResult {
6191        let sts_segs = ctx.find_segments("STS");
6192        let found = sts_segs.iter().any(|s| {
6193            s.elements
6194                .first()
6195                .and_then(|e| e.first())
6196                .is_some_and(|v| v == "E01")
6197                && s.elements
6198                    .get(2)
6199                    .and_then(|e| e.first())
6200                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6201        });
6202        ConditionResult::from(found)
6203    }
6204
6205    /// [357] Wenn SG4 STS+E01++A03/ A09/ A12/ A17 (Status der Antwort) vorhanden
6206    fn evaluate_357(&self, ctx: &EvaluationContext) -> ConditionResult {
6207        let sts_segs = ctx.find_segments("STS");
6208        let found = sts_segs.iter().any(|s| {
6209            s.elements
6210                .first()
6211                .and_then(|e| e.first())
6212                .is_some_and(|v| v == "E01")
6213                && s.elements
6214                    .get(2)
6215                    .and_then(|e| e.first())
6216                    .is_some_and(|v| ["A03", "A09", "A12", "A17"].contains(&v.as_str()))
6217        });
6218        ConditionResult::from(found)
6219    }
6220
6221    /// [358] Wenn SG4 STS+E01++A06 (Status der Antwort) vorhanden
6222    fn evaluate_358(&self, ctx: &EvaluationContext) -> ConditionResult {
6223        let sts_segs = ctx.find_segments("STS");
6224        let found = sts_segs.iter().any(|s| {
6225            s.elements
6226                .first()
6227                .and_then(|e| e.first())
6228                .is_some_and(|v| v == "E01")
6229                && s.elements
6230                    .get(2)
6231                    .and_then(|e| e.first())
6232                    .is_some_and(|v| ["A06"].contains(&v.as_str()))
6233        });
6234        ConditionResult::from(found)
6235    }
6236
6237    /// [359] Es sind nur Antwortcodes aus dem Cluster Ablehnung erlaubt
6238    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6239    // Preserve across regeneration until the codegen prompt is updated.
6240    fn evaluate_359(&self, ctx: &EvaluationContext) -> ConditionResult {
6241        all_sts_e01_in_cluster(ctx, Cluster::is_ablehnung)
6242    }
6243
6244    /// [360] Es sind nur Antwortcodes aus dem Cluster Zustimmung erlaubt
6245    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6246    // Preserve across regeneration until the codegen prompt is updated.
6247    fn evaluate_360(&self, ctx: &EvaluationContext) -> ConditionResult {
6248        all_sts_e01_in_cluster(ctx, Cluster::is_zustimmung)
6249    }
6250
6251    /// [363] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6252    fn evaluate_363(&self, ctx: &EvaluationContext) -> ConditionResult {
6253        let sts_segs = ctx.find_segments("STS");
6254        let found = sts_segs.iter().any(|s| {
6255            s.elements
6256                .first()
6257                .and_then(|e| e.first())
6258                .is_some_and(|v| v == "E01")
6259                && s.elements
6260                    .get(2)
6261                    .and_then(|e| e.first())
6262                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6263        });
6264        ConditionResult::from(found)
6265    }
6266
6267    /// [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
6268    fn evaluate_365(&self, ctx: &EvaluationContext) -> ConditionResult {
6269        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6270        // Check: STS+E01 does NOT have A04 with E_0047/E_0014 or A03 with E_0049/E_0004
6271        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6272        let has_excluded = sts_segs.iter().any(|s| {
6273            let code = s
6274                .elements
6275                .get(2)
6276                .and_then(|e| e.first())
6277                .map(|v| v.as_str());
6278            let ebd = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
6279            matches!(
6280                (code, ebd),
6281                (Some("A04"), Some("E_0047"))
6282                    | (Some("A04"), Some("E_0014"))
6283                    | (Some("A03"), Some("E_0049"))
6284                    | (Some("A03"), Some("E_0004"))
6285            )
6286        });
6287        ConditionResult::from(!has_excluded)
6288    }
6289
6290    /// [366] Bis auf den Code A30 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6291    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6292    // Preserve across regeneration until the codegen prompt is updated.
6293    fn evaluate_366(&self, ctx: &EvaluationContext) -> ConditionResult {
6294        ebd_ablehnung_except(ctx, "E_0624", &["A30"])
6295    }
6296
6297    /// [367] Wenn SG4 STS+E01++A05/ A14 (Status der Antwort) vorhanden
6298    fn evaluate_367(&self, ctx: &EvaluationContext) -> ConditionResult {
6299        let sts_segs = ctx.find_segments("STS");
6300        let found = sts_segs.iter().any(|s| {
6301            s.elements
6302                .first()
6303                .and_then(|e| e.first())
6304                .is_some_and(|v| v == "E01")
6305                && s.elements
6306                    .get(2)
6307                    .and_then(|e| e.first())
6308                    .is_some_and(|v| ["A05", "A14"].contains(&v.as_str()))
6309        });
6310        ConditionResult::from(found)
6311    }
6312
6313    /// [368] Bis auf den Code A41 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6314    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6315    // Preserve across regeneration until the codegen prompt is updated.
6316    fn evaluate_368(&self, ctx: &EvaluationContext) -> ConditionResult {
6317        ebd_ablehnung_except(ctx, "E_0624", &["A41"])
6318    }
6319
6320    /// [370] Wenn SG10 CAV+TLS/TES/BIT/GET/GAT/SOT/WNT/WFT/WAT
6321    fn evaluate_370(&self, ctx: &EvaluationContext) -> ConditionResult {
6322        let cav_segs = ctx.find_segments("CAV");
6323        let found = cav_segs.iter().any(|s| {
6324            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6325                [
6326                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
6327                ]
6328                .contains(&v.as_str())
6329            })
6330        });
6331        ConditionResult::from(found)
6332    }
6333
6334    /// [371] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
6335    fn evaluate_371(&self, ctx: &EvaluationContext) -> ConditionResult {
6336        let seq_segs = ctx.find_segments("SEQ");
6337        let found = seq_segs.iter().any(|s| {
6338            s.elements
6339                .first()
6340                .and_then(|e| e.first())
6341                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
6342        });
6343        ConditionResult::from(!found)
6344    }
6345
6346    /// [372] Wenn SG10 CAV+TES/BIT/GET/GAT/SOT/WNT/WFT/WAT vorhanden
6347    fn evaluate_372(&self, ctx: &EvaluationContext) -> ConditionResult {
6348        let cav_segs = ctx.find_segments("CAV");
6349        let found = cav_segs.iter().any(|s| {
6350            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6351                ["TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT"].contains(&v.as_str())
6352            })
6353        });
6354        ConditionResult::from(found)
6355    }
6356
6357    /// [373] Wenn ein Referenzprofil an der Marktlokation vorhanden ist
6358    fn evaluate_373(&self, _ctx: &EvaluationContext) -> ConditionResult {
6359        // TODO: implement
6360        ConditionResult::Unknown
6361    }
6362
6363    /// [375] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z24 (Daten der Überführungszeitreihe) nicht vorhanden
6364    fn evaluate_375(&self, ctx: &EvaluationContext) -> ConditionResult {
6365        // Check: no SG8 SEQ+Z24 (Daten der Überführungszeitreihe) exists
6366        ctx.lacks_qualifier("SEQ", 0, "Z24")
6367    }
6368
6369    /// [376] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z22 (Daten der Summenzeitreihe) nicht vorhanden
6370    fn evaluate_376(&self, ctx: &EvaluationContext) -> ConditionResult {
6371        // Check: no SG8 SEQ+Z22 (Daten der Summenzeitreihe) exists
6372        ctx.lacks_qualifier("SEQ", 0, "Z22")
6373    }
6374
6375    /// [377] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS vorhanden
6376    // 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)
6377    fn evaluate_377(&self, ctx: &EvaluationContext) -> ConditionResult {
6378        let nav = match ctx.navigator {
6379            Some(n) => n,
6380            None => return ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]),
6381        };
6382        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6383        for i in 0..sg8_count {
6384            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6385            for j in 0..sg10_count {
6386                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6387                let has_cci = ccis.iter().any(|s| {
6388                    s.elements
6389                        .first()
6390                        .and_then(|e: &Vec<String>| e.first())
6391                        .is_some_and(|v: &String| v == "15")
6392                        && s.elements
6393                            .get(2)
6394                            .and_then(|e: &Vec<String>| e.first())
6395                            .is_some_and(|v: &String| v == "Z21")
6396                });
6397                if has_cci {
6398                    let cavs =
6399                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6400                    if cavs.iter().any(|s| {
6401                        s.elements
6402                            .first()
6403                            .and_then(|e: &Vec<String>| e.first())
6404                            .is_some_and(|v: &String| v == "AUS")
6405                    }) {
6406                        return ConditionResult::True;
6407                    }
6408                }
6409            }
6410        }
6411        ConditionResult::False
6412    }
6413
6414    /// [378] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS nicht vorhanden
6415    // 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)
6416    fn evaluate_378(&self, ctx: &EvaluationContext) -> ConditionResult {
6417        let nav = match ctx.navigator {
6418            Some(n) => n,
6419            None => {
6420                let r = ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]);
6421                return match r {
6422                    ConditionResult::True => ConditionResult::False,
6423                    ConditionResult::False => ConditionResult::True,
6424                    ConditionResult::Unknown => ConditionResult::Unknown,
6425                };
6426            }
6427        };
6428        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6429        for i in 0..sg8_count {
6430            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6431            for j in 0..sg10_count {
6432                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6433                let has_cci = ccis.iter().any(|s| {
6434                    s.elements
6435                        .first()
6436                        .and_then(|e: &Vec<String>| e.first())
6437                        .is_some_and(|v: &String| v == "15")
6438                        && s.elements
6439                            .get(2)
6440                            .and_then(|e: &Vec<String>| e.first())
6441                            .is_some_and(|v: &String| v == "Z21")
6442                });
6443                if has_cci {
6444                    let cavs =
6445                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6446                    if cavs.iter().any(|s| {
6447                        s.elements
6448                            .first()
6449                            .and_then(|e: &Vec<String>| e.first())
6450                            .is_some_and(|v: &String| v == "AUS")
6451                    }) {
6452                        return ConditionResult::False;
6453                    }
6454                }
6455            }
6456        }
6457        ConditionResult::True
6458    }
6459
6460    /// [379] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+ Code für EEG-Überführungszeitreihen (ausgenommen AU1) vorhanden
6461    fn evaluate_379(&self, ctx: &EvaluationContext) -> ConditionResult {
6462        let cci_segs = ctx.find_segments("CCI");
6463        let found = cci_segs.iter().any(|s| {
6464            s.elements
6465                .first()
6466                .and_then(|e| e.first())
6467                .is_some_and(|v| v == "15")
6468                && s.elements
6469                    .get(2)
6470                    .and_then(|e| e.first())
6471                    .is_some_and(|v| ["Z21"].contains(&v.as_str()))
6472        });
6473        ConditionResult::from(found)
6474    }
6475
6476    /// [380] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS/AU1 vorhanden
6477    // 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)
6478    fn evaluate_380(&self, ctx: &EvaluationContext) -> ConditionResult {
6479        let nav = match ctx.navigator {
6480            Some(n) => n,
6481            None => {
6482                return ctx.any_group_has_any_qualifier("CAV", 0, &["AUS", "AU1"], &["SG4", "SG8"])
6483            }
6484        };
6485        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6486        for i in 0..sg8_count {
6487            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6488            for j in 0..sg10_count {
6489                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6490                let has_cci = ccis.iter().any(|s| {
6491                    s.elements
6492                        .first()
6493                        .and_then(|e: &Vec<String>| e.first())
6494                        .is_some_and(|v: &String| v == "15")
6495                        && s.elements
6496                            .get(2)
6497                            .and_then(|e: &Vec<String>| e.first())
6498                            .is_some_and(|v: &String| v == "Z21")
6499                });
6500                if has_cci {
6501                    let cavs =
6502                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6503                    if cavs.iter().any(|s| {
6504                        s.elements
6505                            .first()
6506                            .and_then(|e: &Vec<String>| e.first())
6507                            .is_some_and(|v: &String| v == "AUS" || v == "AU1")
6508                    }) {
6509                        return ConditionResult::True;
6510                    }
6511                }
6512            }
6513        }
6514        ConditionResult::False
6515    }
6516
6517    /// [384] Wenn in derselben SG8 SG10 CCI+Z37++ZD1 (Basis zur Bildung der Tranchengröße) (Prozentual) vorhanden
6518    fn evaluate_384(&self, ctx: &EvaluationContext) -> ConditionResult {
6519        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6520        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6521        let found = cci_segs.iter().any(|s| {
6522            s.elements
6523                .first()
6524                .and_then(|e| e.first())
6525                .is_some_and(|v| v == "Z37")
6526                && s.elements
6527                    .get(2)
6528                    .and_then(|e| e.first())
6529                    .is_some_and(|v| v == "ZD1")
6530        });
6531        ConditionResult::from(found)
6532    }
6533
6534    /// [386] Wenn mehr als eine SG8 SEQ+Z02/ ZE3 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden
6535    fn evaluate_386(&self, ctx: &EvaluationContext) -> ConditionResult {
6536        // Check: more than one SG8 SEQ+Z02/ZE3 with OBIS energy quantity in PIA+5
6537        let seq_count = ctx
6538            .find_segments("SEQ")
6539            .iter()
6540            .filter(|s| {
6541                s.elements
6542                    .first()
6543                    .and_then(|e| e.first())
6544                    .is_some_and(|v| v == "Z02" || v == "ZE3")
6545            })
6546            .count();
6547        ConditionResult::from(seq_count > 1)
6548    }
6549
6550    /// [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...
6551    fn evaluate_387(&self, ctx: &EvaluationContext) -> ConditionResult {
6552        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6553        // Check: same SG8 (OBIS Daten) has OBIS energy quantity code in PIA+5
6554        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6555        let found = pia_segs.iter().any(|s| {
6556            s.elements
6557                .first()
6558                .and_then(|e| e.first())
6559                .is_some_and(|v| v == "5")
6560                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6561                    v.starts_with("1-")
6562                        && (v.contains(":1.8.")
6563                            || v.contains(":2.8.")
6564                            || v.contains(":1.9.")
6565                            || v.contains(":2.9."))
6566                })
6567        });
6568        ConditionResult::from(found)
6569    }
6570
6571    /// [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...
6572    fn evaluate_388(&self, ctx: &EvaluationContext) -> ConditionResult {
6573        // Check: at least one RFF+Z49 (Zeitraum) exists
6574        let found = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6575        ConditionResult::from(found)
6576    }
6577
6578    /// [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...
6579    fn evaluate_391(&self, ctx: &EvaluationContext) -> ConditionResult {
6580        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6581        // Check: OBIS Zählerstand (meter reading) code in PIA+5 within SEQ+Z20
6582        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6583        let found = pia_segs.iter().any(|s| {
6584            s.elements
6585                .first()
6586                .and_then(|e| e.first())
6587                .is_some_and(|v| v == "5")
6588                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6589                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
6590                })
6591        });
6592        ConditionResult::from(found)
6593    }
6594
6595    /// [392] Wenn in diesem PIA der Code im Format n1-n2-n1 angegeben ist
6596    fn evaluate_392(&self, ctx: &EvaluationContext) -> ConditionResult {
6597        // Check: PIA code in format n1-n2-n1 (e.g., "1-08-1")
6598        let pia_segs = ctx.find_segments("PIA");
6599        let found = pia_segs.iter().any(|s| {
6600            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6601                let parts: Vec<&str> = v.split('-').collect();
6602                parts.len() == 3
6603                    && parts[0].len() == 1
6604                    && parts[0].chars().all(|c| c.is_ascii_digit())
6605                    && parts[1].len() == 2
6606                    && parts[1].chars().all(|c| c.is_ascii_digit())
6607                    && parts[2].len() == 1
6608                    && parts[2].chars().all(|c| c.is_ascii_digit())
6609            })
6610        });
6611        ConditionResult::from(found)
6612    }
6613
6614    /// [393] Wenn in diesem PIA der Code im Format n1-n2-n1-n3 angegeben ist
6615    fn evaluate_393(&self, ctx: &EvaluationContext) -> ConditionResult {
6616        // Check: PIA code in format n1-n2-n1-n3 (e.g., "1-08-1-001")
6617        let pia_segs = ctx.find_segments("PIA");
6618        let found = pia_segs.iter().any(|s| {
6619            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6620                let parts: Vec<&str> = v.split('-').collect();
6621                parts.len() == 4
6622                    && parts[0].len() == 1
6623                    && parts[0].chars().all(|c| c.is_ascii_digit())
6624                    && parts[1].len() == 2
6625                    && parts[1].chars().all(|c| c.is_ascii_digit())
6626                    && parts[2].len() == 1
6627                    && parts[2].chars().all(|c| c.is_ascii_digit())
6628                    && parts[3].len() == 3
6629                    && parts[3].chars().all(|c| c.is_ascii_digit())
6630            })
6631        });
6632        ConditionResult::from(found)
6633    }
6634
6635    /// [394] Wenn in diesem PIA der Code im Format n1-n2-n1-n8-n2 angegeben ist
6636    fn evaluate_394(&self, ctx: &EvaluationContext) -> ConditionResult {
6637        // Check: PIA code in format n1-n2-n1-n8-n2
6638        let pia_segs = ctx.find_segments("PIA");
6639        let found = pia_segs.iter().any(|s| {
6640            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6641                let parts: Vec<&str> = v.split('-').collect();
6642                parts.len() == 5
6643                    && parts[0].len() == 1
6644                    && parts[0].chars().all(|c| c.is_ascii_digit())
6645                    && parts[1].len() == 2
6646                    && parts[1].chars().all(|c| c.is_ascii_digit())
6647                    && parts[2].len() == 1
6648                    && parts[2].chars().all(|c| c.is_ascii_digit())
6649                    && parts[3].len() == 8
6650                    && parts[3].chars().all(|c| c.is_ascii_digit())
6651                    && parts[4].len() == 2
6652                    && parts[4].chars().all(|c| c.is_ascii_digit())
6653            })
6654        });
6655        ConditionResult::from(found)
6656    }
6657
6658    /// [395] Wenn in diesem PIA der Code im Format n1-n2-n1-n8 angegeben ist
6659    fn evaluate_395(&self, ctx: &EvaluationContext) -> ConditionResult {
6660        // Check: PIA code in format n1-n2-n1-n8
6661        let pia_segs = ctx.find_segments("PIA");
6662        let found = pia_segs.iter().any(|s| {
6663            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6664                let parts: Vec<&str> = v.split('-').collect();
6665                parts.len() == 4
6666                    && parts[0].len() == 1
6667                    && parts[0].chars().all(|c| c.is_ascii_digit())
6668                    && parts[1].len() == 2
6669                    && parts[1].chars().all(|c| c.is_ascii_digit())
6670                    && parts[2].len() == 1
6671                    && parts[2].chars().all(|c| c.is_ascii_digit())
6672                    && parts[3].len() == 8
6673                    && parts[3].chars().all(|c| c.is_ascii_digit())
6674            })
6675        });
6676        ConditionResult::from(found)
6677    }
6678
6679    /// [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
6680    fn evaluate_396(&self, ctx: &EvaluationContext) -> ConditionResult {
6681        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6682        // Check: OBIS Wirkarbeit und 1/4 Stunde code in PIA+5
6683        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6684        let found = pia_segs.iter().any(|s| {
6685            s.elements
6686                .first()
6687                .and_then(|e| e.first())
6688                .is_some_and(|v| v == "5")
6689                && s.elements
6690                    .get(1)
6691                    .and_then(|e| e.first())
6692                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
6693        });
6694        ConditionResult::from(found)
6695    }
6696
6697    /// [397] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/1-08-4 beginnt
6698    fn evaluate_397(&self, ctx: &EvaluationContext) -> ConditionResult {
6699        let pia_segs = ctx.find_segments("PIA");
6700        let found = pia_segs.iter().any(|s| {
6701            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6702                let prefixes = ["1-08-1", "1-08-4"];
6703                prefixes.iter().any(|p| v.starts_with(p))
6704            })
6705        });
6706        ConditionResult::from(found)
6707    }
6708
6709    /// [398] Wenn in diesem PIA der Code im DE7140 mit 1-08-2/1-08-5 beginnt
6710    fn evaluate_398(&self, ctx: &EvaluationContext) -> ConditionResult {
6711        let pia_segs = ctx.find_segments("PIA");
6712        let found = pia_segs.iter().any(|s| {
6713            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6714                let prefixes = ["1-08-2", "1-08-5"];
6715                prefixes.iter().any(|p| v.starts_with(p))
6716            })
6717        });
6718        ConditionResult::from(found)
6719    }
6720
6721    /// [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...
6722    // 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)
6723    fn evaluate_399(&self, ctx: &EvaluationContext) -> ConditionResult {
6724        let nav = match ctx.navigator() {
6725            Some(n) => n,
6726            None => return ConditionResult::Unknown,
6727        };
6728        // Collect DE3224 values from LOC segments in SG5 — LOC.C517.DE3224 = elements[1][3]
6729        // These serve as the Zeitraum-IDs referenced by the condition's "dieses Segments"
6730        let sg5_count = nav.group_instance_count(&["SG4", "SG5"]);
6731        let mut loc_zeitraum_ids: Vec<String> = Vec::new();
6732        for i in 0..sg5_count {
6733            let locs = nav.find_segments_in_group("LOC", &["SG4", "SG5"], i);
6734            for loc in &locs {
6735                if let Some(zid) = loc.elements.get(1).and_then(|e| e.get(3)) {
6736                    if !zid.is_empty() {
6737                        loc_zeitraum_ids.push(zid.clone());
6738                    }
6739                }
6740            }
6741        }
6742        if loc_zeitraum_ids.is_empty() {
6743            return ConditionResult::Unknown;
6744        }
6745        let pauschal_id = "9992000000018";
6746        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6747        // Find any Z78 SG8 whose Zeitraum-ID (SEQ.C286.DE1050) matches a LOC DE3224
6748        // and that has RFF+Z31 with DE1154 != pauschal_id
6749        for i in 0..sg8_count {
6750            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
6751            for seq in &seqs {
6752                if !seq
6753                    .elements
6754                    .first()
6755                    .and_then(|e| e.first())
6756                    .is_some_and(|v| v == "Z78")
6757                {
6758                    continue;
6759                }
6760                let zid = seq
6761                    .elements
6762                    .get(1)
6763                    .and_then(|e| e.first())
6764                    .map(|s| s.as_str())
6765                    .unwrap_or("");
6766                if !loc_zeitraum_ids.iter().any(|z| z == zid) {
6767                    continue;
6768                }
6769                // Matching Z78 SG8 found — check RFF+Z31 DE1154
6770                let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
6771                for rff in &rffs {
6772                    let qual = rff
6773                        .elements
6774                        .first()
6775                        .and_then(|e| e.first())
6776                        .map(|v| v.as_str())
6777                        .unwrap_or("");
6778                    if qual == "Z31" {
6779                        let id = rff
6780                            .elements
6781                            .first()
6782                            .and_then(|e| e.get(1))
6783                            .map(|v| v.as_str())
6784                            .unwrap_or("");
6785                        let normalized = id.replace(' ', "");
6786                        if !normalized.is_empty() && normalized != pauschal_id {
6787                            return ConditionResult::True;
6788                        }
6789                    }
6790                }
6791            }
6792        }
6793        ConditionResult::False
6794    }
6795
6796    /// [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+...
6797    fn evaluate_401(&self, ctx: &EvaluationContext) -> ConditionResult {
6798        // Check: this DTM+Z25 is not in SG6 RFF+Z49 or Z53
6799        // Approximate: check if RFF+Z49 or Z53 exists
6800        let has_z49 = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6801        let has_z53 = !ctx.find_segments_with_qualifier("RFF", 0, "Z53").is_empty();
6802        ConditionResult::from(!has_z49 && !has_z53)
6803    }
6804
6805    /// [402] Wenn in derselben SG8  (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl der Werteart \"Zählerstand\" im PIA+5 vorhanden
6806    fn evaluate_402(&self, ctx: &EvaluationContext) -> ConditionResult {
6807        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6808        // Check: OBIS Zählerstand code in PIA+5
6809        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6810        let found = pia_segs.iter().any(|s| {
6811            s.elements
6812                .first()
6813                .and_then(|e| e.first())
6814                .is_some_and(|v| v == "5")
6815                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6816                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
6817                })
6818        });
6819        ConditionResult::from(found)
6820    }
6821
6822    /// [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
6823    // 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)
6824    fn evaluate_403(&self, ctx: &EvaluationContext) -> ConditionResult {
6825        let sender_mp_id: String = match ctx.find_segments_with_qualifier("NAD", 0, "MS").first() {
6826            Some(nad) => match nad.elements.get(1).and_then(|e: &Vec<String>| e.first()) {
6827                Some(id) if !id.is_empty() => id.clone(),
6828                _ => return ConditionResult::Unknown,
6829            },
6830            None => return ConditionResult::Unknown,
6831        };
6832        let nav = match ctx.navigator {
6833            Some(n) => n,
6834            None => return ConditionResult::Unknown,
6835        };
6836        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6837        for i in 0..sg8_count {
6838            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6839            for j in 0..sg10_count {
6840                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6841                let has_cci_zb3 = ccis.iter().any(|s| {
6842                    s.elements
6843                        .get(2)
6844                        .and_then(|e: &Vec<String>| e.first())
6845                        .is_some_and(|v: &String| v == "ZB3")
6846                });
6847                if !has_cci_zb3 {
6848                    continue;
6849                }
6850                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6851                for cav in &cavs {
6852                    if cav
6853                        .elements
6854                        .first()
6855                        .and_then(|e: &Vec<String>| e.first())
6856                        .is_some_and(|v: &String| v == "Z91")
6857                    {
6858                        if let Some(msb_id) = cav.elements.first().and_then(|e| e.get(1)) {
6859                            if !msb_id.is_empty() {
6860                                return ConditionResult::from(
6861                                    sender_mp_id.as_str() != msb_id.as_str(),
6862                                );
6863                            }
6864                        }
6865                    }
6866                }
6867            }
6868        }
6869        ConditionResult::Unknown
6870    }
6871
6872    /// [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
6873    // 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)
6874    fn evaluate_404(&self, ctx: &EvaluationContext) -> ConditionResult {
6875        let mr_nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
6876        let mr_mp_id = match mr_nads.first() {
6877            Some(nad) => nad
6878                .elements
6879                .get(1)
6880                .and_then(|e: &Vec<String>| e.first())
6881                .cloned()
6882                .unwrap_or_default(),
6883            None => return ConditionResult::Unknown,
6884        };
6885        if mr_mp_id.is_empty() {
6886            return ConditionResult::Unknown;
6887        }
6888
6889        let nav = match ctx.navigator {
6890            Some(n) => n,
6891            None => {
6892                let cavs = ctx.find_segments("CAV");
6893                let same_id_exists = cavs.iter().any(|s| {
6894                    s.elements
6895                        .first()
6896                        .and_then(|e: &Vec<String>| e.first())
6897                        .is_some_and(|v: &String| v == "Z91")
6898                        && s.elements
6899                            .first()
6900                            .and_then(|e| e.get(1))
6901                            .is_some_and(|v: &String| v == &mr_mp_id)
6902                });
6903                return ConditionResult::from(!same_id_exists);
6904            }
6905        };
6906
6907        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6908        for i in 0..sg8_count {
6909            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6910            for j in 0..sg10_count {
6911                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6912                let has_cci_zb3 = ccis.iter().any(|s| {
6913                    s.elements
6914                        .get(2)
6915                        .and_then(|e: &Vec<String>| e.first())
6916                        .is_some_and(|v: &String| v == "ZB3")
6917                });
6918                if !has_cci_zb3 {
6919                    continue;
6920                }
6921                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6922                for cav in &cavs {
6923                    if cav
6924                        .elements
6925                        .first()
6926                        .and_then(|e: &Vec<String>| e.first())
6927                        .is_some_and(|v: &String| v == "Z91")
6928                    {
6929                        let cav_mp_id = cav
6930                            .elements
6931                            .first()
6932                            .and_then(|e| e.get(1))
6933                            .map(|s| s.as_str())
6934                            .unwrap_or("");
6935                        if cav_mp_id == mr_mp_id.as_str() {
6936                            return ConditionResult::False;
6937                        }
6938                    }
6939                }
6940            }
6941        }
6942
6943        ConditionResult::True
6944    }
6945
6946    /// [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
6947    fn evaluate_405(&self, ctx: &EvaluationContext) -> ConditionResult {
6948        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6949        // Check: OBIS Wirkarbeit kumuliert code in PIA+5
6950        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6951        let found = pia_segs.iter().any(|s| {
6952            s.elements
6953                .first()
6954                .and_then(|e| e.first())
6955                .is_some_and(|v| v == "5")
6956                && s.elements
6957                    .get(1)
6958                    .and_then(|e| e.first())
6959                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
6960        });
6961        ConditionResult::from(found)
6962    }
6963
6964    /// [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
6965    fn evaluate_406(&self, ctx: &EvaluationContext) -> ConditionResult {
6966        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6967        // Check: OBIS Wirkarbeit höchste 1/4 Stunde code in PIA+5
6968        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6969        let found = pia_segs.iter().any(|s| {
6970            s.elements
6971                .first()
6972                .and_then(|e| e.first())
6973                .is_some_and(|v| v == "5")
6974                && s.elements
6975                    .get(1)
6976                    .and_then(|e| e.first())
6977                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
6978        });
6979        ConditionResult::from(found)
6980    }
6981
6982    /// [407] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Blindarbeit im PIA+5 vorhanden
6983    fn evaluate_407(&self, ctx: &EvaluationContext) -> ConditionResult {
6984        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6985        // Check: OBIS Blindarbeit code in PIA+5
6986        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6987        let found = pia_segs.iter().any(|s| {
6988            s.elements
6989                .first()
6990                .and_then(|e| e.first())
6991                .is_some_and(|v| v == "5")
6992                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6993                    v.starts_with("1-") && (v.contains(":3.8.") || v.contains(":4.8."))
6994                })
6995        });
6996        ConditionResult::from(found)
6997    }
6998
6999    /// [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...
7000    fn evaluate_408(&self, ctx: &EvaluationContext) -> ConditionResult {
7001        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7002        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7003        ConditionResult::from(has_seq_z02)
7004    }
7005
7006    /// [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 ...
7007    fn evaluate_409(&self, ctx: &EvaluationContext) -> ConditionResult {
7008        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7009        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7010        ConditionResult::from(!has_seq_z02)
7011    }
7012
7013    /// [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...
7014    fn evaluate_410(&self, ctx: &EvaluationContext) -> ConditionResult {
7015        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7016        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7017        ConditionResult::from(has_seq_z02)
7018    }
7019
7020    /// [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 ...
7021    fn evaluate_411(&self, ctx: &EvaluationContext) -> ConditionResult {
7022        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7023        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7024        ConditionResult::from(!has_seq_z02)
7025    }
7026
7027    /// [412] Es sind ausschließlich Konfigurationsprodukt-Codes der \"EDI@Energy Codeliste der Konfigurationen\" aus Kapitel 4.2 Konfigurationsprodukte Leistungskurvendefinition erlaubt
7028    fn evaluate_412(&self, ctx: &EvaluationContext) -> ConditionResult {
7029        ctx.external.evaluate("code_list_membership_check")
7030    }
7031
7032    /// [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
7033    fn evaluate_413(&self, ctx: &EvaluationContext) -> ConditionResult {
7034        // Check: SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7035        let found = ctx.find_segments("SEQ").iter().any(|s| {
7036            s.elements
7037                .first()
7038                .and_then(|e| e.first())
7039                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7040        });
7041        ConditionResult::from(found)
7042    }
7043
7044    /// [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
7045    fn evaluate_414(&self, ctx: &EvaluationContext) -> ConditionResult {
7046        // Check: no SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7047        let found = ctx.find_segments("SEQ").iter().any(|s| {
7048            s.elements
7049                .first()
7050                .and_then(|e| e.first())
7051                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7052        });
7053        ConditionResult::from(!found)
7054    }
7055
7056    /// [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
7057    fn evaluate_415(&self, ctx: &EvaluationContext) -> ConditionResult {
7058        // Check: another SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7059        let count = ctx
7060            .find_segments("SEQ")
7061            .iter()
7062            .filter(|s| {
7063                s.elements
7064                    .first()
7065                    .and_then(|e| e.first())
7066                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7067            })
7068            .count();
7069        ConditionResult::from(count > 1)
7070    }
7071
7072    /// [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
7073    fn evaluate_416(&self, ctx: &EvaluationContext) -> ConditionResult {
7074        // Check: no other SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7075        let count = ctx
7076            .find_segments("SEQ")
7077            .iter()
7078            .filter(|s| {
7079                s.elements
7080                    .first()
7081                    .and_then(|e| e.first())
7082                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7083            })
7084            .count();
7085        ConditionResult::from(count <= 1)
7086    }
7087
7088    /// [417] Wenn für den erforderlichen Wert keine Zählzeit benötigt wird
7089    fn evaluate_417(&self, _ctx: &EvaluationContext) -> ConditionResult {
7090        // TODO: implement
7091        ConditionResult::Unknown
7092    }
7093
7094    /// [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
7095    fn evaluate_419(&self, _ctx: &EvaluationContext) -> ConditionResult {
7096        // TODO: implement
7097        ConditionResult::Unknown
7098    }
7099
7100    /// [420] Wenn in dieser SG8 das RFF+Z14 (Smartmeter-Gateway) vorhanden ist
7101    fn evaluate_420(&self, ctx: &EvaluationContext) -> ConditionResult {
7102        // HAND-EDITED: "in dieser SG8" — scoped to the enclosing SG8.
7103        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7104        let found = rff_segs.iter().any(|s| {
7105            s.elements
7106                .first()
7107                .and_then(|e| e.first())
7108                .is_some_and(|v| v == "Z14")
7109        });
7110        ConditionResult::from(found)
7111    }
7112
7113    /// [421] Wenn in dieser SG8 das SG10 CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden ist
7114    fn evaluate_421(&self, ctx: &EvaluationContext) -> ConditionResult {
7115        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing group.
7116        // Check: CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden
7117        ctx.this_group_has_qualifier("CCI", 0, "Z39", &["SG4", "SG8"])
7118    }
7119
7120    /// [425] Messprodukt-Code aus den Kapitel 2.1 \"Standard-Messprodukte der Marktlokation\" der Codeliste der Konfigurationen
7121    fn evaluate_425(&self, ctx: &EvaluationContext) -> ConditionResult {
7122        ctx.external.evaluate("code_list_membership_check")
7123    }
7124
7125    /// [426] Messprodukt-Code aus den Kapitel 2.2 \"Standard-Messprodukte der Tranche\" der Codeliste der Konfigurationen
7126    fn evaluate_426(&self, ctx: &EvaluationContext) -> ConditionResult {
7127        ctx.external.evaluate("code_list_membership_check")
7128    }
7129
7130    /// [427] Messprodukt-Code aus den Kapitel 2.3 \"Standard-Messprodukte der Messlokation\" der Codeliste der Konfigurationen
7131    fn evaluate_427(&self, ctx: &EvaluationContext) -> ConditionResult {
7132        ctx.external.evaluate("code_list_membership_check")
7133    }
7134
7135    /// [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...
7136    fn evaluate_428(&self, ctx: &EvaluationContext) -> ConditionResult {
7137        ctx.external.evaluate("code_list_membership_check")
7138    }
7139
7140    /// [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...
7141    fn evaluate_429(&self, ctx: &EvaluationContext) -> ConditionResult {
7142        ctx.external.evaluate("code_list_membership_check")
7143    }
7144
7145    /// [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...
7146    fn evaluate_430(&self, ctx: &EvaluationContext) -> ConditionResult {
7147        // Check: another SG8 SEQ+Z27 exists with matching RFF+Z18
7148        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7149        ConditionResult::from(seq_z27_count > 1)
7150    }
7151
7152    /// [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+...
7153    fn evaluate_431(&self, ctx: &EvaluationContext) -> ConditionResult {
7154        // Check: no other SG8 SEQ+Z27 exists with matching RFF+Z18
7155        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7156        ConditionResult::from(seq_z27_count <= 1)
7157    }
7158
7159    /// [432] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
7160    fn evaluate_432(&self, ctx: &EvaluationContext) -> ConditionResult {
7161        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7162        // Check: PIA+5 with code 9991000000078 (Wirkarbeit Lastgang) exists
7163        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7164        let found = pia_segs.iter().any(|s| {
7165            s.elements
7166                .first()
7167                .and_then(|e| e.first())
7168                .is_some_and(|v| v == "5")
7169                && s.elements
7170                    .get(1)
7171                    .and_then(|e| e.first())
7172                    .is_some_and(|v| v == "9991000000078")
7173        });
7174        ConditionResult::from(found)
7175    }
7176
7177    /// [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 ...
7178    fn evaluate_433(&self, ctx: &EvaluationContext) -> ConditionResult {
7179        ctx.external.evaluate("code_list_membership_check")
7180    }
7181
7182    /// [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...
7183    fn evaluate_435(&self, ctx: &EvaluationContext) -> ConditionResult {
7184        ctx.external.evaluate("code_list_membership_check")
7185    }
7186
7187    /// [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
7188    fn evaluate_436(&self, ctx: &EvaluationContext) -> ConditionResult {
7189        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7190        // Check: PIA+5 with code 9991000000086 (Wirkarbeit höchste 1/4h Leistung) exists
7191        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7192        let found = pia_segs.iter().any(|s| {
7193            s.elements
7194                .first()
7195                .and_then(|e| e.first())
7196                .is_some_and(|v| v == "5")
7197                && s.elements
7198                    .get(1)
7199                    .and_then(|e| e.first())
7200                    .is_some_and(|v| v == "9991000000086")
7201        });
7202        ConditionResult::from(found)
7203    }
7204
7205    /// [437] Wenn in dieser SG4 das STS+E01++A04 / A23 (Status der Antwort) vorhanden
7206    fn evaluate_437(&self, ctx: &EvaluationContext) -> ConditionResult {
7207        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
7208        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
7209        let found = sts_segs.iter().any(|s| {
7210            s.elements
7211                .first()
7212                .and_then(|e| e.first())
7213                .is_some_and(|v| v == "E01")
7214                && s.elements
7215                    .get(2)
7216                    .and_then(|e| e.first())
7217                    .is_some_and(|v| ["A04", "A23"].contains(&v.as_str()))
7218        });
7219        ConditionResult::from(found)
7220    }
7221
7222    /// [438] Wenn im selben CCI im DE7059 der Code Z39 (Code der Zählzeit) vorhanden ist
7223    fn evaluate_438(&self, ctx: &EvaluationContext) -> ConditionResult {
7224        // HAND-EDITED: "im selben CCI" — the CCI the field is in (its own segment
7225        // in tree validation), else the enclosing SG10's CCI.
7226        let is_z39 = |elements: &[Vec<String>]| {
7227            elements
7228                .first()
7229                .and_then(|e| e.first())
7230                .is_some_and(|v| v == "Z39")
7231        };
7232        if let Some(own) = ctx.resolved_segment.filter(|_| {
7233            ctx.scoped_find_segments_in("SG10", "CCI")
7234                .iter()
7235                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
7236        }) {
7237            return ConditionResult::from(is_z39(own));
7238        }
7239        ConditionResult::from(
7240            ctx.scoped_find_segments_in("SG10", "CCI")
7241                .iter()
7242                .any(|s| is_z39(&s.elements)),
7243        )
7244    }
7245
7246    /// [440] Wenn in demselben RFF der Code Z35 (vorgelagerte Netzlokation) im DE1153 vorhanden
7247    fn evaluate_440(&self, ctx: &EvaluationContext) -> ConditionResult {
7248        // Check: RFF with qualifier Z35 (vorgelagerte Netzlokation) exists
7249        ctx.has_qualifier("RFF", 0, "Z35")
7250    }
7251
7252    /// [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
7253    fn evaluate_441(&self, ctx: &EvaluationContext) -> ConditionResult {
7254        // Check: SG8 SEQ+Z03/ZF5 device without RFF+Z14 (SMGW reference)
7255        ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
7256    }
7257
7258    /// [442] Wenn in demselben RFF der Code Z34 (vorgelagerte Messlokation) im DE1153 vorhanden
7259    fn evaluate_442(&self, ctx: &EvaluationContext) -> ConditionResult {
7260        // Check: RFF with qualifier Z34 (vorgelagerte Messlokation) exists
7261        ctx.has_qualifier("RFF", 0, "Z34")
7262    }
7263
7264    /// [444] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das PIA+5 (Produkt-Daten der Netzlokation) nicht vorhanden
7265    // 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)
7266    fn evaluate_444(&self, ctx: &EvaluationContext) -> ConditionResult {
7267        for code in ["Z60", "ZE0", "ZG8", "ZG9"] {
7268            if matches!(
7269                ctx.any_group_has_qualifier_without("SEQ", 0, code, "PIA", 0, "5", &["SG4", "SG8"]),
7270                ConditionResult::True
7271            ) {
7272                return ConditionResult::True;
7273            }
7274        }
7275        ConditionResult::False
7276    }
7277
7278    /// [445] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das SG10 CCI+11 (Details zum Produkt der Netzlokation) nicht vorhanden
7279    // 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)
7280    fn evaluate_445(&self, ctx: &EvaluationContext) -> ConditionResult {
7281        let nav = match ctx.navigator {
7282            Some(n) => n,
7283            None => {
7284                let has_cci11 = ["Z60", "ZE0", "ZG8", "ZG9"].iter().any(|code| {
7285                    matches!(
7286                        ctx.filtered_parent_child_has_qualifier(
7287                            &["SG4", "SG8"],
7288                            "SEQ",
7289                            0,
7290                            code,
7291                            "SG10",
7292                            "CCI",
7293                            0,
7294                            "11",
7295                        ),
7296                        ConditionResult::True
7297                    )
7298                });
7299                return ConditionResult::from(!has_cci11);
7300            }
7301        };
7302
7303        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7304        for i in 0..sg8_count {
7305            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7306            let has_cci11 = (0..sg10_count).any(|j| {
7307                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7308                ccis.iter().any(|s| {
7309                    s.elements
7310                        .first()
7311                        .and_then(|e: &Vec<String>| e.first())
7312                        .is_some_and(|v: &String| v == "11")
7313                })
7314            });
7315            if !has_cci11 {
7316                return ConditionResult::True;
7317            }
7318        }
7319        ConditionResult::False
7320    }
7321
7322    /// [446] Wenn in derselben SG8 das CCI+Z17 (Stromverbrauchsart) vorhanden
7323    fn evaluate_446(&self, ctx: &EvaluationContext) -> ConditionResult {
7324        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7325        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7326        let found = cci_segs.iter().any(|s| {
7327            s.elements
7328                .first()
7329                .and_then(|e| e.first())
7330                .is_some_and(|v| v == "Z17")
7331        });
7332        ConditionResult::from(found)
7333    }
7334
7335    /// [447] Wenn in derselben SG8 das CCI+Z50 (Stromerzeugungsart) vorhanden
7336    fn evaluate_447(&self, ctx: &EvaluationContext) -> ConditionResult {
7337        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7338        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7339        let found = cci_segs.iter().any(|s| {
7340            s.elements
7341                .first()
7342                .and_then(|e| e.first())
7343                .is_some_and(|v| v == "Z50")
7344        });
7345        ConditionResult::from(found)
7346    }
7347
7348    /// [448] Wenn in derselben SG8 das CCI+Z56 (Speicher) vorhanden
7349    fn evaluate_448(&self, ctx: &EvaluationContext) -> ConditionResult {
7350        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7351        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7352        let found = cci_segs.iter().any(|s| {
7353            s.elements
7354                .first()
7355                .and_then(|e| e.first())
7356                .is_some_and(|v| v == "Z56")
7357        });
7358        ConditionResult::from(found)
7359    }
7360
7361    /// [449] Wenn in derselben SG8 das SG10 CCI+6++ZA9 CAV=ZG3 (Aggreg.verantw. ÜNB und Lokation im Regelbetrieb) vorhanden
7362    fn evaluate_449(&self, ctx: &EvaluationContext) -> ConditionResult {
7363        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7364        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7365        let has_cci = cci_segs.iter().any(|s| {
7366            s.elements
7367                .first()
7368                .and_then(|e| e.first())
7369                .is_some_and(|v| v == "6")
7370                && s.elements
7371                    .get(2)
7372                    .and_then(|e| e.first())
7373                    .is_some_and(|v| v == "ZA9")
7374        });
7375        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
7376        let has_cav = cav_segs.iter().any(|s| {
7377            s.elements
7378                .first()
7379                .and_then(|e| e.first())
7380                .is_some_and(|v| v == "ZG3")
7381        });
7382        ConditionResult::from(has_cci && has_cav)
7383    }
7384
7385    /// [450] Wenn in demselben RFF der Code Z32 (Netzlokation) im DE1153 vorhanden
7386    fn evaluate_450(&self, ctx: &EvaluationContext) -> ConditionResult {
7387        // Check: RFF with qualifier Z32 (Netzlokation) exists
7388        ctx.has_qualifier("RFF", 0, "Z32")
7389    }
7390
7391    /// [451] Wenn in demselben RFF der Code Z18 (Marktlokation) im DE1153 vorhanden
7392    fn evaluate_451(&self, ctx: &EvaluationContext) -> ConditionResult {
7393        // Check: RFF with qualifier Z18 (Marktlokation) exists
7394        ctx.has_qualifier("RFF", 0, "Z18")
7395    }
7396
7397    /// [452] Wenn in demselben RFF der Code Z19 (Messlokation) im DE1153 vorhanden
7398    fn evaluate_452(&self, ctx: &EvaluationContext) -> ConditionResult {
7399        // Check: RFF with qualifier Z19 (Messlokation) exists
7400        ctx.has_qualifier("RFF", 0, "Z19")
7401    }
7402
7403    /// [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...
7404    fn evaluate_453(&self, ctx: &EvaluationContext) -> ConditionResult {
7405        ctx.external.evaluate("code_list_membership_check")
7406    }
7407
7408    /// [454] Das Bilanzierungsbeginn Datum muss kleiner sein als das Bilanzierungsende Datum in der SG8 „Datenstand des ÜNB", SG 9 Energiemenge DZÜ Anteil
7409    fn evaluate_454(&self, _ctx: &EvaluationContext) -> ConditionResult {
7410        // Check: begin date < end date (structural — always true for valid messages)
7411        ConditionResult::True
7412    }
7413
7414    /// [455] Das Bilanzierungsende-Datum muss größer sein, als das Bilanzierungsbeginn Datum in der SG8 „Datenstand des ÜNB" , SG 9 Energiemenge DZÜ Anteil
7415    fn evaluate_455(&self, _ctx: &EvaluationContext) -> ConditionResult {
7416        // Check: end date > begin date (structural — always true for valid messages)
7417        ConditionResult::True
7418    }
7419
7420    /// [456] Wenn in demselben RFF der Code Z37 (Technische Ressource) im DE1153 vorhanden
7421    fn evaluate_456(&self, ctx: &EvaluationContext) -> ConditionResult {
7422        // Check: RFF with qualifier Z37 (Technische Ressource) exists
7423        ctx.has_qualifier("RFF", 0, "Z37")
7424    }
7425
7426    /// [457] Wenn bei der Bestellung das Messprodukten 9991000001498 bestellt wurde
7427    fn evaluate_457(&self, ctx: &EvaluationContext) -> ConditionResult {
7428        // Check: PIA+5 with Messprodukt 9991000001498 exists
7429        let pia_segs = ctx.find_segments("PIA");
7430        let found = pia_segs.iter().any(|s| {
7431            s.elements
7432                .first()
7433                .and_then(|e| e.first())
7434                .is_some_and(|v| v == "5")
7435                && s.elements
7436                    .get(1)
7437                    .and_then(|e| e.first())
7438                    .is_some_and(|v| v == "9991000001498")
7439        });
7440        ConditionResult::from(found)
7441    }
7442
7443    /// [458] Wenn bei der Bestellung das Konfigurationsprodukte 9991000000739 bestellt wurde
7444    fn evaluate_458(&self, ctx: &EvaluationContext) -> ConditionResult {
7445        // Check: PIA+5 with Konfigurationsprodukt 9991000000739 exists
7446        let pia_segs = ctx.find_segments("PIA");
7447        let found = pia_segs.iter().any(|s| {
7448            s.elements
7449                .first()
7450                .and_then(|e| e.first())
7451                .is_some_and(|v| v == "5")
7452                && s.elements
7453                    .get(1)
7454                    .and_then(|e| e.first())
7455                    .is_some_and(|v| v == "9991000000739")
7456        });
7457        ConditionResult::from(found)
7458    }
7459
7460    /// [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
7461    fn evaluate_459(&self, ctx: &EvaluationContext) -> ConditionResult {
7462        ctx.external.evaluate("code_list_membership_check")
7463    }
7464
7465    /// [460] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"aus dem SMGW\" bestellt wurde
7466    fn evaluate_460(&self, ctx: &EvaluationContext) -> ConditionResult {
7467        ctx.external.evaluate("code_list_membership_check")
7468    }
7469
7470    /// [461] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden
7471    fn evaluate_461(&self, ctx: &EvaluationContext) -> ConditionResult {
7472        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7473        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7474        let found = cci_segs.iter().any(|s| {
7475            s.elements
7476                .first()
7477                .and_then(|e| e.first())
7478                .is_some_and(|v| v == "Z17")
7479        });
7480        ConditionResult::from(found)
7481    }
7482
7483    /// [462] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden
7484    fn evaluate_462(&self, ctx: &EvaluationContext) -> ConditionResult {
7485        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7486        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7487        let found = cci_segs.iter().any(|s| {
7488            s.elements
7489                .first()
7490                .and_then(|e| e.first())
7491                .is_some_and(|v| v == "Z17")
7492        });
7493        ConditionResult::from(found)
7494    }
7495
7496    /// [463] Wenn in derselben SG10 das CCI+Z61++ZF9 (Kunde erfüllt die Voraussetzung nach EnFG) vorhanden
7497    fn evaluate_463(&self, ctx: &EvaluationContext) -> ConditionResult {
7498        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7499        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7500        let found = cci_segs.iter().any(|s| {
7501            s.elements
7502                .first()
7503                .and_then(|e| e.first())
7504                .is_some_and(|v| v == "Z61")
7505                && s.elements
7506                    .get(2)
7507                    .and_then(|e| e.first())
7508                    .is_some_and(|v| v == "ZF9")
7509        });
7510        ConditionResult::from(found)
7511    }
7512
7513    /// [465] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z38 (Referenz auf die der Technischen Ressource zugeordnete Steuerbare Ressource) vorhanden ist
7514    fn evaluate_465(&self, ctx: &EvaluationContext) -> ConditionResult {
7515        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7516        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7517        let found = rff_segs.iter().any(|s| {
7518            s.elements
7519                .first()
7520                .and_then(|e| e.first())
7521                .is_some_and(|v| v == "Z38")
7522        });
7523        ConditionResult::from(found)
7524    }
7525
7526    /// [466] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z32 (Referenz auf die der Technischen Ressource zugeordneten Netzlokation) vorhanden ist
7527    fn evaluate_466(&self, ctx: &EvaluationContext) -> ConditionResult {
7528        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7529        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7530        let found = rff_segs.iter().any(|s| {
7531            s.elements
7532                .first()
7533                .and_then(|e| e.first())
7534                .is_some_and(|v| v == "Z32")
7535        });
7536        ConditionResult::from(found)
7537    }
7538
7539    /// [467] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden ist
7540    fn evaluate_467(&self, ctx: &EvaluationContext) -> ConditionResult {
7541        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7542        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7543        let found = segs.iter().any(|s| {
7544            s.elements
7545                .first()
7546                .and_then(|e| e.first())
7547                .is_some_and(|v| v == "ZG8")
7548        });
7549        ConditionResult::from(found)
7550    }
7551
7552    /// [468] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH0 (Inbetriebsetzung der TR nach 2023) vorhanden ist
7553    fn evaluate_468(&self, ctx: &EvaluationContext) -> ConditionResult {
7554        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7555        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7556        let found = segs.iter().any(|s| {
7557            s.elements
7558                .first()
7559                .and_then(|e| e.first())
7560                .is_some_and(|v| v == "ZH0")
7561        });
7562        ConditionResult::from(found)
7563    }
7564
7565    /// [469] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden ist
7566    fn evaluate_469(&self, ctx: &EvaluationContext) -> ConditionResult {
7567        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7568        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7569        let found = segs.iter().any(|s| {
7570            s.elements
7571                .first()
7572                .and_then(|e| e.first())
7573                .is_some_and(|v| v == "ZH1")
7574        });
7575        ConditionResult::from(found)
7576    }
7577
7578    /// [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
7579    fn evaluate_470(&self, ctx: &EvaluationContext) -> ConditionResult {
7580        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7581        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7582        let found = segs.iter().any(|s| {
7583            s.elements
7584                .first()
7585                .and_then(|e| e.first())
7586                .is_some_and(|v| v == "ZH5")
7587        });
7588        ConditionResult::from(found)
7589    }
7590
7591    /// [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/ ...
7592    fn evaluate_471(&self, ctx: &EvaluationContext) -> ConditionResult {
7593        // Check: SG6 RFF+Z49/Z53 has Zeitraum-ID > 1 in DE1156
7594        let found = ctx.find_segments("RFF").iter().any(|s| {
7595            let qual = s
7596                .elements
7597                .first()
7598                .and_then(|e| e.first())
7599                .map(|v| v.as_str());
7600            matches!(qual, Some("Z49") | Some("Z53"))
7601                && s.elements
7602                    .first()
7603                    .and_then(|e| e.get(2))
7604                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7605        });
7606        ConditionResult::from(found)
7607    }
7608
7609    /// [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...
7610    fn evaluate_472(&self, ctx: &EvaluationContext) -> ConditionResult {
7611        // Check: SG6 RFF+Z48/Z55 has Zeitraum-ID > 1 in DE1156
7612        let found = ctx.find_segments("RFF").iter().any(|s| {
7613            let qual = s
7614                .elements
7615                .first()
7616                .and_then(|e| e.first())
7617                .map(|v| v.as_str());
7618            matches!(qual, Some("Z48") | Some("Z55"))
7619                && s.elements
7620                    .first()
7621                    .and_then(|e| e.get(2))
7622                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7623        });
7624        ConditionResult::from(found)
7625    }
7626
7627    /// [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...
7628    fn evaluate_473(&self, ctx: &EvaluationContext) -> ConditionResult {
7629        // Check: SG6 RFF+Z47/Z54 has Zeitraum-ID > 1 in DE1156
7630        let found = ctx.find_segments("RFF").iter().any(|s| {
7631            let qual = s
7632                .elements
7633                .first()
7634                .and_then(|e| e.first())
7635                .map(|v| v.as_str());
7636            matches!(qual, Some("Z47") | Some("Z54"))
7637                && s.elements
7638                    .first()
7639                    .and_then(|e| e.get(2))
7640                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7641        });
7642        ConditionResult::from(found)
7643    }
7644
7645    /// [474] Wenn SG4 STS+7++ZC8 (Transaktionsgrund \"Beendigung der Zuordnung\") vorhanden
7646    fn evaluate_474(&self, ctx: &EvaluationContext) -> ConditionResult {
7647        let sts_segs = ctx.find_segments("STS");
7648        let found = sts_segs.iter().any(|s| {
7649            s.elements
7650                .first()
7651                .and_then(|e| e.first())
7652                .is_some_and(|v| v == "7")
7653                && s.elements
7654                    .get(2)
7655                    .and_then(|e| e.first())
7656                    .is_some_and(|v| ["ZC8"].contains(&v.as_str()))
7657        });
7658        ConditionResult::from(found)
7659    }
7660
7661    /// [475] Wenn SG4 STS+7++ZH1 (Transaktionsgrund \"Aufhebung einer zukünftigen Zuordnung wegen Stilllegung\") vorhanden
7662    fn evaluate_475(&self, ctx: &EvaluationContext) -> ConditionResult {
7663        let sts_segs = ctx.find_segments("STS");
7664        let found = sts_segs.iter().any(|s| {
7665            s.elements
7666                .first()
7667                .and_then(|e| e.first())
7668                .is_some_and(|v| v == "7")
7669                && s.elements
7670                    .get(2)
7671                    .and_then(|e| e.first())
7672                    .is_some_and(|v| ["ZH1"].contains(&v.as_str()))
7673        });
7674        ConditionResult::from(found)
7675    }
7676
7677    /// [476] Wenn SG4 STS+7++xxx+ZW0 (Transaktionsgrundergänzung Geschäftsvorfall 1) vorhanden
7678    fn evaluate_476(&self, ctx: &EvaluationContext) -> ConditionResult {
7679        let sts_segs = ctx.find_segments("STS");
7680        let found = sts_segs.iter().any(|s| {
7681            s.elements
7682                .first()
7683                .and_then(|e| e.first())
7684                .is_some_and(|v| v == "7")
7685                && s.elements
7686                    .get(3)
7687                    .and_then(|e| e.first())
7688                    .is_some_and(|v| ["ZW0"].contains(&v.as_str()))
7689        });
7690        ConditionResult::from(found)
7691    }
7692
7693    /// [477] Wenn SG4 STS+7++xxx+ZW1 (Transaktionsgrundergänzung Geschäftsvorfall 2) vorhanden
7694    fn evaluate_477(&self, ctx: &EvaluationContext) -> ConditionResult {
7695        let sts_segs = ctx.find_segments("STS");
7696        let found = sts_segs.iter().any(|s| {
7697            s.elements
7698                .first()
7699                .and_then(|e| e.first())
7700                .is_some_and(|v| v == "7")
7701                && s.elements
7702                    .get(3)
7703                    .and_then(|e| e.first())
7704                    .is_some_and(|v| ["ZW1"].contains(&v.as_str()))
7705        });
7706        ConditionResult::from(found)
7707    }
7708
7709    /// [478] Wenn SG4 STS+7++xxx+ZW2 (Transaktionsgrundergänzung Geschäftsvorfall 3) vorhanden
7710    fn evaluate_478(&self, ctx: &EvaluationContext) -> ConditionResult {
7711        let sts_segs = ctx.find_segments("STS");
7712        let found = sts_segs.iter().any(|s| {
7713            s.elements
7714                .first()
7715                .and_then(|e| e.first())
7716                .is_some_and(|v| v == "7")
7717                && s.elements
7718                    .get(3)
7719                    .and_then(|e| e.first())
7720                    .is_some_and(|v| ["ZW2"].contains(&v.as_str()))
7721        });
7722        ConditionResult::from(found)
7723    }
7724
7725    /// [479] Wenn SG4 STS+7++xxx+ZW3 (Transaktionsgrundergänzung Erzeugende Marktlokation) vorhanden
7726    fn evaluate_479(&self, ctx: &EvaluationContext) -> ConditionResult {
7727        let sts_segs = ctx.find_segments("STS");
7728        let found = sts_segs.iter().any(|s| {
7729            s.elements
7730                .first()
7731                .and_then(|e| e.first())
7732                .is_some_and(|v| v == "7")
7733                && s.elements
7734                    .get(3)
7735                    .and_then(|e| e.first())
7736                    .is_some_and(|v| ["ZW3"].contains(&v.as_str()))
7737        });
7738        ConditionResult::from(found)
7739    }
7740
7741    /// [480] Wenn SG4 STS+7++xxx+ZW4 (Transaktionsgrundergänzung Verbrauchende Marktlokation) vorhanden
7742    fn evaluate_480(&self, ctx: &EvaluationContext) -> ConditionResult {
7743        let sts_segs = ctx.find_segments("STS");
7744        let found = sts_segs.iter().any(|s| {
7745            s.elements
7746                .first()
7747                .and_then(|e| e.first())
7748                .is_some_and(|v| v == "7")
7749                && s.elements
7750                    .get(3)
7751                    .and_then(|e| e.first())
7752                    .is_some_and(|v| ["ZW4"].contains(&v.as_str()))
7753        });
7754        ConditionResult::from(found)
7755    }
7756
7757    /// [481] Wenn SG4 STS+7++xxx+ZW5 (Transaktionsgrundergänzung Tranche) vorhanden
7758    fn evaluate_481(&self, ctx: &EvaluationContext) -> ConditionResult {
7759        let sts_segs = ctx.find_segments("STS");
7760        let found = sts_segs.iter().any(|s| {
7761            s.elements
7762                .first()
7763                .and_then(|e| e.first())
7764                .is_some_and(|v| v == "7")
7765                && s.elements
7766                    .get(3)
7767                    .and_then(|e| e.first())
7768                    .is_some_and(|v| ["ZW5"].contains(&v.as_str()))
7769        });
7770        ConditionResult::from(found)
7771    }
7772
7773    /// [483] Wenn SG4 STS+7++xxx+ZW7 (Transaktionsgrundergänzung Gemessene Marktlokation) vorhanden
7774    fn evaluate_483(&self, ctx: &EvaluationContext) -> ConditionResult {
7775        let sts_segs = ctx.find_segments("STS");
7776        let found = sts_segs.iter().any(|s| {
7777            s.elements
7778                .first()
7779                .and_then(|e| e.first())
7780                .is_some_and(|v| v == "7")
7781                && s.elements
7782                    .get(3)
7783                    .and_then(|e| e.first())
7784                    .is_some_and(|v| ["ZW7"].contains(&v.as_str()))
7785        });
7786        ConditionResult::from(found)
7787    }
7788
7789    /// [484] Wenn SG4 STS+7++xxx+ZW8 (Transaktionsgrundergänzung Fall1) vorhanden
7790    fn evaluate_484(&self, ctx: &EvaluationContext) -> ConditionResult {
7791        let sts_segs = ctx.find_segments("STS");
7792        let found = sts_segs.iter().any(|s| {
7793            s.elements
7794                .first()
7795                .and_then(|e| e.first())
7796                .is_some_and(|v| v == "7")
7797                && s.elements
7798                    .get(3)
7799                    .and_then(|e| e.first())
7800                    .is_some_and(|v| ["ZW8"].contains(&v.as_str()))
7801        });
7802        ConditionResult::from(found)
7803    }
7804
7805    /// [487] Wenn SG4 STS+7++xxx+ZX1 (Transaktionsgrundergänzung Fall4) vorhanden
7806    fn evaluate_487(&self, ctx: &EvaluationContext) -> ConditionResult {
7807        let sts_segs = ctx.find_segments("STS");
7808        let found = sts_segs.iter().any(|s| {
7809            s.elements
7810                .first()
7811                .and_then(|e| e.first())
7812                .is_some_and(|v| v == "7")
7813                && s.elements
7814                    .get(3)
7815                    .and_then(|e| e.first())
7816                    .is_some_and(|v| ["ZX1"].contains(&v.as_str()))
7817        });
7818        ConditionResult::from(found)
7819    }
7820
7821    /// [489] Nur bei dem ältesten Zeitraum welcher mit SG6 RFF+Z49 (Verwendungszeitraum der Daten: Gültige Daten) beschrieben ist
7822    fn evaluate_489(&self, ctx: &EvaluationContext) -> ConditionResult {
7823        // Check: this is the oldest Zeitraum (lowest Zeitraum-ID) with RFF+Z49
7824        let rff_z49 = ctx.find_segments_with_qualifier("RFF", 0, "Z49");
7825        if rff_z49.is_empty() {
7826            return ConditionResult::Unknown;
7827        }
7828        // Approximate: condition applies when RFF+Z49 exists
7829        ConditionResult::True
7830    }
7831
7832    /// [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
7833    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
7834        ctx.format_check("DTM", 0, 1, is_mesz_utc)
7835    }
7836
7837    /// [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
7838    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
7839        ctx.format_check("DTM", 0, 1, is_mez_utc)
7840    }
7841
7842    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
7843    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
7844        // Document date must be <= now. Format 303 is CCYYMMDDHHMM — fixed-width,
7845        // so lexicographic comparison works directly.
7846        let val = ctx.resolved_value.or_else(|| {
7847            ctx.find_segments_with_qualifier("DTM", 0, "137")
7848                .first()
7849                .and_then(|s| s.elements.first())
7850                .and_then(|e| e.get(1))
7851                .map(|v| v.as_str())
7852        });
7853        let Some(dtm_val) = val else {
7854            return ConditionResult::Unknown;
7855        };
7856        if dtm_val.len() < 12 {
7857            return ConditionResult::Unknown;
7858        }
7859        let now_str = crate::eval::format_validators::utc_now_ccyymmddhhmm();
7860        ConditionResult::from(&dtm_val[..12] <= now_str.as_str())
7861    }
7862
7863    /// [500] Hinweis: Code ist gemäß der Kategorie der zu stornierenden Meldung zu wählen
7864    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
7865        ConditionResult::True
7866    }
7867
7868    /// [501] Hinweis: Es sind zur Unterstützung die Daten des LFA anzugeben
7869    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
7870        ConditionResult::True
7871    }
7872
7873    /// [502] Hinweis: Es wird der Betrachtungsmonat der DZR bzw. BAS angegeben
7874    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
7875        ConditionResult::True
7876    }
7877
7878    /// [503] Hinweis: Angabe des BGM DE1004 aus der ORDERS
7879    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
7880        ConditionResult::True
7881    }
7882
7883    /// [504] Hinweis: Je Profil (ggf. inkl. Profilschar) ein Vorgang erforderlich
7884    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
7885        ConditionResult::True
7886    }
7887
7888    /// [505] Hinweis: Je zugeordneter ID und je EEG-Überführungszeitreihe ein Vorgang erforderlich
7889    fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
7890        ConditionResult::True
7891    }
7892
7893    /// [506] Hinweis: Für jeden Code im LOC+Z16 (Marktlokation) sollen die entsprechenden Kundendaten des LF angegeben werden, falls bekannt
7894    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
7895        ConditionResult::True
7896    }
7897
7898    /// [507] Hinweis: Ursprünglich vom NB bestätigtes Beginndatum
7899    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
7900        ConditionResult::True
7901    }
7902
7903    /// [508] Hinweis: Ausschließlich kME sind über die NNE abrechenbar
7904    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
7905        ConditionResult::True
7906    }
7907
7908    /// [509] Hinweis: Handelt sich um ein mME oder iMS ist das RFF 1154 immer mit NEIN anzugeben
7909    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
7910        ConditionResult::True
7911    }
7912
7913    /// [510] Hinweis: Zu verwenden bei der Abmeldung der ESV
7914    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
7915        ConditionResult::True
7916    }
7917
7918    /// [511] Hinweis: Es sind alle Bilanzierungsgebiete anzugeben in denen das Profil verwendet wird
7919    fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
7920        ConditionResult::True
7921    }
7922
7923    /// [512] Hinweis: Es ist informativ die bisherige Veräußerungsform des LFA an der erzeugenden Marktlokation anzugeben
7924    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
7925        ConditionResult::True
7926    }
7927
7928    /// [513] Hinweis: Es ist das nächstmögliche Datum ab dem DTM+471 aus der entsprechenden Anfrage zu ermitteln
7929    fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
7930        ConditionResult::True
7931    }
7932
7933    /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
7934    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
7935        ConditionResult::True
7936    }
7937
7938    /// [515] Hinweis: Für den ZP der LieferantensummenZR anzugeben
7939    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
7940        ConditionResult::True
7941    }
7942
7943    /// [516] Hinweis: Es ist der Änderungszeitpunkt anzugeben an dem das Zuordnungsende des NBA und der Zuordnungsbeginn des NBN zu einer Marktlokation fallen
7944    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
7945        ConditionResult::True
7946    }
7947
7948    /// [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
7949    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
7950        ConditionResult::True
7951    }
7952
7953    /// [518] Hinweis: Es sind alle Altlieferanten anzugeben, an die eine Abmeldeanfrage gesendet wird
7954    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
7955        ConditionResult::True
7956    }
7957
7958    /// [519] Hinweis: Wenn in der Anmeldung der Code ZAP vorhanden war, so ist dieser auch in der Antwort zu verwenden
7959    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
7960        ConditionResult::True
7961    }
7962
7963    /// [520] Hinweis: Bei der Verwendung des Codes ZAP handelt es sich immer um eine gemessene Marktlokation
7964    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
7965        ConditionResult::True
7966    }
7967
7968    /// [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...
7969    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
7970        ConditionResult::True
7971    }
7972
7973    /// [522] Hinweis: Es ist der NBN anzugeben
7974    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
7975        ConditionResult::True
7976    }
7977
7978    /// [523] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
7979    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
7980        ConditionResult::True
7981    }
7982
7983    /// [524] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, für die der LF nicht die gemeldete Ansicht des NB teilt
7984    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
7985        ConditionResult::True
7986    }
7987
7988    /// [525] Hinweis: Es sind nur die OBIS-Kennzahlen erlaubt, die im vorherigen Stammdatenaustausch zu dieser Marktlokation vom MSB zu diesem Zeitpunkt übermittelt wurden
7989    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
7990        ConditionResult::True
7991    }
7992
7993    /// [526] Hinweis: Wenn der Marktlokation keine Technische Ressource in der Lokationsbündelstruktur zugeordnet wurde
7994    fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
7995        ConditionResult::True
7996    }
7997
7998    /// [527] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben
7999    fn evaluate_527(&self, _ctx: &EvaluationContext) -> ConditionResult {
8000        ConditionResult::True
8001    }
8002
8003    /// [528] Hinweis: Es ist das Datum/ Daten aus der Anfrage zu verwenden
8004    fn evaluate_528(&self, _ctx: &EvaluationContext) -> ConditionResult {
8005        ConditionResult::True
8006    }
8007
8008    /// [529] Hinweis: Für zusätzliche nicht im Markt standardisierte Identifikatoren wie z.B. eine Netzbetreiber-Projektnummer
8009    fn evaluate_529(&self, _ctx: &EvaluationContext) -> ConditionResult {
8010        ConditionResult::True
8011    }
8012
8013    /// [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, ...
8014    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
8015        ConditionResult::True
8016    }
8017
8018    /// [531] Hinweis: Es ist das Jahr anzugeben in dem die nächste Netznutzungsabrechnung erfolgt
8019    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
8020        ConditionResult::True
8021    }
8022
8023    /// [532] Hinweis: Kritische Daten (gemäß GPKE)
8024    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
8025        ConditionResult::True
8026    }
8027
8028    /// [533] Hinweis: Es ist die MP-ID des Lieferanten anzugeben
8029    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
8030        ConditionResult::True
8031    }
8032
8033    /// [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...
8034    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
8035        // TODO: implement
8036        ConditionResult::Unknown
8037    }
8038
8039    /// [556] Hinweis: Wenn keine Korrespondenzanschrift des Endverbrauchers/ Kunden vorliegt, ist die Anschrift der Marktlokation zu übermitteln
8040    fn evaluate_556(&self, _ctx: &EvaluationContext) -> ConditionResult {
8041        ConditionResult::True
8042    }
8043
8044    /// [558] Hinweis: Diese Information kann freiwillig ausgetauscht werden
8045    fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
8046        ConditionResult::True
8047    }
8048
8049    /// [559] Hinweis: Die Korrespondenzanschrift des Endverbrauchers/Kunden wird nicht zur Identifikation genutzt
8050    fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
8051        ConditionResult::True
8052    }
8053
8054    /// [563] Hinweis: Für die ID der LieferantensummenZR
8055    fn evaluate_563(&self, _ctx: &EvaluationContext) -> ConditionResult {
8056        ConditionResult::True
8057    }
8058
8059    /// [566] Hinweis: Altlieferant
8060    fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
8061        ConditionResult::True
8062    }
8063
8064    /// [567] Hinweis: Neulieferant
8065    fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
8066        ConditionResult::True
8067    }
8068
8069    /// [568] Hinweis: Lieferant der LieferantensummenZR
8070    fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
8071        ConditionResult::True
8072    }
8073
8074    /// [569] Hinweis: Dritter Nutzer
8075    fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
8076        ConditionResult::True
8077    }
8078
8079    /// [570] Hinweis: Netzbetreiber Alt
8080    fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
8081        ConditionResult::True
8082    }
8083
8084    /// [572] Hinweis: Kundenname aus Anmeldung Lieferant neu
8085    fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
8086        ConditionResult::True
8087    }
8088
8089    /// [576] Hinweis: Stammdaten des bisherigen Messstellenbetreibers
8090    fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
8091        ConditionResult::True
8092    }
8093
8094    /// [577] Hinweis: Wird mit dem ursprünglich vom NB bestätigten Beginnzeitpunkt gefüllt
8095    fn evaluate_577(&self, _ctx: &EvaluationContext) -> ConditionResult {
8096        ConditionResult::True
8097    }
8098
8099    /// [579] Hinweis: Auslösender Marktpartner (LFN bei STS+7++ZH0/ZG9, NB bei STS+7++ZH1)
8100    fn evaluate_579(&self, _ctx: &EvaluationContext) -> ConditionResult {
8101        ConditionResult::True
8102    }
8103
8104    /// [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...
8105    fn evaluate_580(&self, _ctx: &EvaluationContext) -> ConditionResult {
8106        ConditionResult::True
8107    }
8108
8109    /// [581] Hinweis: Es ist der Zeitpunkt anzugeben, zu welchem der Vertrag am Tag des Versandes der Antwort noch kündbar ist.
8110    fn evaluate_581(&self, _ctx: &EvaluationContext) -> ConditionResult {
8111        ConditionResult::True
8112    }
8113
8114    /// [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...
8115    fn evaluate_586(&self, _ctx: &EvaluationContext) -> ConditionResult {
8116        ConditionResult::True
8117    }
8118
8119    /// [590] Hinweis: Für den Empfang des Steuerbefehls
8120    fn evaluate_590(&self, _ctx: &EvaluationContext) -> ConditionResult {
8121        ConditionResult::True
8122    }
8123
8124    /// [594] Hinweis: Es ist der ZPB des ZP der NGZ und die ZPB der NZR anzugeben
8125    fn evaluate_594(&self, _ctx: &EvaluationContext) -> ConditionResult {
8126        ConditionResult::True
8127    }
8128
8129    /// [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...
8130    fn evaluate_599(&self, _ctx: &EvaluationContext) -> ConditionResult {
8131        ConditionResult::True
8132    }
8133
8134    /// [601] Hinweis: Es ist die ID der Marktlokation und alle Identifikatoren der Messlokationen anzugeben. Sowie wenn vorhanden die Tranche/n der Marklokation
8135    fn evaluate_601(&self, _ctx: &EvaluationContext) -> ConditionResult {
8136        ConditionResult::True
8137    }
8138
8139    /// [606] Hinweis: In diesem Segment bzw. SG sind die Daten aus der Sicht des NB anzugeben
8140    fn evaluate_606(&self, _ctx: &EvaluationContext) -> ConditionResult {
8141        ConditionResult::True
8142    }
8143
8144    /// [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
8145    fn evaluate_609(&self, _ctx: &EvaluationContext) -> ConditionResult {
8146        ConditionResult::True
8147    }
8148
8149    /// [611] Hinweis: Wenn das Unternehmen NB die Aufgaben der Marktrolle LF wahrnimmt, ist die MP-ID des Unternehmens NB in der Marktrolle LF anzugeben
8150    fn evaluate_611(&self, _ctx: &EvaluationContext) -> ConditionResult {
8151        ConditionResult::True
8152    }
8153
8154    /// [614] Hinweis: Es werden nur die OBIS Kennzahlen übermittelt die für die Bilanzierung relevant sind
8155    fn evaluate_614(&self, _ctx: &EvaluationContext) -> ConditionResult {
8156        ConditionResult::True
8157    }
8158
8159    /// [617] Hinweis: Mehrere Vorgänge für eine Marktlokation bei Wechsel bilanzierungsrelevanter Stammdaten innerhalb des Bilanzierungsmonats
8160    fn evaluate_617(&self, _ctx: &EvaluationContext) -> ConditionResult {
8161        ConditionResult::True
8162    }
8163
8164    /// [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
8165    fn evaluate_618(&self, _ctx: &EvaluationContext) -> ConditionResult {
8166        ConditionResult::True
8167    }
8168
8169    /// [619] Hinweis: Für die selbständige Änderungsmöglichkeit der Schwellwertunter- / -über-schreitung
8170    fn evaluate_619(&self, _ctx: &EvaluationContext) -> ConditionResult {
8171        ConditionResult::True
8172    }
8173
8174    /// [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.
8175    fn evaluate_621(&self, _ctx: &EvaluationContext) -> ConditionResult {
8176        ConditionResult::True
8177    }
8178
8179    /// [622] Hinweis: Falls die OBIS-Kennzahl für mehrere Marktrollen relevant ist, so muss die Segmentgruppe pro Marktrolle wiederholt werden
8180    fn evaluate_622(&self, _ctx: &EvaluationContext) -> ConditionResult {
8181        ConditionResult::True
8182    }
8183
8184    /// [623] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben, die zur Ermittlung der Energiemenge der im Vorgang genannten Marktlokation benötigt werden
8185    fn evaluate_623(&self, _ctx: &EvaluationContext) -> ConditionResult {
8186        ConditionResult::True
8187    }
8188
8189    /// [630] Hinweis: Wenn die Liste abgelehnt wird, ist kein Vorgang enthalten
8190    fn evaluate_630(&self, _ctx: &EvaluationContext) -> ConditionResult {
8191        ConditionResult::True
8192    }
8193
8194    /// [631] Hinweis: Es ist die Listennummer aus der Lieferanten- bzw. Bilanzierungsgebietsclearingliste zu verwenden
8195    fn evaluate_631(&self, _ctx: &EvaluationContext) -> ConditionResult {
8196        ConditionResult::True
8197    }
8198
8199    /// [632] Hinweis: Es ist die Listennummer aus der Lieferanten-ausfallarbeits-clearingliste zu verwenden
8200    fn evaluate_632(&self, _ctx: &EvaluationContext) -> ConditionResult {
8201        ConditionResult::True
8202    }
8203
8204    /// [637] Hinweis: Bei Verpflichtungsanfrage
8205    fn evaluate_637(&self, _ctx: &EvaluationContext) -> ConditionResult {
8206        ConditionResult::True
8207    }
8208
8209    /// [638] Hinweis: Bei Aufforderung zur Übernahme der einzelnen Messlokation durch den gMSB
8210    fn evaluate_638(&self, _ctx: &EvaluationContext) -> ConditionResult {
8211        ConditionResult::True
8212    }
8213
8214    /// [639] Hinweis: Wenn Antwort auf Deaktivierung
8215    fn evaluate_639(&self, _ctx: &EvaluationContext) -> ConditionResult {
8216        ConditionResult::True
8217    }
8218
8219    /// [640] Hinweis: Wenn Antwort auf Aktivierung
8220    fn evaluate_640(&self, _ctx: &EvaluationContext) -> ConditionResult {
8221        ConditionResult::True
8222    }
8223
8224    /// [641] Hinweis: Wenn Einzelanforderung vorliegt
8225    fn evaluate_641(&self, _ctx: &EvaluationContext) -> ConditionResult {
8226        ConditionResult::True
8227    }
8228
8229    /// [642] Hinweis: Wenn Abo vorliegt
8230    fn evaluate_642(&self, _ctx: &EvaluationContext) -> ConditionResult {
8231        ConditionResult::True
8232    }
8233
8234    /// [643] Hinweis: Nachfolgender Netzbetreiber
8235    fn evaluate_643(&self, _ctx: &EvaluationContext) -> ConditionResult {
8236        ConditionResult::True
8237    }
8238
8239    /// [645] Hinweis: Es ist der Bilanzkreis des LF anzugeben
8240    fn evaluate_645(&self, _ctx: &EvaluationContext) -> ConditionResult {
8241        ConditionResult::True
8242    }
8243
8244    /// [646] Hinweis: Es ist der RD-Bilanzkreis des ANB anzugeben
8245    fn evaluate_646(&self, _ctx: &EvaluationContext) -> ConditionResult {
8246        ConditionResult::True
8247    }
8248
8249    /// [647] Hinweis: Es ist der RD-Bilanzkreis des anfNB anzugeben
8250    fn evaluate_647(&self, _ctx: &EvaluationContext) -> ConditionResult {
8251        ConditionResult::True
8252    }
8253
8254    /// [648] Hinweis: Soll ein Produkt ab dem Datum \"Änderung zum\" nicht mehr vorhanden sein, wird dies durch nicht angeben des PIA+5 ausgedrückt
8255    fn evaluate_648(&self, _ctx: &EvaluationContext) -> ConditionResult {
8256        ConditionResult::True
8257    }
8258
8259    /// [651] Hinweis: Es sind die Marktlokationen anzugeben, zu welchen die hier genannte OBIS benötigt wird
8260    fn evaluate_651(&self, _ctx: &EvaluationContext) -> ConditionResult {
8261        ConditionResult::True
8262    }
8263
8264    /// [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...
8265    fn evaluate_653(&self, _ctx: &EvaluationContext) -> ConditionResult {
8266        ConditionResult::True
8267    }
8268
8269    /// [655] Hinweis: Es ist die Listennummer aus der DZÜ Liste zu verwenden
8270    fn evaluate_655(&self, _ctx: &EvaluationContext) -> ConditionResult {
8271        ConditionResult::True
8272    }
8273
8274    /// [659] Hinweis: Bei Tranchen ist nur der Meldepunkt der Tranche anzugeben
8275    fn evaluate_659(&self, _ctx: &EvaluationContext) -> ConditionResult {
8276        ConditionResult::True
8277    }
8278
8279    /// [660] Hinweis: Es ist die ID der BK-Summenzeitreihe Aggregationsebene BG anzugeben
8280    fn evaluate_660(&self, _ctx: &EvaluationContext) -> ConditionResult {
8281        ConditionResult::True
8282    }
8283
8284    /// [662] Hinweis: Es sind die Daten NGZ und die Daten der NZR anzugeben
8285    fn evaluate_662(&self, _ctx: &EvaluationContext) -> ConditionResult {
8286        ConditionResult::True
8287    }
8288
8289    /// [663] Hinweis: Es ist die ID der Marktlokation und die ZPB des ZP der NGZ anzugeben
8290    fn evaluate_663(&self, _ctx: &EvaluationContext) -> ConditionResult {
8291        ConditionResult::True
8292    }
8293
8294    /// [664] Hinweis: Es ist das BG des NB (LPB) anzugeben
8295    fn evaluate_664(&self, _ctx: &EvaluationContext) -> ConditionResult {
8296        ConditionResult::True
8297    }
8298
8299    /// [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
8300    fn evaluate_665(&self, _ctx: &EvaluationContext) -> ConditionResult {
8301        ConditionResult::True
8302    }
8303
8304    /// [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...
8305    fn evaluate_667(&self, _ctx: &EvaluationContext) -> ConditionResult {
8306        ConditionResult::True
8307    }
8308
8309    /// [668] Hinweis: Dieses Segment wird nach Abschluss der Einführung der Lokationsbündelstruktur zum 01.10.2025 aus der UTILMD entfernt
8310    fn evaluate_668(&self, _ctx: &EvaluationContext) -> ConditionResult {
8311        ConditionResult::True
8312    }
8313
8314    /// [670] Hinweis: Es sind alle Netzlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8315    fn evaluate_670(&self, _ctx: &EvaluationContext) -> ConditionResult {
8316        ConditionResult::True
8317    }
8318
8319    /// [671] Hinweis: Es sind alle Marktlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8320    fn evaluate_671(&self, _ctx: &EvaluationContext) -> ConditionResult {
8321        ConditionResult::True
8322    }
8323
8324    /// [672] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8325    fn evaluate_672(&self, _ctx: &EvaluationContext) -> ConditionResult {
8326        ConditionResult::True
8327    }
8328
8329    /// [673] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8330    fn evaluate_673(&self, _ctx: &EvaluationContext) -> ConditionResult {
8331        ConditionResult::True
8332    }
8333
8334    /// [674] Hinweis: Es sind alle Tranchen, zu nennen, die dem gleichen Lokationsbündel angehören
8335    fn evaluate_674(&self, _ctx: &EvaluationContext) -> ConditionResult {
8336        ConditionResult::True
8337    }
8338
8339    /// [675] Hinweis: Es sind alle Messlokationen, zu nennen, die Lokationsbündel angehören
8340    fn evaluate_675(&self, _ctx: &EvaluationContext) -> ConditionResult {
8341        ConditionResult::True
8342    }
8343
8344    /// [677] Hinweis: Es sind alle Netzlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8345    fn evaluate_677(&self, _ctx: &EvaluationContext) -> ConditionResult {
8346        ConditionResult::True
8347    }
8348
8349    /// [678] Hinweis: Es sind alle Marktlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8350    fn evaluate_678(&self, _ctx: &EvaluationContext) -> ConditionResult {
8351        ConditionResult::True
8352    }
8353
8354    /// [679] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8355    fn evaluate_679(&self, _ctx: &EvaluationContext) -> ConditionResult {
8356        ConditionResult::True
8357    }
8358
8359    /// [680] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8360    fn evaluate_680(&self, _ctx: &EvaluationContext) -> ConditionResult {
8361        ConditionResult::True
8362    }
8363
8364    /// [682] Hinweis: Es sind alle Messlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8365    fn evaluate_682(&self, _ctx: &EvaluationContext) -> ConditionResult {
8366        ConditionResult::True
8367    }
8368
8369    /// [683] Hinweis: Es sind alle ID der Netzlokationen, welche der im SG5 LOC+Z16 angegebenen Marktlokation aktuell und / oder zukünftig vorgelagert sind, anzugeben
8370    fn evaluate_683(&self, _ctx: &EvaluationContext) -> ConditionResult {
8371        ConditionResult::True
8372    }
8373
8374    /// [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
8375    fn evaluate_684(&self, _ctx: &EvaluationContext) -> ConditionResult {
8376        ConditionResult::True
8377    }
8378
8379    /// [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.
8380    fn evaluate_685(&self, _ctx: &EvaluationContext) -> ConditionResult {
8381        ConditionResult::True
8382    }
8383
8384    /// [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
8385    fn evaluate_687(&self, _ctx: &EvaluationContext) -> ConditionResult {
8386        ConditionResult::True
8387    }
8388
8389    /// [688] Hinweis: Es sind alle ID der Netzlokationen, welche der im LOC+Z16 angegebenen Marktlokation vorgelagert sind, anzugeben
8390    fn evaluate_688(&self, _ctx: &EvaluationContext) -> ConditionResult {
8391        ConditionResult::True
8392    }
8393
8394    /// [689] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16 angegebenen Marktlokation zugehören, anzugeben
8395    fn evaluate_689(&self, _ctx: &EvaluationContext) -> ConditionResult {
8396        ConditionResult::True
8397    }
8398
8399    /// [690] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen zugeordnet sind, anzugeben
8400    fn evaluate_690(&self, _ctx: &EvaluationContext) -> ConditionResult {
8401        ConditionResult::True
8402    }
8403
8404    /// [693] Hinweis: Es sind die Daten NGZ anzugeben
8405    fn evaluate_693(&self, _ctx: &EvaluationContext) -> ConditionResult {
8406        ConditionResult::True
8407    }
8408
8409    /// [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...
8410    fn evaluate_694(&self, _ctx: &EvaluationContext) -> ConditionResult {
8411        ConditionResult::True
8412    }
8413
8414    /// [695] Hinweis: Verwendung, wenn Rolle LF einem Unternehmen NB zugeordnet ist
8415    fn evaluate_695(&self, _ctx: &EvaluationContext) -> ConditionResult {
8416        ConditionResult::True
8417    }
8418
8419    /// [696] Hinweis: Verwendung, nur wenn die Rolle LF nicht einem Unternehmen NB zugeordnet ist
8420    fn evaluate_696(&self, _ctx: &EvaluationContext) -> ConditionResult {
8421        ConditionResult::True
8422    }
8423
8424    /// [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...
8425    fn evaluate_698(&self, _ctx: &EvaluationContext) -> ConditionResult {
8426        ConditionResult::True
8427    }
8428
8429    /// [699] Hinweis: Es ist die ID der Steuerbare Ressource zu nennen über die die Technische Ressource gesteuert wird
8430    fn evaluate_699(&self, _ctx: &EvaluationContext) -> ConditionResult {
8431        ConditionResult::True
8432    }
8433
8434    /// [700] Hinweis: Es ist die ID der Netzlokation zu nennen über die die Technische Ressource gesteuert wird
8435    fn evaluate_700(&self, _ctx: &EvaluationContext) -> ConditionResult {
8436        ConditionResult::True
8437    }
8438
8439    /// [704] Hinweis: Segment ist zu verwenden, wenn es in Anfrage vorhanden war
8440    fn evaluate_704(&self, _ctx: &EvaluationContext) -> ConditionResult {
8441        ConditionResult::True
8442    }
8443
8444    /// [705] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Änderung\" durchgeführt wird
8445    fn evaluate_705(&self, _ctx: &EvaluationContext) -> ConditionResult {
8446        ConditionResult::True
8447    }
8448
8449    /// [706] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Bestellung einer Änderung von Stammdaten...\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8450    fn evaluate_706(&self, ctx: &EvaluationContext) -> ConditionResult {
8451        ctx.external.evaluate("data_clearing_required")
8452    }
8453
8454    /// [707] Hinweis: für weitere Details siehe Kapitel \"SG6 Verwendungszeitraum der Daten\"
8455    fn evaluate_707(&self, _ctx: &EvaluationContext) -> ConditionResult {
8456        ConditionResult::True
8457    }
8458
8459    /// [708] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des E/G zur Marktlokation\" durchgeführt wird
8460    fn evaluate_708(&self, _ctx: &EvaluationContext) -> ConditionResult {
8461        ConditionResult::True
8462    }
8463
8464    /// [709] Hinweis: Wenn die Aktion \"Zuordnung des E/G zur Marktlokation aufgrund fehlender Antwort\" durchgeführt wird
8465    fn evaluate_709(&self, _ctx: &EvaluationContext) -> ConditionResult {
8466        ConditionResult::True
8467    }
8468
8469    /// [710] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des LFN zur Marktlokation bzw. Tranche\" durchgeführt wird
8470    fn evaluate_710(&self, _ctx: &EvaluationContext) -> ConditionResult {
8471        ConditionResult::True
8472    }
8473
8474    /// [711] Hinweis: Wenn die Aktion \"Zuordnung des LFN zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8475    fn evaluate_711(&self, _ctx: &EvaluationContext) -> ConditionResult {
8476        ConditionResult::True
8477    }
8478
8479    /// [712] Hinweis: Wenn die Aktion \"Ankündigung der Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche\" durchgeführt wird
8480    fn evaluate_712(&self, _ctx: &EvaluationContext) -> ConditionResult {
8481        ConditionResult::True
8482    }
8483
8484    /// [713] Hinweis: Wenn die Aktion \"Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8485    fn evaluate_713(&self, _ctx: &EvaluationContext) -> ConditionResult {
8486        ConditionResult::True
8487    }
8488
8489    /// [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...
8490    fn evaluate_714(&self, _ctx: &EvaluationContext) -> ConditionResult {
8491        ConditionResult::True
8492    }
8493
8494    /// [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...
8495    fn evaluate_715(&self, _ctx: &EvaluationContext) -> ConditionResult {
8496        ConditionResult::True
8497    }
8498
8499    /// [716] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Abrechnungsdaten\" durchgeführt wird
8500    fn evaluate_716(&self, _ctx: &EvaluationContext) -> ConditionResult {
8501        ConditionResult::True
8502    }
8503
8504    /// [717] Hinweis: Wenn die Aktion des Sequenzdiagramms \"Bestellung einer Änderung von Abrechnungsdaten\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8505    fn evaluate_717(&self, ctx: &EvaluationContext) -> ConditionResult {
8506        ctx.external.evaluate("data_clearing_required")
8507    }
8508
8509    /// [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
8510    fn evaluate_718(&self, _ctx: &EvaluationContext) -> ConditionResult {
8511        ConditionResult::True
8512    }
8513
8514    /// [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...
8515    fn evaluate_719(&self, _ctx: &EvaluationContext) -> ConditionResult {
8516        ConditionResult::True
8517    }
8518
8519    /// [902] Format: Möglicher Wert: ≥ 0
8520    fn evaluate_902(&self, _ctx: &EvaluationContext) -> ConditionResult {
8521        // Format condition — informational, always applies
8522        ConditionResult::True
8523    }
8524
8525    /// [910] Format: Möglicher Wert: < 0 oder ≥ 0
8526    fn evaluate_910(&self, _ctx: &EvaluationContext) -> ConditionResult {
8527        // Format condition — informational, always applies
8528        ConditionResult::True
8529    }
8530
8531    /// [914] Format: Möglicher Wert: > 0
8532    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
8533        ctx.format_check("SEQ", 1, 0, |val| validate_numeric(val, ">", 0.0))
8534    }
8535
8536    /// [922] Format: TR-ID
8537    fn evaluate_922(&self, _ctx: &EvaluationContext) -> ConditionResult {
8538        // Format condition — informational, always applies
8539        ConditionResult::True
8540    }
8541
8542    /// [926] Format: Möglicher Wert: 0
8543    fn evaluate_926(&self, _ctx: &EvaluationContext) -> ConditionResult {
8544        // Format condition — informational, always applies
8545        ConditionResult::True
8546    }
8547
8548    /// [930] Format: max. 2 Nachkommastellen
8549    fn evaluate_930(&self, _ctx: &EvaluationContext) -> ConditionResult {
8550        // Format condition — informational, always applies
8551        ConditionResult::True
8552    }
8553
8554    /// [931] Format: ZZZ = +00
8555    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
8556        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
8557    }
8558
8559    /// [932] Format: HHMM = 2200
8560    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
8561        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
8562    }
8563
8564    /// [933] Format: HHMM = 2300
8565    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
8566        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
8567    }
8568
8569    /// [937] Format: keine Nachkommastelle
8570    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
8571        ctx.format_check("SEQ", 1, 0, |val| validate_max_decimal_places(val, 0))
8572    }
8573
8574    /// [938] Format: Möglicher Wert: <= 10
8575    fn evaluate_938(&self, _ctx: &EvaluationContext) -> ConditionResult {
8576        ConditionResult::True
8577    }
8578
8579    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
8580    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
8581        ctx.format_check("COM", 0, 0, validate_email)
8582    }
8583
8584    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
8585    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
8586        ctx.format_check("COM", 0, 0, validate_phone)
8587    }
8588
8589    /// [942] Format: n1-n2-n1-n3
8590    fn evaluate_942(&self, _ctx: &EvaluationContext) -> ConditionResult {
8591        // Format condition — informational, always applies
8592        ConditionResult::True
8593    }
8594
8595    /// [943] Format: n1-n2-n1
8596    fn evaluate_943(&self, _ctx: &EvaluationContext) -> ConditionResult {
8597        // Format condition — informational, always applies
8598        ConditionResult::True
8599    }
8600
8601    /// [946] Format: max. 11 Nachkommastellen
8602    fn evaluate_946(&self, _ctx: &EvaluationContext) -> ConditionResult {
8603        // Format condition — informational, always applies
8604        ConditionResult::True
8605    }
8606
8607    /// [948] Format: n1-n2-n1-n8-n2
8608    fn evaluate_948(&self, _ctx: &EvaluationContext) -> ConditionResult {
8609        // Format condition — informational, always applies
8610        ConditionResult::True
8611    }
8612
8613    /// [950] Format: Marktlokations-ID
8614    fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
8615        // Format condition — informational, always applies
8616        ConditionResult::True
8617    }
8618
8619    /// [951] Format: Zählpunktbezeichnung
8620    fn evaluate_951(&self, _ctx: &EvaluationContext) -> ConditionResult {
8621        // Format condition — informational, always applies
8622        ConditionResult::True
8623    }
8624
8625    /// [952] Format: Gerätenummer nach DIN 43863-5
8626    fn evaluate_952(&self, _ctx: &EvaluationContext) -> ConditionResult {
8627        // Format condition — informational, always applies
8628        ConditionResult::True
8629    }
8630
8631    /// [955] Format: Möglicher Wert: < 100
8632    fn evaluate_955(&self, _ctx: &EvaluationContext) -> ConditionResult {
8633        // Format condition — informational, always applies
8634        ConditionResult::True
8635    }
8636
8637    /// [957] Format: n1-n2-n1-n8
8638    fn evaluate_957(&self, _ctx: &EvaluationContext) -> ConditionResult {
8639        // Format condition — informational, always applies
8640        ConditionResult::True
8641    }
8642
8643    /// [960] Format: Netzlokations-ID
8644    fn evaluate_960(&self, _ctx: &EvaluationContext) -> ConditionResult {
8645        // Format condition — informational, always applies
8646        ConditionResult::True
8647    }
8648
8649    /// [961] Format: SR-ID
8650    fn evaluate_961(&self, _ctx: &EvaluationContext) -> ConditionResult {
8651        // Format condition — informational, always applies
8652        ConditionResult::True
8653    }
8654
8655    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
8656    fn evaluate_967(&self, _ctx: &EvaluationContext) -> ConditionResult {
8657        // Format condition — informational, always applies
8658        ConditionResult::True
8659    }
8660
8661    /// [2001] Segmentgruppe ist mindestens zweimal je SG4 IDE+24 (Vorgang) anzugeben
8662    fn evaluate_2001(&self, ctx: &EvaluationContext) -> ConditionResult {
8663        // Cardinality: at least twice per SG4 IDE+24
8664        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8665        ConditionResult::from(ide_count > 0)
8666    }
8667
8668    /// [2002] Für jede Produktpaket-ID im SG8 SEQ+Z79 (Erforderliches Produkt) DE1050 genau einmal anzugeben
8669    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
8670        let seq_segs = ctx.find_segments("SEQ");
8671        let has_z79 = seq_segs.iter().any(|s| {
8672            s.elements
8673                .first()
8674                .and_then(|e| e.first())
8675                .is_some_and(|v| v == "Z79")
8676        });
8677        ConditionResult::from(has_z79)
8678    }
8679
8680    /// [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
8681    fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
8682        // TODO: implement
8683        ConditionResult::Unknown
8684    }
8685
8686    /// [2004] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8687    // 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)
8688    fn evaluate_2004(&self, ctx: &EvaluationContext) -> ConditionResult {
8689        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
8690        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
8691
8692        let zeitraum_ids: Vec<String> = rff_qualifiers
8693            .iter()
8694            .filter(|(_, qual)| qual.as_str() == "Z49")
8695            .filter_map(|(idx, _)| {
8696                rff_zeitraum_ids
8697                    .iter()
8698                    .find(|(i, v)| i == idx && !v.is_empty())
8699                    .map(|(_, v)| v.clone())
8700            })
8701            .collect();
8702
8703        if zeitraum_ids.is_empty() {
8704            return ConditionResult::Unknown;
8705        }
8706
8707        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
8708
8709        for zid in &zeitraum_ids {
8710            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
8711            if count != 1 {
8712                return ConditionResult::False;
8713            }
8714        }
8715
8716        ConditionResult::True
8717    }
8718
8719    /// [2005] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8720    // 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)
8721    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
8722        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
8723        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
8724
8725        let zeitraum_ids: Vec<String> = rff_qualifiers
8726            .iter()
8727            .filter(|(_, qual)| qual.as_str() == "Z49")
8728            .filter_map(|(idx, _)| {
8729                rff_zeitraum_ids
8730                    .iter()
8731                    .find(|(i, v)| i == idx && !v.is_empty())
8732                    .map(|(_, v)| v.clone())
8733            })
8734            .collect();
8735
8736        if zeitraum_ids.is_empty() {
8737            return ConditionResult::Unknown;
8738        }
8739
8740        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
8741
8742        for zid in &zeitraum_ids {
8743            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
8744            if count == 0 {
8745                return ConditionResult::False;
8746            }
8747        }
8748
8749        ConditionResult::True
8750    }
8751
8752    /// [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 ...
8753    fn evaluate_2010(&self, _ctx: &EvaluationContext) -> ConditionResult {
8754        // TODO: implement
8755        ConditionResult::Unknown
8756    }
8757
8758    /// [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 ...
8759    /// EXTERNAL: Requires context from outside the message.
8760    // 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)
8761    fn evaluate_2011(&self, ctx: &EvaluationContext) -> ConditionResult {
8762        ctx.external
8763            .evaluate("request_message_zeitraum_ids_present")
8764    }
8765
8766    /// [2012] Segmentgruppe ist genau einmal für die Angabe der Informativen Daten der Marktlokation anzugeben
8767    fn evaluate_2012(&self, _ctx: &EvaluationContext) -> ConditionResult {
8768        // TODO: implement
8769        ConditionResult::Unknown
8770    }
8771
8772    /// [2013] Mindesten einmal anzugeben wenn kein SG6 RFF+Z54 (Verwendungszeitraum der Daten: Im System keine Daten vorhanden) vorhanden
8773    fn evaluate_2013(&self, ctx: &EvaluationContext) -> ConditionResult {
8774        // Check: condition applies when no RFF+Z54 exists
8775        ctx.lacks_qualifier("RFF", 0, "Z54")
8776    }
8777
8778    /// [2014] Mindesten einmal anzugeben wenn kein SG6 RFF+Z47 (Verwendungszeitraum der Daten: Im System vorhandene Daten) vorhanden
8779    fn evaluate_2014(&self, ctx: &EvaluationContext) -> ConditionResult {
8780        // Check: condition applies when no RFF+Z47 exists
8781        ctx.lacks_qualifier("RFF", 0, "Z47")
8782    }
8783
8784    /// [2015] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) untergeordnet ist
8785    fn evaluate_2015(&self, _ctx: &EvaluationContext) -> ConditionResult {
8786        // TODO: implement
8787        ConditionResult::Unknown
8788    }
8789
8790    /// [2016] Je SG5 LOC+Z16 (Marktlokation) ist genau einmal die Segmentgruppe anzugeben
8791    fn evaluate_2016(&self, ctx: &EvaluationContext) -> ConditionResult {
8792        // Cardinality: exactly once per SG5 LOC+Z16 (Marktlokation)
8793        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
8794        ConditionResult::from(loc_count > 0)
8795    }
8796
8797    /// [2017] Je SG5 LOC+Z17 (Messlokation) ist genau einmal die Segmentgruppe anzugeben
8798    fn evaluate_2017(&self, ctx: &EvaluationContext) -> ConditionResult {
8799        // Cardinality: exactly once per SG5 LOC+Z17 (Messlokation)
8800        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
8801        ConditionResult::from(loc_count > 0)
8802    }
8803
8804    /// [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...
8805    fn evaluate_2018(&self, _ctx: &EvaluationContext) -> ConditionResult {
8806        // TODO: implement
8807        ConditionResult::Unknown
8808    }
8809
8810    /// [2060] Wenn SG10 CAV+NZR ist die Segmentgruppe genau zwei Mal je IDE+24 anzugeben
8811    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
8812        // Check: CAV+NZR exists (then segment group exactly twice per IDE+24)
8813        let found = ctx.find_segments("CAV").iter().any(|s| {
8814            s.elements
8815                .first()
8816                .and_then(|e| e.first())
8817                .is_some_and(|v| v == "NZR")
8818        });
8819        ConditionResult::from(found)
8820    }
8821
8822    /// [2061] Segment bzw. Segmentgruppe ist genau einmal je SG4 IDE (Vorgang) anzugeben
8823    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
8824        // Cardinality: exactly once per SG4 IDE (always true for single-transaction messages)
8825        let ide_count = ctx.find_segments("IDE").len();
8826        ConditionResult::from(ide_count > 0)
8827    }
8828
8829    /// [2071] Für die ID der LieferantensummenZR einmal je SG4 IDE+24 (Vorgang)
8830    fn evaluate_2071(&self, ctx: &EvaluationContext) -> ConditionResult {
8831        // Cardinality: LieferantensummenZR once per SG4 IDE+24
8832        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8833        ConditionResult::from(ide_count > 0)
8834    }
8835
8836    /// [2073] Für die ID der BilanzkreissummenZR einmal je SG4 IDE+24 (Vorgang)
8837    fn evaluate_2073(&self, ctx: &EvaluationContext) -> ConditionResult {
8838        // Cardinality: BilanzkreissummenZR once per SG4 IDE+24
8839        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8840        ConditionResult::from(ide_count > 0)
8841    }
8842
8843    /// [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)
8844    fn evaluate_2075(&self, ctx: &EvaluationContext) -> ConditionResult {
8845        // Cardinality: Zeitreihen (not EEG-EUZ/AAÜZ) once per SG4 IDE+24
8846        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8847        ConditionResult::from(ide_count > 0)
8848    }
8849
8850    /// [2080] Segmentgruppe ist max. zweimal je SG4 IDE+24 (Vorgang) anzugeben
8851    fn evaluate_2080(&self, ctx: &EvaluationContext) -> ConditionResult {
8852        // Cardinality: max twice per SG4 IDE+24
8853        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8854        ConditionResult::from(ide_count > 0)
8855    }
8856
8857    /// [2095] Je SG5 LOC+Z15 (MaBiS-Zählpunkt) ist genau einmal die Segmentgruppe anzugeben
8858    fn evaluate_2095(&self, ctx: &EvaluationContext) -> ConditionResult {
8859        // Cardinality: exactly once per SG5 LOC+Z15 (MaBiS-Zählpunkt)
8860        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z15").len();
8861        ConditionResult::from(loc_count > 0)
8862    }
8863
8864    /// [2096] Segmentgruppe ist genau zweimal je SG4 IDE anzugeben
8865    fn evaluate_2096(&self, ctx: &EvaluationContext) -> ConditionResult {
8866        // Cardinality: exactly twice per SG4 IDE
8867        let ide_count = ctx.find_segments("IDE").len();
8868        ConditionResult::from(ide_count > 0)
8869    }
8870
8871    /// [2119] Je SG8 SEQ+Z13/ ZG0 (Smartmeter-Gateway) ist genau einmal die Segmentgruppe anzugeben
8872    fn evaluate_2119(&self, ctx: &EvaluationContext) -> ConditionResult {
8873        // Cardinality: exactly once per SG8 SEQ+Z13/ZG0 (Smartmeter-Gateway)
8874        let found = ctx.find_segments("SEQ").iter().any(|s| {
8875            s.elements
8876                .first()
8877                .and_then(|e| e.first())
8878                .is_some_and(|v| v == "Z13" || v == "ZG0")
8879        });
8880        ConditionResult::from(found)
8881    }
8882
8883    /// [2140] Für die ID der LieferantensummenZR einmal je SG4 IDE+Z01 (Liste)
8884    fn evaluate_2140(&self, ctx: &EvaluationContext) -> ConditionResult {
8885        // Cardinality: LieferantensummenZR once per SG4 IDE+Z01
8886        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
8887        ConditionResult::from(ide_count > 0)
8888    }
8889
8890    /// [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
8891    fn evaluate_2182(&self, ctx: &EvaluationContext) -> ConditionResult {
8892        // Cardinality: exactly once per SG8 SEQ+Z01/Z80/Z81/Z98
8893        let found = ctx.find_segments("SEQ").iter().any(|s| {
8894            s.elements
8895                .first()
8896                .and_then(|e| e.first())
8897                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
8898        });
8899        ConditionResult::from(found)
8900    }
8901
8902    /// [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
8903    fn evaluate_2183(&self, ctx: &EvaluationContext) -> ConditionResult {
8904        // Cardinality: exactly twice per SG8 SEQ+Z01/Z80/Z81/Z98
8905        let found = ctx.find_segments("SEQ").iter().any(|s| {
8906            s.elements
8907                .first()
8908                .and_then(|e| e.first())
8909                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
8910        });
8911        ConditionResult::from(found)
8912    }
8913
8914    /// [2207] Für die ID der Lieferanten-ausfall-arbeits-summen-zeitreihe einmal je SG4 IDE+Z01 (Liste)
8915    fn evaluate_2207(&self, ctx: &EvaluationContext) -> ConditionResult {
8916        // Cardinality: Lieferantenausfallarbeitssummenzeitreihe once per SG4 IDE+Z01
8917        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
8918        ConditionResult::from(ide_count > 0)
8919    }
8920
8921    /// [2225] Einmal für jede Marktlokation bzw. Tranche für die der LF nicht die gemeldete Ansicht des NB teilt
8922    fn evaluate_2225(&self, _ctx: &EvaluationContext) -> ConditionResult {
8923        // TODO: implement
8924        ConditionResult::Unknown
8925    }
8926
8927    /// [2236] Code einmal je SG4 IDE+24 (Vorgang)
8928    fn evaluate_2236(&self, ctx: &EvaluationContext) -> ConditionResult {
8929        // Cardinality: code once per SG4 IDE+24
8930        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8931        ConditionResult::from(ide_count > 0)
8932    }
8933
8934    /// [2252] Einmal für jede Marktlokation bzw. Tranche, die in der Lieferantenausfallarbeits-summenzeitreihe berücksichtigt wurde
8935    fn evaluate_2252(&self, _ctx: &EvaluationContext) -> ConditionResult {
8936        // TODO: implement
8937        ConditionResult::Unknown
8938    }
8939
8940    /// [2261] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
8941    fn evaluate_2261(&self, ctx: &EvaluationContext) -> ConditionResult {
8942        // Cardinality: at least once per LOC+Z21 (Tranche) ID
8943        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
8944        ConditionResult::from(loc_count > 0)
8945    }
8946
8947    /// [2284] Für jede Messlokations-ID im SG5 LOC+Z17 (Messlokation) DE3225 genau einmal anzugeben
8948    fn evaluate_2284(&self, ctx: &EvaluationContext) -> ConditionResult {
8949        // Cardinality: exactly once per LOC+Z17 (Messlokation) ID
8950        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
8951        ConditionResult::from(loc_count > 0)
8952    }
8953
8954    /// [2286] Für jede SEQ+ZF3 (Daten der Messlokation) mindestens einmal anzugeben
8955    fn evaluate_2286(&self, ctx: &EvaluationContext) -> ConditionResult {
8956        // Cardinality: at least once per SEQ+ZF3 (Daten der Messlokation)
8957        let found = !ctx.find_segments_with_qualifier("SEQ", 0, "ZF3").is_empty();
8958        ConditionResult::from(found)
8959    }
8960
8961    /// [2287] Für jede SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) mindestens einmal anzugeben
8962    fn evaluate_2287(&self, ctx: &EvaluationContext) -> ConditionResult {
8963        // Cardinality: at least once per SEQ+Z03/ZF5 (Zähleinrichtungsdaten)
8964        let found = ctx.find_segments("SEQ").iter().any(|s| {
8965            s.elements
8966                .first()
8967                .and_then(|e| e.first())
8968                .is_some_and(|v| v == "Z03" || v == "ZF5")
8969        });
8970        ConditionResult::from(found)
8971    }
8972
8973    /// [2288] Einmal für jede Zeitreihe, die in der DZR berücksichtigt wurde
8974    fn evaluate_2288(&self, _ctx: &EvaluationContext) -> ConditionResult {
8975        // TODO: implement
8976        ConditionResult::Unknown
8977    }
8978
8979    /// [2307] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225 genau einmal anzugeben
8980    fn evaluate_2307(&self, ctx: &EvaluationContext) -> ConditionResult {
8981        // Cardinality: exactly once per LOC+Z21 (Tranche) ID
8982        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
8983        ConditionResult::from(loc_count > 0)
8984    }
8985
8986    /// [2308] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225, mindestens einmal anzugeben
8987    fn evaluate_2308(&self, ctx: &EvaluationContext) -> ConditionResult {
8988        // Cardinality: at least once per LOC+Z16 (Marktlokation) ID
8989        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
8990        ConditionResult::from(loc_count > 0)
8991    }
8992
8993    /// [2309] Für jede ID im SG5 LOC+Z17 (Messlokation) DE3225 mindestens einmal anzugeben
8994    fn evaluate_2309(&self, ctx: &EvaluationContext) -> ConditionResult {
8995        // Cardinality: at least once per LOC+Z17 (Messlokation) ID
8996        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
8997        ConditionResult::from(loc_count > 0)
8998    }
8999
9000    /// [2310] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225 genau einmal anzugeben
9001    fn evaluate_2310(&self, ctx: &EvaluationContext) -> ConditionResult {
9002        // Cardinality: exactly once per LOC+Z16 (Marktlokation) ID
9003        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9004        ConditionResult::from(loc_count > 0)
9005    }
9006
9007    /// [2311] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9008    fn evaluate_2311(&self, ctx: &EvaluationContext) -> ConditionResult {
9009        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9010        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9011        ConditionResult::from(loc_count > 0)
9012    }
9013
9014    /// [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...
9015    fn evaluate_2312(&self, ctx: &EvaluationContext) -> ConditionResult {
9016        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9017        // Check: RFF+Z33 DE1154 does NOT contain Objektcode 9992000001256 (Netzlokation)
9018        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
9019        let has_netzlokation = rff_z33.iter().any(|s| {
9020            s.elements
9021                .first()
9022                .and_then(|e| e.get(1))
9023                .is_some_and(|v| v == "9992000001256")
9024        });
9025        ConditionResult::from(!has_netzlokation)
9026    }
9027
9028    /// [2313] Je SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels) genau einmal anzugeben
9029    fn evaluate_2313(&self, ctx: &EvaluationContext) -> ConditionResult {
9030        // Check: SG8 with SEQ+Z58/ZC9/ZD0/ZD6 exists (cardinality constraint)
9031        let found = ctx.find_segments("SEQ").iter().any(|s| {
9032            s.elements
9033                .first()
9034                .and_then(|e| e.first())
9035                .is_some_and(|v| ["Z58", "ZC9", "ZD0", "ZD6"].contains(&v.as_str()))
9036        });
9037        ConditionResult::from(found)
9038    }
9039
9040    /// [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
9041    fn evaluate_2317(&self, ctx: &EvaluationContext) -> ConditionResult {
9042        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9043        // Check: SG10 CCI+++Z25 has CAV+MIW/MPW/MUW (Wandler types)
9044        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9045            s.elements
9046                .first()
9047                .and_then(|e| e.first())
9048                .is_some_and(|v| ["MIW", "MPW", "MUW"].contains(&v.as_str()))
9049        });
9050        ConditionResult::from(has_cav)
9051    }
9052
9053    /// [2318] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MBW (Blockstromwandler) vorhanden, ist das Segment genau einmal anzugeben
9054    fn evaluate_2318(&self, ctx: &EvaluationContext) -> ConditionResult {
9055        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9056        // Check: SG10 CCI+++Z25 has CAV+MBW (Blockstromwandler)
9057        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9058            s.elements
9059                .first()
9060                .and_then(|e| e.first())
9061                .is_some_and(|v| v == "MBW")
9062        });
9063        ConditionResult::from(has_cav)
9064    }
9065
9066    /// [2344] Einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
9067    fn evaluate_2344(&self, _ctx: &EvaluationContext) -> ConditionResult {
9068        // TODO: implement
9069        ConditionResult::Unknown
9070    }
9071
9072    /// [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...
9073    fn evaluate_2350(&self, _ctx: &EvaluationContext) -> ConditionResult {
9074        // TODO: implement
9075        ConditionResult::Unknown
9076    }
9077
9078    /// [2351] Einmal je ZP einer BK-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufgenommen wurde
9079    fn evaluate_2351(&self, _ctx: &EvaluationContext) -> ConditionResult {
9080        // TODO: implement
9081        ConditionResult::Unknown
9082    }
9083
9084    /// [2352] Einmal je ZP einer BG-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufge-nommen wurde
9085    fn evaluate_2352(&self, _ctx: &EvaluationContext) -> ConditionResult {
9086        // TODO: implement
9087        ConditionResult::Unknown
9088    }
9089
9090    /// [2356] Je SG5 LOC+Z18 (Netzlokation) ist genau einmal die Segmentgruppe anzugeben
9091    fn evaluate_2356(&self, ctx: &EvaluationContext) -> ConditionResult {
9092        // Cardinality: exactly once per SG5 LOC+Z18 (Netzlokation)
9093        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9094        ConditionResult::from(loc_count > 0)
9095    }
9096
9097    /// [2357] Je SG5 LOC+Z20 (Technische Ressource) ist genau einmal die Segmentgruppe anzugeben
9098    fn evaluate_2357(&self, ctx: &EvaluationContext) -> ConditionResult {
9099        // Cardinality: exactly once per SG5 LOC+Z20 (Technische Ressource)
9100        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z20").len();
9101        ConditionResult::from(loc_count > 0)
9102    }
9103
9104    /// [2358] Je SG5 LOC+Z19 (Steuerbare Ressource) ist genau einmal die Segmentgruppe anzugeben
9105    fn evaluate_2358(&self, ctx: &EvaluationContext) -> ConditionResult {
9106        // Cardinality: exactly once per SG5 LOC+Z19 (Steuerbare Ressource)
9107        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z19").len();
9108        ConditionResult::from(loc_count > 0)
9109    }
9110
9111    /// [2359] Für jede ID im SG5 LOC+Z16 / Z17 / Z20 (Marktlokation / Messlokation / Technische Ressource) DE3225 mindestens einmal anzugeben
9112    fn evaluate_2359(&self, ctx: &EvaluationContext) -> ConditionResult {
9113        // Cardinality: at least once per LOC+Z16/Z17/Z20 ID
9114        let found = ctx.find_segments("LOC").iter().any(|s| {
9115            s.elements
9116                .first()
9117                .and_then(|e| e.first())
9118                .is_some_and(|v| ["Z16", "Z17", "Z20"].contains(&v.as_str()))
9119        });
9120        ConditionResult::from(found)
9121    }
9122
9123    /// [2360] Für jede ID im SG5 LOC+Z18 (Netzlokation) DE3225 mindestens einmal anzugeben
9124    fn evaluate_2360(&self, ctx: &EvaluationContext) -> ConditionResult {
9125        // Cardinality: at least once per LOC+Z18 (Netzlokation) ID
9126        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9127        ConditionResult::from(loc_count > 0)
9128    }
9129
9130    /// [2361] Für jede ID im SG5 LOC+Z17 / Z20 / Z22 (Messlokation / Technische Ressource / ruhende Marktlokation) DE3225 mindestens einmal anzugeben
9131    fn evaluate_2361(&self, ctx: &EvaluationContext) -> ConditionResult {
9132        // Cardinality: at least once per LOC+Z17/Z20/Z22 ID
9133        let found = ctx.find_segments("LOC").iter().any(|s| {
9134            s.elements
9135                .first()
9136                .and_then(|e| e.first())
9137                .is_some_and(|v| ["Z17", "Z20", "Z22"].contains(&v.as_str()))
9138        });
9139        ConditionResult::from(found)
9140    }
9141}
9142
9143// HAND-EDITED: helpers for [223] / [265].
9144
9145use mig_types::segment::OwnedSegment;
9146
9147/// One SG8 repetition: its SEQ qualifier, the Marktlokation IDs of its
9148/// RFF+Z18, and its SG10s as `(CCIs, CAVs)`.
9149struct Sg8Block {
9150    seq: String,
9151    malo_refs: Vec<String>,
9152    sg10: Vec<(Vec<OwnedSegment>, Vec<OwnedSegment>)>,
9153}
9154
9155fn component(seg: &OwnedSegment, element: usize, comp: usize) -> Option<&str> {
9156    seg.elements
9157        .get(element)
9158        .and_then(|e| e.get(comp))
9159        .map(String::as_str)
9160}
9161
9162/// The message's SG8 repetitions, from the group navigator when there is one,
9163/// else from the segments in message order: SEQ opens an SG8, CCI opens an
9164/// SG10 within it that takes the CAVs after it. (`validate_pid_with_conditions`
9165/// evaluates BO4E input without a navigator.) `None` without either.
9166fn sg8_blocks(ctx: &EvaluationContext) -> Option<Vec<Sg8Block>> {
9167    let malo_refs = |rffs: &[OwnedSegment]| -> Vec<String> {
9168        rffs.iter()
9169            .filter(|s| component(s, 0, 0) == Some("Z18"))
9170            .filter_map(|s| component(s, 0, 1).map(str::to_string))
9171            .collect()
9172    };
9173    if let Some(nav) = ctx.navigator() {
9174        let sg8 = ["SG4", "SG8"];
9175        return Some(
9176            (0..nav.group_instance_count(&sg8))
9177                .map(|i| Sg8Block {
9178                    seq: nav
9179                        .find_segments_in_group("SEQ", &sg8, i)
9180                        .first()
9181                        .and_then(|s| component(s, 0, 0).map(str::to_string))
9182                        .unwrap_or_default(),
9183                    malo_refs: malo_refs(&nav.find_segments_in_group("RFF", &sg8, i)),
9184                    sg10: (0..nav.child_group_instance_count(&sg8, i, "SG10"))
9185                        .map(|j| {
9186                            (
9187                                nav.find_segments_in_child_group("CCI", &sg8, i, "SG10", j),
9188                                nav.find_segments_in_child_group("CAV", &sg8, i, "SG10", j),
9189                            )
9190                        })
9191                        .collect(),
9192                })
9193                .collect(),
9194        );
9195    }
9196    if ctx.segments.is_empty() {
9197        return None;
9198    }
9199    let mut blocks: Vec<Sg8Block> = Vec::new();
9200    let mut rffs: Vec<OwnedSegment> = Vec::new();
9201    for seg in ctx.segments {
9202        match seg.id.as_str() {
9203            "SEQ" => {
9204                if let Some(b) = blocks.last_mut() {
9205                    b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9206                }
9207                rffs.clear();
9208                blocks.push(Sg8Block {
9209                    seq: component(seg, 0, 0).unwrap_or_default().to_string(),
9210                    malo_refs: Vec::new(),
9211                    sg10: Vec::new(),
9212                });
9213            }
9214            // Segments after the SG8s end the last one.
9215            "NAD" | "UNT" | "IDE" => {
9216                if let Some(b) = blocks.last_mut() {
9217                    if b.malo_refs.is_empty() {
9218                        b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9219                    }
9220                }
9221                rffs.clear();
9222                if seg.id == "IDE" {
9223                    continue;
9224                }
9225                break;
9226            }
9227            "RFF" if !blocks.is_empty() => rffs.push(seg.clone()),
9228            "CCI" => {
9229                if let Some(b) = blocks.last_mut() {
9230                    b.sg10.push((vec![seg.clone()], Vec::new()));
9231                }
9232            }
9233            "CAV" => {
9234                if let Some((_, cavs)) = blocks.last_mut().and_then(|b| b.sg10.last_mut()) {
9235                    cavs.push(seg.clone());
9236                }
9237            }
9238            _ => {}
9239        }
9240    }
9241    if let Some(b) = blocks.last_mut() {
9242        if b.malo_refs.is_empty() {
9243            b.malo_refs = malo_refs(&rffs);
9244        }
9245    }
9246    Some(blocks)
9247}