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::*;
14// HAND-EDITED: instances of one SG4 compared with each other.
15use crate::eval::sg4_instances;
16use crate::eval::{Cluster, ConditionEvaluator, ConditionResult, EvaluationContext};
17// HAND-EDITED: shared cluster helpers for conditions [359]/[360]/[366]/[368].
18// Preserve across regeneration — see CLAUDE.md "Codelist Providers".
19use crate::eval::ebd_cluster::{all_sts_e01_in_cluster, ebd_ablehnung_except};
20
21/// Generated condition evaluator for UTILMD_Strom FV2504.
22pub struct UtilmdStromConditionEvaluatorFV2504 {
23    // External condition IDs that require runtime context.
24    external_conditions: std::collections::HashSet<u32>,
25}
26
27impl Default for UtilmdStromConditionEvaluatorFV2504 {
28    fn default() -> Self {
29        let mut external_conditions = std::collections::HashSet::new();
30        external_conditions.insert(1);
31        external_conditions.insert(4);
32        external_conditions.insert(5);
33        external_conditions.insert(6);
34        external_conditions.insert(8);
35        external_conditions.insert(9);
36        external_conditions.insert(14);
37        external_conditions.insert(31);
38        external_conditions.insert(33);
39        external_conditions.insert(36);
40        external_conditions.insert(38);
41        external_conditions.insert(39);
42        external_conditions.insert(40);
43        external_conditions.insert(75);
44        external_conditions.insert(98);
45        external_conditions.insert(99);
46        external_conditions.insert(151);
47        external_conditions.insert(233);
48        external_conditions.insert(241);
49        external_conditions.insert(251);
50        external_conditions.insert(292);
51        external_conditions.insert(314);
52        external_conditions.insert(315);
53        external_conditions.insert(323);
54        external_conditions.insert(412);
55        external_conditions.insert(425);
56        external_conditions.insert(426);
57        external_conditions.insert(427);
58        external_conditions.insert(428);
59        external_conditions.insert(429);
60        external_conditions.insert(433);
61        external_conditions.insert(435);
62        external_conditions.insert(453);
63        external_conditions.insert(459);
64        external_conditions.insert(460);
65        external_conditions.insert(706);
66        external_conditions.insert(717);
67        external_conditions.insert(2011);
68        Self {
69            external_conditions,
70        }
71    }
72}
73
74impl ConditionEvaluator for UtilmdStromConditionEvaluatorFV2504 {
75    fn message_type(&self) -> &str {
76        "UTILMD_Strom"
77    }
78
79    fn format_version(&self) -> &str {
80        "FV2504"
81    }
82
83    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
84        match condition {
85            1 => self.evaluate_1(ctx),
86            4 => self.evaluate_4(ctx),
87            5 => self.evaluate_5(ctx),
88            6 => self.evaluate_6(ctx),
89            7 => self.evaluate_7(ctx),
90            8 => self.evaluate_8(ctx),
91            9 => self.evaluate_9(ctx),
92            10 => self.evaluate_10(ctx),
93            11 => self.evaluate_11(ctx),
94            12 => self.evaluate_12(ctx),
95            13 => self.evaluate_13(ctx),
96            14 => self.evaluate_14(ctx),
97            15 => self.evaluate_15(ctx),
98            16 => self.evaluate_16(ctx),
99            17 => self.evaluate_17(ctx),
100            18 => self.evaluate_18(ctx),
101            19 => self.evaluate_19(ctx),
102            20 => self.evaluate_20(ctx),
103            21 => self.evaluate_21(ctx),
104            22 => self.evaluate_22(ctx),
105            23 => self.evaluate_23(ctx),
106            24 => self.evaluate_24(ctx),
107            25 => self.evaluate_25(ctx),
108            27 => self.evaluate_27(ctx),
109            30 => self.evaluate_30(ctx),
110            31 => self.evaluate_31(ctx),
111            32 => self.evaluate_32(ctx),
112            33 => self.evaluate_33(ctx),
113            34 => self.evaluate_34(ctx),
114            35 => self.evaluate_35(ctx),
115            36 => self.evaluate_36(ctx),
116            37 => self.evaluate_37(ctx),
117            38 => self.evaluate_38(ctx),
118            39 => self.evaluate_39(ctx),
119            40 => self.evaluate_40(ctx),
120            41 => self.evaluate_41(ctx),
121            42 => self.evaluate_42(ctx),
122            43 => self.evaluate_43(ctx),
123            44 => self.evaluate_44(ctx),
124            45 => self.evaluate_45(ctx),
125            46 => self.evaluate_46(ctx),
126            47 => self.evaluate_47(ctx),
127            48 => self.evaluate_48(ctx),
128            49 => self.evaluate_49(ctx),
129            50 => self.evaluate_50(ctx),
130            51 => self.evaluate_51(ctx),
131            52 => self.evaluate_52(ctx),
132            53 => self.evaluate_53(ctx),
133            54 => self.evaluate_54(ctx),
134            55 => self.evaluate_55(ctx),
135            56 => self.evaluate_56(ctx),
136            57 => self.evaluate_57(ctx),
137            58 => self.evaluate_58(ctx),
138            60 => self.evaluate_60(ctx),
139            61 => self.evaluate_61(ctx),
140            62 => self.evaluate_62(ctx),
141            63 => self.evaluate_63(ctx),
142            64 => self.evaluate_64(ctx),
143            65 => self.evaluate_65(ctx),
144            66 => self.evaluate_66(ctx),
145            67 => self.evaluate_67(ctx),
146            68 => self.evaluate_68(ctx),
147            69 => self.evaluate_69(ctx),
148            70 => self.evaluate_70(ctx),
149            71 => self.evaluate_71(ctx),
150            72 => self.evaluate_72(ctx),
151            73 => self.evaluate_73(ctx),
152            74 => self.evaluate_74(ctx),
153            75 => self.evaluate_75(ctx),
154            76 => self.evaluate_76(ctx),
155            77 => self.evaluate_77(ctx),
156            78 => self.evaluate_78(ctx),
157            83 => self.evaluate_83(ctx),
158            84 => self.evaluate_84(ctx),
159            85 => self.evaluate_85(ctx),
160            86 => self.evaluate_86(ctx),
161            87 => self.evaluate_87(ctx),
162            88 => self.evaluate_88(ctx),
163            89 => self.evaluate_89(ctx),
164            90 => self.evaluate_90(ctx),
165            91 => self.evaluate_91(ctx),
166            92 => self.evaluate_92(ctx),
167            93 => self.evaluate_93(ctx),
168            94 => self.evaluate_94(ctx),
169            95 => self.evaluate_95(ctx),
170            96 => self.evaluate_96(ctx),
171            97 => self.evaluate_97(ctx),
172            98 => self.evaluate_98(ctx),
173            99 => self.evaluate_99(ctx),
174            100 => self.evaluate_100(ctx),
175            101 => self.evaluate_101(ctx),
176            102 => self.evaluate_102(ctx),
177            103 => self.evaluate_103(ctx),
178            104 => self.evaluate_104(ctx),
179            105 => self.evaluate_105(ctx),
180            106 => self.evaluate_106(ctx),
181            107 => self.evaluate_107(ctx),
182            108 => self.evaluate_108(ctx),
183            110 => self.evaluate_110(ctx),
184            111 => self.evaluate_111(ctx),
185            112 => self.evaluate_112(ctx),
186            113 => self.evaluate_113(ctx),
187            114 => self.evaluate_114(ctx),
188            115 => self.evaluate_115(ctx),
189            116 => self.evaluate_116(ctx),
190            117 => self.evaluate_117(ctx),
191            118 => self.evaluate_118(ctx),
192            119 => self.evaluate_119(ctx),
193            120 => self.evaluate_120(ctx),
194            121 => self.evaluate_121(ctx),
195            122 => self.evaluate_122(ctx),
196            123 => self.evaluate_123(ctx),
197            124 => self.evaluate_124(ctx),
198            125 => self.evaluate_125(ctx),
199            126 => self.evaluate_126(ctx),
200            127 => self.evaluate_127(ctx),
201            128 => self.evaluate_128(ctx),
202            129 => self.evaluate_129(ctx),
203            130 => self.evaluate_130(ctx),
204            131 => self.evaluate_131(ctx),
205            132 => self.evaluate_132(ctx),
206            133 => self.evaluate_133(ctx),
207            134 => self.evaluate_134(ctx),
208            135 => self.evaluate_135(ctx),
209            136 => self.evaluate_136(ctx),
210            137 => self.evaluate_137(ctx),
211            138 => self.evaluate_138(ctx),
212            139 => self.evaluate_139(ctx),
213            140 => self.evaluate_140(ctx),
214            141 => self.evaluate_141(ctx),
215            142 => self.evaluate_142(ctx),
216            143 => self.evaluate_143(ctx),
217            144 => self.evaluate_144(ctx),
218            145 => self.evaluate_145(ctx),
219            146 => self.evaluate_146(ctx),
220            147 => self.evaluate_147(ctx),
221            148 => self.evaluate_148(ctx),
222            149 => self.evaluate_149(ctx),
223            150 => self.evaluate_150(ctx),
224            151 => self.evaluate_151(ctx),
225            152 => self.evaluate_152(ctx),
226            153 => self.evaluate_153(ctx),
227            156 => self.evaluate_156(ctx),
228            161 => self.evaluate_161(ctx),
229            162 => self.evaluate_162(ctx),
230            163 => self.evaluate_163(ctx),
231            164 => self.evaluate_164(ctx),
232            165 => self.evaluate_165(ctx),
233            166 => self.evaluate_166(ctx),
234            167 => self.evaluate_167(ctx),
235            170 => self.evaluate_170(ctx),
236            172 => self.evaluate_172(ctx),
237            173 => self.evaluate_173(ctx),
238            174 => self.evaluate_174(ctx),
239            175 => self.evaluate_175(ctx),
240            176 => self.evaluate_176(ctx),
241            177 => self.evaluate_177(ctx),
242            178 => self.evaluate_178(ctx),
243            179 => self.evaluate_179(ctx),
244            180 => self.evaluate_180(ctx),
245            184 => self.evaluate_184(ctx),
246            190 => self.evaluate_190(ctx),
247            191 => self.evaluate_191(ctx),
248            192 => self.evaluate_192(ctx),
249            193 => self.evaluate_193(ctx),
250            194 => self.evaluate_194(ctx),
251            195 => self.evaluate_195(ctx),
252            196 => self.evaluate_196(ctx),
253            197 => self.evaluate_197(ctx),
254            198 => self.evaluate_198(ctx),
255            199 => self.evaluate_199(ctx),
256            201 => self.evaluate_201(ctx),
257            202 => self.evaluate_202(ctx),
258            203 => self.evaluate_203(ctx),
259            204 => self.evaluate_204(ctx),
260            205 => self.evaluate_205(ctx),
261            206 => self.evaluate_206(ctx),
262            209 => self.evaluate_209(ctx),
263            210 => self.evaluate_210(ctx),
264            212 => self.evaluate_212(ctx),
265            213 => self.evaluate_213(ctx),
266            215 => self.evaluate_215(ctx),
267            216 => self.evaluate_216(ctx),
268            217 => self.evaluate_217(ctx),
269            219 => self.evaluate_219(ctx),
270            220 => self.evaluate_220(ctx),
271            221 => self.evaluate_221(ctx),
272            223 => self.evaluate_223(ctx),
273            224 => self.evaluate_224(ctx),
274            227 => self.evaluate_227(ctx),
275            229 => self.evaluate_229(ctx),
276            232 => self.evaluate_232(ctx),
277            233 => self.evaluate_233(ctx),
278            234 => self.evaluate_234(ctx),
279            237 => self.evaluate_237(ctx),
280            238 => self.evaluate_238(ctx),
281            239 => self.evaluate_239(ctx),
282            240 => self.evaluate_240(ctx),
283            241 => self.evaluate_241(ctx),
284            243 => self.evaluate_243(ctx),
285            244 => self.evaluate_244(ctx),
286            248 => self.evaluate_248(ctx),
287            249 => self.evaluate_249(ctx),
288            251 => self.evaluate_251(ctx),
289            252 => self.evaluate_252(ctx),
290            254 => self.evaluate_254(ctx),
291            255 => self.evaluate_255(ctx),
292            256 => self.evaluate_256(ctx),
293            257 => self.evaluate_257(ctx),
294            259 => self.evaluate_259(ctx),
295            261 => self.evaluate_261(ctx),
296            262 => self.evaluate_262(ctx),
297            265 => self.evaluate_265(ctx),
298            266 => self.evaluate_266(ctx),
299            267 => self.evaluate_267(ctx),
300            268 => self.evaluate_268(ctx),
301            269 => self.evaluate_269(ctx),
302            270 => self.evaluate_270(ctx),
303            273 => self.evaluate_273(ctx),
304            279 => self.evaluate_279(ctx),
305            280 => self.evaluate_280(ctx),
306            282 => self.evaluate_282(ctx),
307            284 => self.evaluate_284(ctx),
308            285 => self.evaluate_285(ctx),
309            286 => self.evaluate_286(ctx),
310            287 => self.evaluate_287(ctx),
311            288 => self.evaluate_288(ctx),
312            291 => self.evaluate_291(ctx),
313            292 => self.evaluate_292(ctx),
314            293 => self.evaluate_293(ctx),
315            294 => self.evaluate_294(ctx),
316            295 => self.evaluate_295(ctx),
317            296 => self.evaluate_296(ctx),
318            297 => self.evaluate_297(ctx),
319            298 => self.evaluate_298(ctx),
320            299 => self.evaluate_299(ctx),
321            300 => self.evaluate_300(ctx),
322            301 => self.evaluate_301(ctx),
323            302 => self.evaluate_302(ctx),
324            303 => self.evaluate_303(ctx),
325            304 => self.evaluate_304(ctx),
326            305 => self.evaluate_305(ctx),
327            306 => self.evaluate_306(ctx),
328            307 => self.evaluate_307(ctx),
329            309 => self.evaluate_309(ctx),
330            314 => self.evaluate_314(ctx),
331            315 => self.evaluate_315(ctx),
332            316 => self.evaluate_316(ctx),
333            317 => self.evaluate_317(ctx),
334            318 => self.evaluate_318(ctx),
335            321 => self.evaluate_321(ctx),
336            322 => self.evaluate_322(ctx),
337            323 => self.evaluate_323(ctx),
338            327 => self.evaluate_327(ctx),
339            329 => self.evaluate_329(ctx),
340            331 => self.evaluate_331(ctx),
341            332 => self.evaluate_332(ctx),
342            333 => self.evaluate_333(ctx),
343            334 => self.evaluate_334(ctx),
344            335 => self.evaluate_335(ctx),
345            336 => self.evaluate_336(ctx),
346            337 => self.evaluate_337(ctx),
347            338 => self.evaluate_338(ctx),
348            339 => self.evaluate_339(ctx),
349            340 => self.evaluate_340(ctx),
350            341 => self.evaluate_341(ctx),
351            342 => self.evaluate_342(ctx),
352            344 => self.evaluate_344(ctx),
353            345 => self.evaluate_345(ctx),
354            346 => self.evaluate_346(ctx),
355            347 => self.evaluate_347(ctx),
356            348 => self.evaluate_348(ctx),
357            349 => self.evaluate_349(ctx),
358            350 => self.evaluate_350(ctx),
359            351 => self.evaluate_351(ctx),
360            352 => self.evaluate_352(ctx),
361            355 => self.evaluate_355(ctx),
362            356 => self.evaluate_356(ctx),
363            357 => self.evaluate_357(ctx),
364            358 => self.evaluate_358(ctx),
365            359 => self.evaluate_359(ctx),
366            360 => self.evaluate_360(ctx),
367            363 => self.evaluate_363(ctx),
368            365 => self.evaluate_365(ctx),
369            366 => self.evaluate_366(ctx),
370            367 => self.evaluate_367(ctx),
371            368 => self.evaluate_368(ctx),
372            370 => self.evaluate_370(ctx),
373            371 => self.evaluate_371(ctx),
374            372 => self.evaluate_372(ctx),
375            373 => self.evaluate_373(ctx),
376            375 => self.evaluate_375(ctx),
377            376 => self.evaluate_376(ctx),
378            377 => self.evaluate_377(ctx),
379            378 => self.evaluate_378(ctx),
380            379 => self.evaluate_379(ctx),
381            380 => self.evaluate_380(ctx),
382            384 => self.evaluate_384(ctx),
383            386 => self.evaluate_386(ctx),
384            387 => self.evaluate_387(ctx),
385            388 => self.evaluate_388(ctx),
386            391 => self.evaluate_391(ctx),
387            392 => self.evaluate_392(ctx),
388            393 => self.evaluate_393(ctx),
389            394 => self.evaluate_394(ctx),
390            395 => self.evaluate_395(ctx),
391            396 => self.evaluate_396(ctx),
392            397 => self.evaluate_397(ctx),
393            398 => self.evaluate_398(ctx),
394            399 => self.evaluate_399(ctx),
395            401 => self.evaluate_401(ctx),
396            402 => self.evaluate_402(ctx),
397            403 => self.evaluate_403(ctx),
398            404 => self.evaluate_404(ctx),
399            405 => self.evaluate_405(ctx),
400            406 => self.evaluate_406(ctx),
401            407 => self.evaluate_407(ctx),
402            408 => self.evaluate_408(ctx),
403            409 => self.evaluate_409(ctx),
404            410 => self.evaluate_410(ctx),
405            411 => self.evaluate_411(ctx),
406            412 => self.evaluate_412(ctx),
407            413 => self.evaluate_413(ctx),
408            414 => self.evaluate_414(ctx),
409            415 => self.evaluate_415(ctx),
410            416 => self.evaluate_416(ctx),
411            417 => self.evaluate_417(ctx),
412            419 => self.evaluate_419(ctx),
413            420 => self.evaluate_420(ctx),
414            421 => self.evaluate_421(ctx),
415            425 => self.evaluate_425(ctx),
416            426 => self.evaluate_426(ctx),
417            427 => self.evaluate_427(ctx),
418            428 => self.evaluate_428(ctx),
419            429 => self.evaluate_429(ctx),
420            430 => self.evaluate_430(ctx),
421            431 => self.evaluate_431(ctx),
422            432 => self.evaluate_432(ctx),
423            433 => self.evaluate_433(ctx),
424            435 => self.evaluate_435(ctx),
425            436 => self.evaluate_436(ctx),
426            437 => self.evaluate_437(ctx),
427            438 => self.evaluate_438(ctx),
428            440 => self.evaluate_440(ctx),
429            441 => self.evaluate_441(ctx),
430            442 => self.evaluate_442(ctx),
431            444 => self.evaluate_444(ctx),
432            445 => self.evaluate_445(ctx),
433            446 => self.evaluate_446(ctx),
434            447 => self.evaluate_447(ctx),
435            448 => self.evaluate_448(ctx),
436            449 => self.evaluate_449(ctx),
437            450 => self.evaluate_450(ctx),
438            451 => self.evaluate_451(ctx),
439            452 => self.evaluate_452(ctx),
440            453 => self.evaluate_453(ctx),
441            454 => self.evaluate_454(ctx),
442            455 => self.evaluate_455(ctx),
443            456 => self.evaluate_456(ctx),
444            457 => self.evaluate_457(ctx),
445            458 => self.evaluate_458(ctx),
446            459 => self.evaluate_459(ctx),
447            460 => self.evaluate_460(ctx),
448            461 => self.evaluate_461(ctx),
449            462 => self.evaluate_462(ctx),
450            463 => self.evaluate_463(ctx),
451            465 => self.evaluate_465(ctx),
452            466 => self.evaluate_466(ctx),
453            467 => self.evaluate_467(ctx),
454            468 => self.evaluate_468(ctx),
455            469 => self.evaluate_469(ctx),
456            470 => self.evaluate_470(ctx),
457            471 => self.evaluate_471(ctx),
458            472 => self.evaluate_472(ctx),
459            473 => self.evaluate_473(ctx),
460            474 => self.evaluate_474(ctx),
461            475 => self.evaluate_475(ctx),
462            476 => self.evaluate_476(ctx),
463            477 => self.evaluate_477(ctx),
464            478 => self.evaluate_478(ctx),
465            479 => self.evaluate_479(ctx),
466            480 => self.evaluate_480(ctx),
467            481 => self.evaluate_481(ctx),
468            483 => self.evaluate_483(ctx),
469            484 => self.evaluate_484(ctx),
470            487 => self.evaluate_487(ctx),
471            489 => self.evaluate_489(ctx),
472            490 => self.evaluate_490(ctx),
473            491 => self.evaluate_491(ctx),
474            494 => self.evaluate_494(ctx),
475            500 => self.evaluate_500(ctx),
476            501 => self.evaluate_501(ctx),
477            502 => self.evaluate_502(ctx),
478            503 => self.evaluate_503(ctx),
479            504 => self.evaluate_504(ctx),
480            505 => self.evaluate_505(ctx),
481            506 => self.evaluate_506(ctx),
482            507 => self.evaluate_507(ctx),
483            508 => self.evaluate_508(ctx),
484            509 => self.evaluate_509(ctx),
485            510 => self.evaluate_510(ctx),
486            511 => self.evaluate_511(ctx),
487            512 => self.evaluate_512(ctx),
488            513 => self.evaluate_513(ctx),
489            514 => self.evaluate_514(ctx),
490            515 => self.evaluate_515(ctx),
491            516 => self.evaluate_516(ctx),
492            517 => self.evaluate_517(ctx),
493            518 => self.evaluate_518(ctx),
494            519 => self.evaluate_519(ctx),
495            520 => self.evaluate_520(ctx),
496            521 => self.evaluate_521(ctx),
497            522 => self.evaluate_522(ctx),
498            523 => self.evaluate_523(ctx),
499            524 => self.evaluate_524(ctx),
500            525 => self.evaluate_525(ctx),
501            526 => self.evaluate_526(ctx),
502            527 => self.evaluate_527(ctx),
503            528 => self.evaluate_528(ctx),
504            529 => self.evaluate_529(ctx),
505            530 => self.evaluate_530(ctx),
506            531 => self.evaluate_531(ctx),
507            532 => self.evaluate_532(ctx),
508            533 => self.evaluate_533(ctx),
509            555 => self.evaluate_555(ctx),
510            556 => self.evaluate_556(ctx),
511            558 => self.evaluate_558(ctx),
512            559 => self.evaluate_559(ctx),
513            563 => self.evaluate_563(ctx),
514            566 => self.evaluate_566(ctx),
515            567 => self.evaluate_567(ctx),
516            568 => self.evaluate_568(ctx),
517            569 => self.evaluate_569(ctx),
518            570 => self.evaluate_570(ctx),
519            572 => self.evaluate_572(ctx),
520            576 => self.evaluate_576(ctx),
521            577 => self.evaluate_577(ctx),
522            579 => self.evaluate_579(ctx),
523            580 => self.evaluate_580(ctx),
524            581 => self.evaluate_581(ctx),
525            586 => self.evaluate_586(ctx),
526            590 => self.evaluate_590(ctx),
527            594 => self.evaluate_594(ctx),
528            599 => self.evaluate_599(ctx),
529            601 => self.evaluate_601(ctx),
530            606 => self.evaluate_606(ctx),
531            609 => self.evaluate_609(ctx),
532            611 => self.evaluate_611(ctx),
533            614 => self.evaluate_614(ctx),
534            617 => self.evaluate_617(ctx),
535            618 => self.evaluate_618(ctx),
536            619 => self.evaluate_619(ctx),
537            621 => self.evaluate_621(ctx),
538            622 => self.evaluate_622(ctx),
539            623 => self.evaluate_623(ctx),
540            630 => self.evaluate_630(ctx),
541            631 => self.evaluate_631(ctx),
542            632 => self.evaluate_632(ctx),
543            637 => self.evaluate_637(ctx),
544            638 => self.evaluate_638(ctx),
545            639 => self.evaluate_639(ctx),
546            640 => self.evaluate_640(ctx),
547            641 => self.evaluate_641(ctx),
548            642 => self.evaluate_642(ctx),
549            643 => self.evaluate_643(ctx),
550            645 => self.evaluate_645(ctx),
551            646 => self.evaluate_646(ctx),
552            647 => self.evaluate_647(ctx),
553            648 => self.evaluate_648(ctx),
554            651 => self.evaluate_651(ctx),
555            653 => self.evaluate_653(ctx),
556            655 => self.evaluate_655(ctx),
557            659 => self.evaluate_659(ctx),
558            660 => self.evaluate_660(ctx),
559            662 => self.evaluate_662(ctx),
560            663 => self.evaluate_663(ctx),
561            664 => self.evaluate_664(ctx),
562            665 => self.evaluate_665(ctx),
563            667 => self.evaluate_667(ctx),
564            668 => self.evaluate_668(ctx),
565            670 => self.evaluate_670(ctx),
566            671 => self.evaluate_671(ctx),
567            672 => self.evaluate_672(ctx),
568            673 => self.evaluate_673(ctx),
569            674 => self.evaluate_674(ctx),
570            675 => self.evaluate_675(ctx),
571            677 => self.evaluate_677(ctx),
572            678 => self.evaluate_678(ctx),
573            679 => self.evaluate_679(ctx),
574            680 => self.evaluate_680(ctx),
575            682 => self.evaluate_682(ctx),
576            683 => self.evaluate_683(ctx),
577            684 => self.evaluate_684(ctx),
578            685 => self.evaluate_685(ctx),
579            687 => self.evaluate_687(ctx),
580            688 => self.evaluate_688(ctx),
581            689 => self.evaluate_689(ctx),
582            690 => self.evaluate_690(ctx),
583            693 => self.evaluate_693(ctx),
584            694 => self.evaluate_694(ctx),
585            695 => self.evaluate_695(ctx),
586            696 => self.evaluate_696(ctx),
587            698 => self.evaluate_698(ctx),
588            699 => self.evaluate_699(ctx),
589            700 => self.evaluate_700(ctx),
590            704 => self.evaluate_704(ctx),
591            705 => self.evaluate_705(ctx),
592            706 => self.evaluate_706(ctx),
593            707 => self.evaluate_707(ctx),
594            708 => self.evaluate_708(ctx),
595            709 => self.evaluate_709(ctx),
596            710 => self.evaluate_710(ctx),
597            711 => self.evaluate_711(ctx),
598            712 => self.evaluate_712(ctx),
599            713 => self.evaluate_713(ctx),
600            714 => self.evaluate_714(ctx),
601            715 => self.evaluate_715(ctx),
602            716 => self.evaluate_716(ctx),
603            717 => self.evaluate_717(ctx),
604            718 => self.evaluate_718(ctx),
605            719 => self.evaluate_719(ctx),
606            902 => self.evaluate_902(ctx),
607            910 => self.evaluate_910(ctx),
608            914 => self.evaluate_914(ctx),
609            922 => self.evaluate_922(ctx),
610            926 => self.evaluate_926(ctx),
611            930 => self.evaluate_930(ctx),
612            931 => self.evaluate_931(ctx),
613            932 => self.evaluate_932(ctx),
614            933 => self.evaluate_933(ctx),
615            937 => self.evaluate_937(ctx),
616            938 => self.evaluate_938(ctx),
617            939 => self.evaluate_939(ctx),
618            940 => self.evaluate_940(ctx),
619            942 => self.evaluate_942(ctx),
620            943 => self.evaluate_943(ctx),
621            946 => self.evaluate_946(ctx),
622            948 => self.evaluate_948(ctx),
623            950 => self.evaluate_950(ctx),
624            951 => self.evaluate_951(ctx),
625            952 => self.evaluate_952(ctx),
626            955 => self.evaluate_955(ctx),
627            957 => self.evaluate_957(ctx),
628            960 => self.evaluate_960(ctx),
629            961 => self.evaluate_961(ctx),
630            967 => self.evaluate_967(ctx),
631            2001 => self.evaluate_2001(ctx),
632            2002 => self.evaluate_2002(ctx),
633            2003 => self.evaluate_2003(ctx),
634            2004 => self.evaluate_2004(ctx),
635            2005 => self.evaluate_2005(ctx),
636            2006 => self.evaluate_2006(ctx),
637            2007 => self.evaluate_2007(ctx),
638            2008 => self.evaluate_2008(ctx),
639            2009 => self.evaluate_2009(ctx),
640            2010 => self.evaluate_2010(ctx),
641            2011 => self.evaluate_2011(ctx),
642            2012 => self.evaluate_2012(ctx),
643            2013 => self.evaluate_2013(ctx),
644            2014 => self.evaluate_2014(ctx),
645            2015 => self.evaluate_2015(ctx),
646            2016 => self.evaluate_2016(ctx),
647            2017 => self.evaluate_2017(ctx),
648            2018 => self.evaluate_2018(ctx),
649            2060 => self.evaluate_2060(ctx),
650            2061 => self.evaluate_2061(ctx),
651            2071 => self.evaluate_2071(ctx),
652            2073 => self.evaluate_2073(ctx),
653            2075 => self.evaluate_2075(ctx),
654            2080 => self.evaluate_2080(ctx),
655            2095 => self.evaluate_2095(ctx),
656            2096 => self.evaluate_2096(ctx),
657            2119 => self.evaluate_2119(ctx),
658            2140 => self.evaluate_2140(ctx),
659            2182 => self.evaluate_2182(ctx),
660            2183 => self.evaluate_2183(ctx),
661            2207 => self.evaluate_2207(ctx),
662            2225 => self.evaluate_2225(ctx),
663            2236 => self.evaluate_2236(ctx),
664            2252 => self.evaluate_2252(ctx),
665            2261 => self.evaluate_2261(ctx),
666            2284 => self.evaluate_2284(ctx),
667            2286 => self.evaluate_2286(ctx),
668            2287 => self.evaluate_2287(ctx),
669            2288 => self.evaluate_2288(ctx),
670            2307 => self.evaluate_2307(ctx),
671            2308 => self.evaluate_2308(ctx),
672            2309 => self.evaluate_2309(ctx),
673            2310 => self.evaluate_2310(ctx),
674            2311 => self.evaluate_2311(ctx),
675            2312 => self.evaluate_2312(ctx),
676            2313 => self.evaluate_2313(ctx),
677            2317 => self.evaluate_2317(ctx),
678            2318 => self.evaluate_2318(ctx),
679            2344 => self.evaluate_2344(ctx),
680            2350 => self.evaluate_2350(ctx),
681            2351 => self.evaluate_2351(ctx),
682            2352 => self.evaluate_2352(ctx),
683            2356 => self.evaluate_2356(ctx),
684            2357 => self.evaluate_2357(ctx),
685            2358 => self.evaluate_2358(ctx),
686            2359 => self.evaluate_2359(ctx),
687            2360 => self.evaluate_2360(ctx),
688            2361 => self.evaluate_2361(ctx),
689            _ => ConditionResult::Unknown,
690        }
691    }
692
693    fn is_external(&self, condition: u32) -> bool {
694        self.external_conditions.contains(&condition)
695    }
696    fn is_known(&self, condition: u32) -> bool {
697        matches!(
698            condition,
699            1 | 4
700                | 5
701                | 6
702                | 7
703                | 8
704                | 9
705                | 10
706                | 11
707                | 12
708                | 13
709                | 14
710                | 15
711                | 16
712                | 17
713                | 18
714                | 19
715                | 20
716                | 21
717                | 22
718                | 23
719                | 24
720                | 25
721                | 27
722                | 30
723                | 31
724                | 32
725                | 33
726                | 34
727                | 35
728                | 36
729                | 37
730                | 38
731                | 39
732                | 40
733                | 41
734                | 42
735                | 43
736                | 44
737                | 45
738                | 46
739                | 47
740                | 48
741                | 49
742                | 50
743                | 51
744                | 52
745                | 53
746                | 54
747                | 55
748                | 56
749                | 57
750                | 58
751                | 60
752                | 61
753                | 62
754                | 63
755                | 64
756                | 65
757                | 66
758                | 67
759                | 68
760                | 69
761                | 70
762                | 71
763                | 72
764                | 73
765                | 74
766                | 75
767                | 76
768                | 77
769                | 78
770                | 83
771                | 84
772                | 85
773                | 86
774                | 87
775                | 88
776                | 89
777                | 90
778                | 91
779                | 92
780                | 93
781                | 94
782                | 95
783                | 96
784                | 97
785                | 98
786                | 99
787                | 100
788                | 101
789                | 102
790                | 103
791                | 104
792                | 105
793                | 106
794                | 107
795                | 108
796                | 110
797                | 111
798                | 112
799                | 113
800                | 114
801                | 115
802                | 116
803                | 117
804                | 118
805                | 119
806                | 120
807                | 121
808                | 122
809                | 123
810                | 124
811                | 125
812                | 126
813                | 127
814                | 128
815                | 129
816                | 130
817                | 131
818                | 132
819                | 133
820                | 134
821                | 135
822                | 136
823                | 137
824                | 138
825                | 139
826                | 140
827                | 141
828                | 142
829                | 143
830                | 144
831                | 145
832                | 146
833                | 147
834                | 148
835                | 149
836                | 150
837                | 151
838                | 152
839                | 153
840                | 156
841                | 161
842                | 162
843                | 163
844                | 164
845                | 165
846                | 166
847                | 167
848                | 170
849                | 172
850                | 173
851                | 174
852                | 175
853                | 176
854                | 177
855                | 178
856                | 179
857                | 180
858                | 184
859                | 190
860                | 191
861                | 192
862                | 193
863                | 194
864                | 195
865                | 196
866                | 197
867                | 198
868                | 199
869                | 201
870                | 202
871                | 203
872                | 204
873                | 205
874                | 206
875                | 209
876                | 210
877                | 212
878                | 213
879                | 215
880                | 216
881                | 217
882                | 219
883                | 220
884                | 221
885                | 223
886                | 224
887                | 227
888                | 229
889                | 232
890                | 233
891                | 234
892                | 237
893                | 238
894                | 239
895                | 240
896                | 241
897                | 243
898                | 244
899                | 248
900                | 249
901                | 251
902                | 252
903                | 254
904                | 255
905                | 256
906                | 257
907                | 259
908                | 261
909                | 262
910                | 265
911                | 266
912                | 267
913                | 268
914                | 269
915                | 270
916                | 273
917                | 279
918                | 280
919                | 282
920                | 284
921                | 285
922                | 286
923                | 287
924                | 288
925                | 291
926                | 292
927                | 293
928                | 294
929                | 295
930                | 296
931                | 297
932                | 298
933                | 299
934                | 300
935                | 301
936                | 302
937                | 303
938                | 304
939                | 305
940                | 306
941                | 307
942                | 309
943                | 314
944                | 315
945                | 316
946                | 317
947                | 318
948                | 321
949                | 322
950                | 323
951                | 327
952                | 329
953                | 331
954                | 332
955                | 333
956                | 334
957                | 335
958                | 336
959                | 337
960                | 338
961                | 339
962                | 340
963                | 341
964                | 342
965                | 344
966                | 345
967                | 346
968                | 347
969                | 348
970                | 349
971                | 350
972                | 351
973                | 352
974                | 355
975                | 356
976                | 357
977                | 358
978                | 359
979                | 360
980                | 363
981                | 365
982                | 366
983                | 367
984                | 368
985                | 370
986                | 371
987                | 372
988                | 373
989                | 375
990                | 376
991                | 377
992                | 378
993                | 379
994                | 380
995                | 384
996                | 386
997                | 387
998                | 388
999                | 391
1000                | 392
1001                | 393
1002                | 394
1003                | 395
1004                | 396
1005                | 397
1006                | 398
1007                | 399
1008                | 401
1009                | 402
1010                | 403
1011                | 404
1012                | 405
1013                | 406
1014                | 407
1015                | 408
1016                | 409
1017                | 410
1018                | 411
1019                | 412
1020                | 413
1021                | 414
1022                | 415
1023                | 416
1024                | 417
1025                | 419
1026                | 420
1027                | 421
1028                | 425
1029                | 426
1030                | 427
1031                | 428
1032                | 429
1033                | 430
1034                | 431
1035                | 432
1036                | 433
1037                | 435
1038                | 436
1039                | 437
1040                | 438
1041                | 440
1042                | 441
1043                | 442
1044                | 444
1045                | 445
1046                | 446
1047                | 447
1048                | 448
1049                | 449
1050                | 450
1051                | 451
1052                | 452
1053                | 453
1054                | 454
1055                | 455
1056                | 456
1057                | 457
1058                | 458
1059                | 459
1060                | 460
1061                | 461
1062                | 462
1063                | 463
1064                | 465
1065                | 466
1066                | 467
1067                | 468
1068                | 469
1069                | 470
1070                | 471
1071                | 472
1072                | 473
1073                | 474
1074                | 475
1075                | 476
1076                | 477
1077                | 478
1078                | 479
1079                | 480
1080                | 481
1081                | 483
1082                | 484
1083                | 487
1084                | 489
1085                | 490
1086                | 491
1087                | 494
1088                | 500
1089                | 501
1090                | 502
1091                | 503
1092                | 504
1093                | 505
1094                | 506
1095                | 507
1096                | 508
1097                | 509
1098                | 510
1099                | 511
1100                | 512
1101                | 513
1102                | 514
1103                | 515
1104                | 516
1105                | 517
1106                | 518
1107                | 519
1108                | 520
1109                | 521
1110                | 522
1111                | 523
1112                | 524
1113                | 525
1114                | 526
1115                | 527
1116                | 528
1117                | 529
1118                | 530
1119                | 531
1120                | 532
1121                | 533
1122                | 555
1123                | 556
1124                | 558
1125                | 559
1126                | 563
1127                | 566
1128                | 567
1129                | 568
1130                | 569
1131                | 570
1132                | 572
1133                | 576
1134                | 577
1135                | 579
1136                | 580
1137                | 581
1138                | 586
1139                | 590
1140                | 594
1141                | 599
1142                | 601
1143                | 606
1144                | 609
1145                | 611
1146                | 614
1147                | 617
1148                | 618
1149                | 619
1150                | 621
1151                | 622
1152                | 623
1153                | 630
1154                | 631
1155                | 632
1156                | 637
1157                | 638
1158                | 639
1159                | 640
1160                | 641
1161                | 642
1162                | 643
1163                | 645
1164                | 646
1165                | 647
1166                | 648
1167                | 651
1168                | 653
1169                | 655
1170                | 659
1171                | 660
1172                | 662
1173                | 663
1174                | 664
1175                | 665
1176                | 667
1177                | 668
1178                | 670
1179                | 671
1180                | 672
1181                | 673
1182                | 674
1183                | 675
1184                | 677
1185                | 678
1186                | 679
1187                | 680
1188                | 682
1189                | 683
1190                | 684
1191                | 685
1192                | 687
1193                | 688
1194                | 689
1195                | 690
1196                | 693
1197                | 694
1198                | 695
1199                | 696
1200                | 698
1201                | 699
1202                | 700
1203                | 704
1204                | 705
1205                | 706
1206                | 707
1207                | 708
1208                | 709
1209                | 710
1210                | 711
1211                | 712
1212                | 713
1213                | 714
1214                | 715
1215                | 716
1216                | 717
1217                | 718
1218                | 719
1219                | 902
1220                | 910
1221                | 914
1222                | 922
1223                | 926
1224                | 930
1225                | 931
1226                | 932
1227                | 933
1228                | 937
1229                | 938
1230                | 939
1231                | 940
1232                | 942
1233                | 943
1234                | 946
1235                | 948
1236                | 950
1237                | 951
1238                | 952
1239                | 955
1240                | 957
1241                | 960
1242                | 961
1243                | 967
1244                | 2001
1245                | 2002
1246                | 2003
1247                | 2004
1248                | 2005
1249                | 2006
1250                | 2007
1251                | 2008
1252                | 2009
1253                | 2010
1254                | 2011
1255                | 2012
1256                | 2013
1257                | 2014
1258                | 2015
1259                | 2016
1260                | 2017
1261                | 2018
1262                | 2060
1263                | 2061
1264                | 2071
1265                | 2073
1266                | 2075
1267                | 2080
1268                | 2095
1269                | 2096
1270                | 2119
1271                | 2140
1272                | 2182
1273                | 2183
1274                | 2207
1275                | 2225
1276                | 2236
1277                | 2252
1278                | 2261
1279                | 2284
1280                | 2286
1281                | 2287
1282                | 2288
1283                | 2307
1284                | 2308
1285                | 2309
1286                | 2310
1287                | 2311
1288                | 2312
1289                | 2313
1290                | 2317
1291                | 2318
1292                | 2344
1293                | 2350
1294                | 2351
1295                | 2352
1296                | 2356
1297                | 2357
1298                | 2358
1299                | 2359
1300                | 2360
1301                | 2361
1302        )
1303    }
1304}
1305
1306impl UtilmdStromConditionEvaluatorFV2504 {
1307    /// [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...
1308    // 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)
1309    fn evaluate_2006(&self, ctx: &EvaluationContext) -> ConditionResult {
1310        let nav = match ctx.navigator() {
1311            Some(n) => n,
1312            None => return ConditionResult::Unknown,
1313        };
1314        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1315        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1316        for i in 0..sg8_count {
1317            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1318            let is_z01 = seq_segs.iter().any(|s| {
1319                s.elements
1320                    .first()
1321                    .and_then(|e| e.first())
1322                    .is_some_and(|v| v == "Z01")
1323            });
1324            if is_z01 && matches!(self.evaluate(199, ctx), ConditionResult::True) {
1325                for seq in &seq_segs {
1326                    if seq
1327                        .elements
1328                        .first()
1329                        .and_then(|e| e.first())
1330                        .is_some_and(|v| v == "Z01")
1331                    {
1332                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1333                            if !zid.is_empty() {
1334                                qualifying_zeitraum_ids.push(zid.clone());
1335                            }
1336                        }
1337                    }
1338                }
1339            }
1340        }
1341        if qualifying_zeitraum_ids.is_empty() {
1342            return ConditionResult::Unknown;
1343        }
1344        ConditionResult::True
1345    }
1346
1347    /// [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...
1348    // 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)
1349    fn evaluate_2007(&self, ctx: &EvaluationContext) -> ConditionResult {
1350        let nav = match ctx.navigator() {
1351            Some(n) => n,
1352            None => return ConditionResult::Unknown,
1353        };
1354        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1355        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1356        for i in 0..sg8_count {
1357            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1358            let is_z01 = seq_segs.iter().any(|s| {
1359                s.elements
1360                    .first()
1361                    .and_then(|e| e.first())
1362                    .is_some_and(|v| v == "Z01")
1363            });
1364            if is_z01 && matches!(self.evaluate(199, ctx), ConditionResult::True) {
1365                for seq in &seq_segs {
1366                    if seq
1367                        .elements
1368                        .first()
1369                        .and_then(|e| e.first())
1370                        .is_some_and(|v| v == "Z01")
1371                    {
1372                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1373                            if !zid.is_empty() {
1374                                qualifying_zeitraum_ids.push(zid.clone());
1375                            }
1376                        }
1377                    }
1378                }
1379            }
1380        }
1381        if qualifying_zeitraum_ids.is_empty() {
1382            return ConditionResult::Unknown;
1383        }
1384        ConditionResult::True
1385    }
1386
1387    /// [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...
1388    // 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)
1389    fn evaluate_2008(&self, ctx: &EvaluationContext) -> ConditionResult {
1390        let nav = match ctx.navigator() {
1391            Some(n) => n,
1392            None => return ConditionResult::Unknown,
1393        };
1394        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1395        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1396        for i in 0..sg8_count {
1397            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1398            let is_z01 = seq_segs.iter().any(|s| {
1399                s.elements
1400                    .first()
1401                    .and_then(|e| e.first())
1402                    .is_some_and(|v| v == "Z01")
1403            });
1404            if is_z01 && matches!(self.evaluate(89, ctx), ConditionResult::True) {
1405                for seq in &seq_segs {
1406                    if seq
1407                        .elements
1408                        .first()
1409                        .and_then(|e| e.first())
1410                        .is_some_and(|v| v == "Z01")
1411                    {
1412                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1413                            if !zid.is_empty() {
1414                                qualifying_zeitraum_ids.push(zid.clone());
1415                            }
1416                        }
1417                    }
1418                }
1419            }
1420        }
1421        if qualifying_zeitraum_ids.is_empty() {
1422            return ConditionResult::Unknown;
1423        }
1424        ConditionResult::True
1425    }
1426
1427    /// [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...
1428    // 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)
1429    fn evaluate_2009(&self, ctx: &EvaluationContext) -> ConditionResult {
1430        let nav = match ctx.navigator() {
1431            Some(n) => n,
1432            None => return ConditionResult::Unknown,
1433        };
1434        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1435        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1436        for i in 0..sg8_count {
1437            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1438            let is_z01 = seq_segs.iter().any(|s| {
1439                s.elements
1440                    .first()
1441                    .and_then(|e| e.first())
1442                    .is_some_and(|v| v == "Z01")
1443            });
1444            if is_z01 && matches!(self.evaluate(89, ctx), ConditionResult::True) {
1445                for seq in &seq_segs {
1446                    if seq
1447                        .elements
1448                        .first()
1449                        .and_then(|e| e.first())
1450                        .is_some_and(|v| v == "Z01")
1451                    {
1452                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1453                            if !zid.is_empty() {
1454                                qualifying_zeitraum_ids.push(zid.clone());
1455                            }
1456                        }
1457                    }
1458                }
1459            }
1460        }
1461        if qualifying_zeitraum_ids.is_empty() {
1462            return ConditionResult::Unknown;
1463        }
1464        ConditionResult::True
1465    }
1466
1467    /// [1] Wenn Aufteilung vorhanden
1468    fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
1469        ctx.external.evaluate("message_splitting")
1470    }
1471
1472    /// [4] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle LF
1473    fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
1474        ctx.external.evaluate("recipient_is_lf")
1475    }
1476
1477    /// [5] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle LF
1478    fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
1479        ctx.external.evaluate("sender_is_lf")
1480    }
1481
1482    /// [6] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle ÜNB
1483    fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
1484        ctx.external.evaluate("recipient_role_check")
1485    }
1486
1487    /// [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
1488    fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
1489        let sts_segs = ctx.find_segments("STS");
1490        let found = sts_segs.iter().any(|s| {
1491            s.elements
1492                .first()
1493                .and_then(|e| e.first())
1494                .is_some_and(|v| v == "7")
1495                && s.elements
1496                    .get(2)
1497                    .and_then(|e| e.first())
1498                    .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
1499        });
1500        ConditionResult::from(found)
1501    }
1502
1503    /// [8] Wenn für Datenclearing erforderlich
1504    fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
1505        ctx.external.evaluate("data_clearing_required")
1506    }
1507
1508    /// [9] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle MSB
1509    fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
1510        ctx.external.evaluate("sender_role_check")
1511    }
1512
1513    /// [10] Wenn SG4 STS+7++xxx+xxx+E01/E03 (Transaktionsgrund befristete Anmeldung) vorhanden
1514    fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
1515        // STS+7++E01+ZW4+E03: element[4] is c556_3.d9013 = transaktionsgrundErgaenzungBefristeteAnmeldung
1516        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
1517    }
1518
1519    /// [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
1520    fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
1521        let sts_segs = ctx.find_segments("STS");
1522        let found = sts_segs.iter().any(|s| {
1523            s.elements
1524                .first()
1525                .and_then(|e| e.first())
1526                .is_some_and(|v| v == "7")
1527                && s.elements
1528                    .get(2)
1529                    .and_then(|e| e.first())
1530                    .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
1531        });
1532        ConditionResult::from(!found)
1533    }
1534
1535    /// [12] Wenn SG4 DTM+471 (Ende zum nächstmöglichem Termin) nicht vorhanden
1536    fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
1537        let dtm_segs = ctx.find_segments("DTM");
1538        let found = dtm_segs.iter().any(|s| {
1539            s.elements
1540                .first()
1541                .and_then(|e| e.first())
1542                .is_some_and(|v| v == "471")
1543        });
1544        ConditionResult::from(!found)
1545    }
1546
1547    /// [13] Wenn SG4 STS+E01++Z01 (Status der Antwort: Zustimmung mit Terminänderung) nicht vorhanden
1548    fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
1549        let sts_segs = ctx.find_segments("STS");
1550        let found = sts_segs.iter().any(|s| {
1551            s.elements
1552                .first()
1553                .and_then(|e| e.first())
1554                .is_some_and(|v| v == "E01")
1555                && s.elements
1556                    .get(2)
1557                    .and_then(|e| e.first())
1558                    .is_some_and(|v| ["Z01"].contains(&v.as_str()))
1559        });
1560        ConditionResult::from(!found)
1561    }
1562
1563    /// [14] Wenn Datum bekannt
1564    fn evaluate_14(&self, ctx: &EvaluationContext) -> ConditionResult {
1565        ctx.external.evaluate("date_known")
1566    }
1567
1568    /// [15] Wenn in derselben SG8 das SEQ+Z98 (Informative Daten der Marktlokation) vorhanden
1569    fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
1570        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1571        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
1572        let found = seq_segs.iter().any(|s| {
1573            s.elements
1574                .first()
1575                .and_then(|e| e.first())
1576                .is_some_and(|v| ["Z98"].contains(&v.as_str()))
1577        });
1578        ConditionResult::from(found)
1579    }
1580
1581    /// [16] Wenn SG4 STS+E01++Z12 (Status der Antwort: Ablehnung Vertragsbindung) vorhanden
1582    fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
1583        let sts_segs = ctx.find_segments("STS");
1584        let found = sts_segs.iter().any(|s| {
1585            s.elements
1586                .first()
1587                .and_then(|e| e.first())
1588                .is_some_and(|v| v == "E01")
1589                && s.elements
1590                    .get(2)
1591                    .and_then(|e| e.first())
1592                    .is_some_and(|v| ["Z12"].contains(&v.as_str()))
1593        });
1594        ConditionResult::from(found)
1595    }
1596
1597    /// [17] Wenn in derselben SG8 das SEQ+Z01/Z80/Z81 (Daten der Marktlokation) vorhanden
1598    fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
1599        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1600        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
1601        let found = seq_segs.iter().any(|s| {
1602            s.elements
1603                .first()
1604                .and_then(|e| e.first())
1605                .is_some_and(|v| ["Z01", "Z80", "Z81"].contains(&v.as_str()))
1606        });
1607        ConditionResult::from(found)
1608    }
1609
1610    /// [18] Wenn SG4 DTM+93 (Ende zum) nicht vorhanden
1611    fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
1612        let dtm_segs = ctx.find_segments("DTM");
1613        let found = dtm_segs.iter().any(|s| {
1614            s.elements
1615                .first()
1616                .and_then(|e| e.first())
1617                .is_some_and(|v| v == "93")
1618        });
1619        ConditionResult::from(!found)
1620    }
1621
1622    /// [19] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+++ZC0 (Prognose auf Basis von Werten) vorhanden
1623    fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
1624        let seq_segs = ctx.find_segments("SEQ");
1625        let found = seq_segs.iter().any(|s| {
1626            s.elements
1627                .first()
1628                .and_then(|e| e.first())
1629                .is_some_and(|v| ["Z01", "Z98"].contains(&v.as_str()))
1630        });
1631        ConditionResult::from(found)
1632    }
1633
1634    /// [20] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z07 (Verbrauch) vorhanden
1635    fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
1636        let seq_segs = ctx.find_segments("SEQ");
1637        let found = seq_segs.iter().any(|s| {
1638            s.elements
1639                .first()
1640                .and_then(|e| e.first())
1641                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
1642        });
1643        ConditionResult::from(found)
1644    }
1645
1646    /// [21] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
1647    fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
1648        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1649        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
1650        let found = cci_segs.iter().any(|s| {
1651            s.elements
1652                .get(2)
1653                .and_then(|e| e.first())
1654                .is_some_and(|v| v == "ZA6")
1655        });
1656        ConditionResult::from(found)
1657    }
1658
1659    /// [22] Es ist die Zeitraum-ID vom DE1156 aus einem passenden SG6 RFF+Z47/ Z48/ Z49 (Verwendungszeitraum der Daten) einzutragen
1660    // 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)
1661    fn evaluate_22(&self, ctx: &EvaluationContext) -> ConditionResult {
1662        for qual in &["Z47", "Z48", "Z49"] {
1663            if matches!(
1664                ctx.groups_share_qualified_value(
1665                    "RFF",
1666                    0,
1667                    qual,
1668                    0,
1669                    2,
1670                    &["SG4", "SG6"],
1671                    "SEQ",
1672                    1,
1673                    0,
1674                    &["SG4", "SG8"],
1675                ),
1676                ConditionResult::True
1677            ) {
1678                return ConditionResult::True;
1679            }
1680        }
1681        ConditionResult::False
1682    }
1683
1684    /// [23] Wenn in dieser SG4 das STS+E01++A05/A99 (Status der Antwort) vorhanden
1685    fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
1686        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1687        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1688        let found = sts_segs.iter().any(|s| {
1689            s.elements
1690                .first()
1691                .and_then(|e| e.first())
1692                .is_some_and(|v| v == "E01")
1693                && s.elements
1694                    .get(2)
1695                    .and_then(|e| e.first())
1696                    .is_some_and(|v| ["A05", "A99"].contains(&v.as_str()))
1697        });
1698        ConditionResult::from(found)
1699    }
1700
1701    /// [24] Wenn in dieser SG4 das STS+E01++A25/A99 (Status der Antwort) vorhanden
1702    fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
1703        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1704        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1705        let found = sts_segs.iter().any(|s| {
1706            s.elements
1707                .first()
1708                .and_then(|e| e.first())
1709                .is_some_and(|v| v == "E01")
1710                && s.elements
1711                    .get(2)
1712                    .and_then(|e| e.first())
1713                    .is_some_and(|v| ["A25", "A99"].contains(&v.as_str()))
1714        });
1715        ConditionResult::from(found)
1716    }
1717
1718    /// [25] Wenn die Veräußerungsform der erzeugenden Marktlokation der Marktprämie zugeordnet ist
1719    fn evaluate_25(&self, _ctx: &EvaluationContext) -> ConditionResult {
1720        // TODO: implement
1721        ConditionResult::Unknown
1722    }
1723
1724    /// [27] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle ein T oder R enthält
1725    fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
1726        let dtm_segs = ctx.find_segments("DTM");
1727        let found = dtm_segs.iter().any(|s| {
1728            s.elements
1729                .first()
1730                .and_then(|e| e.first())
1731                .is_some_and(|v| v == "Z01")
1732                && s.elements
1733                    .first()
1734                    .and_then(|e| e.get(1))
1735                    .is_some_and(|v| v.chars().nth(3).is_some_and(|c| ['T', 'R'].contains(&c)))
1736        });
1737        ConditionResult::from(found)
1738    }
1739
1740    /// [30] Wenn Antwort auf Aktivierung übermittelt wird
1741    fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
1742        // Antwort auf Aktivierung — depends on PID context
1743        ctx.external.evaluate("activation_response")
1744    }
1745
1746    /// [31] Wenn eine Korrektur erfolgt
1747    fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
1748        ctx.external.evaluate("correction_in_progress")
1749    }
1750
1751    /// [32] Wenn mehr als ein SG8 (Referenz auf die Lokationsbündelstruktur) vorhanden
1752    fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
1753        let seq_segs = ctx.find_segments("SEQ");
1754        ConditionResult::from(seq_segs.len() > 1)
1755    }
1756
1757    /// [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...
1758    fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
1759        ctx.external.evaluate("code_list_membership_check")
1760    }
1761
1762    /// [34] Wenn Antwort auf Deaktivierung übermittelt wird
1763    fn evaluate_34(&self, ctx: &EvaluationContext) -> ConditionResult {
1764        // Antwort auf Deaktivierung — depends on PID context
1765        ctx.external.evaluate("deactivation_response")
1766    }
1767
1768    /// [35] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle T (Termin) enthält
1769    fn evaluate_35(&self, ctx: &EvaluationContext) -> ConditionResult {
1770        let dtm_segs = ctx.find_segments("DTM");
1771        let found = dtm_segs.iter().any(|s| {
1772            s.elements
1773                .first()
1774                .and_then(|e| e.first())
1775                .is_some_and(|v| v == "Z01")
1776                && s.elements
1777                    .first()
1778                    .and_then(|e| e.get(1))
1779                    .is_some_and(|v| v.chars().nth(3).is_some_and(|c| c == 'T'))
1780        });
1781        ConditionResult::from(found)
1782    }
1783
1784    /// [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...
1785    fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
1786        ctx.external.evaluate("code_list_membership_check")
1787    }
1788
1789    /// [37] Wenn Anmeldung/ Änderung befristet
1790    fn evaluate_37(&self, ctx: &EvaluationContext) -> ConditionResult {
1791        // Same check as [10]: STS element[4] is befristete Anmeldung code
1792        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
1793    }
1794
1795    /// [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...
1796    fn evaluate_38(&self, ctx: &EvaluationContext) -> ConditionResult {
1797        ctx.external.evaluate("code_list_membership_check")
1798    }
1799
1800    /// [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...
1801    fn evaluate_39(&self, ctx: &EvaluationContext) -> ConditionResult {
1802        ctx.external.evaluate("code_list_membership_check")
1803    }
1804
1805    /// [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...
1806    fn evaluate_40(&self, ctx: &EvaluationContext) -> ConditionResult {
1807        ctx.external.evaluate("code_list_membership_check")
1808    }
1809
1810    /// [41] Es ist eine Produktpaket-ID aus dem DE1050 von einem SG8 SEQ+Z79 (Erforderliches Produkt) zu nennen
1811    fn evaluate_41(&self, ctx: &EvaluationContext) -> ConditionResult {
1812        // Check: Produktpaket-ID exists in SEQ+Z79 (Erforderliches Produkt)
1813        let seq_z79 = ctx.find_segments_with_qualifier("SEQ", 0, "Z79");
1814        if seq_z79.is_empty() {
1815            return ConditionResult::Unknown;
1816        }
1817        // Check if SEQ has a value in C286.D1050 (elements[1][0])
1818        let found = seq_z79.iter().any(|s| {
1819            s.elements
1820                .get(1)
1821                .and_then(|e| e.first())
1822                .is_some_and(|v| !v.is_empty())
1823        });
1824        ConditionResult::from(found)
1825    }
1826
1827    /// [42] Wenn mehr als ein SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) vorhanden
1828    fn evaluate_42(&self, ctx: &EvaluationContext) -> ConditionResult {
1829        let seq_segs = ctx.find_segments("SEQ");
1830        let zh0_count = seq_segs
1831            .iter()
1832            .filter(|s| {
1833                s.elements
1834                    .first()
1835                    .and_then(|e| e.first())
1836                    .is_some_and(|v| v == "ZH0")
1837            })
1838            .count();
1839        ConditionResult::from(zh0_count > 1)
1840    }
1841
1842    /// [43] Wenn in dieser SG4 das STS+E01++A12 / A13 (Status der Antwort) vorhanden
1843    fn evaluate_43(&self, ctx: &EvaluationContext) -> ConditionResult {
1844        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1845        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1846        let found = sts_segs.iter().any(|s| {
1847            s.elements
1848                .first()
1849                .and_then(|e| e.first())
1850                .is_some_and(|v| v == "E01")
1851                && s.elements
1852                    .get(2)
1853                    .and_then(|e| e.first())
1854                    .is_some_and(|v| ["A12", "A13"].contains(&v.as_str()))
1855        });
1856        ConditionResult::from(found)
1857    }
1858
1859    /// [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...
1860    fn evaluate_44(&self, _ctx: &EvaluationContext) -> ConditionResult {
1861        // Hinweis: Zeitraum-ID aus passendem SG6 RFF+Z49/Z53 der Anfragenachricht eintragen — informational data population rule
1862        ConditionResult::True
1863    }
1864
1865    /// [45] Wenn in derselben SG8 das SG10 CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
1866    fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
1867        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1868        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
1869        let found = cci_segs.iter().any(|s| {
1870            s.elements
1871                .first()
1872                .and_then(|e| e.first())
1873                .is_some_and(|v| v == "Z01")
1874                && s.elements
1875                    .get(2)
1876                    .and_then(|e| e.first())
1877                    .is_some_and(|v| v == "Z82")
1878        });
1879        ConditionResult::from(found)
1880    }
1881
1882    /// [46] Wenn ID für Objekt vergeben wurde
1883    fn evaluate_46(&self, _ctx: &EvaluationContext) -> ConditionResult {
1884        // TODO: implement
1885        ConditionResult::Unknown
1886    }
1887
1888    /// [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
1889    // 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)
1890    fn evaluate_47(&self, ctx: &EvaluationContext) -> ConditionResult {
1891        const CODES: &[&str] = &[
1892            "DBA", "VZR", "EGS", "LGS", "SLS", "SES", "TLS", "TES", "BIL", "BIP", "BIT", "GAL",
1893            "GAP", "GAT", "GEL", "GEP", "GET", "SOL", "SOP", "SOT", "WFL", "WFP", "WFT", "WNL",
1894            "WNP", "WNT",
1895        ];
1896        let nav = match ctx.navigator {
1897            Some(n) => n,
1898            None => return ConditionResult::Unknown,
1899        };
1900        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1901        for i in 0..sg8_count {
1902            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
1903            for j in 0..sg10_count {
1904                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
1905                let has_cci_15_z21 = ccis.iter().any(|s| {
1906                    s.elements
1907                        .first()
1908                        .and_then(|e: &Vec<String>| e.first())
1909                        .is_some_and(|v: &String| v == "15")
1910                        && s.elements
1911                            .get(2)
1912                            .and_then(|e: &Vec<String>| e.first())
1913                            .is_some_and(|v: &String| v == "Z21")
1914                });
1915                if has_cci_15_z21 {
1916                    let cavs =
1917                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
1918                    if cavs.iter().any(|s| {
1919                        s.elements
1920                            .first()
1921                            .and_then(|e: &Vec<String>| e.first())
1922                            .is_some_and(|v: &String| CODES.contains(&v.as_str()))
1923                    }) {
1924                        return ConditionResult::True;
1925                    }
1926                }
1927            }
1928        }
1929        ConditionResult::False
1930    }
1931
1932    /// [48] Wenn in dieser SG4 das STS+E01++A99 (Status der Antwort: Sonstiges) vorhanden
1933    fn evaluate_48(&self, ctx: &EvaluationContext) -> ConditionResult {
1934        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1935        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1936        let found = sts_segs.iter().any(|s| {
1937            s.elements
1938                .first()
1939                .and_then(|e| e.first())
1940                .is_some_and(|v| v == "E01")
1941                && s.elements
1942                    .get(2)
1943                    .and_then(|e| e.first())
1944                    .is_some_and(|v| ["A99"].contains(&v.as_str()))
1945        });
1946        ConditionResult::from(found)
1947    }
1948
1949    /// [49] Wenn in dieser SG4 das STS+E01++E14 (Status der Antwort: Ablehnung Sonstiges) vorhanden
1950    fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
1951        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1952        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1953        let found = sts_segs.iter().any(|s| {
1954            s.elements
1955                .first()
1956                .and_then(|e| e.first())
1957                .is_some_and(|v| v == "E01")
1958                && s.elements
1959                    .get(2)
1960                    .and_then(|e| e.first())
1961                    .is_some_and(|v| ["E14"].contains(&v.as_str()))
1962        });
1963        ConditionResult::from(found)
1964    }
1965
1966    /// [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...
1967    // 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)
1968    fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
1969        use std::collections::HashMap;
1970
1971        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
1972        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
1973        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
1974        let rff_ids = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
1975
1976        // Build per-instance RFF lookup: instance_idx -> list of (qualifier, id)
1977        let mut rff_by_instance: HashMap<usize, Vec<(String, String)>> = HashMap::new();
1978        for ((idx, q), (_, id)) in rff_quals.iter().zip(rff_ids.iter()) {
1979            rff_by_instance
1980                .entry(*idx)
1981                .or_default()
1982                .push((q.clone(), id.clone()));
1983        }
1984
1985        // Build (zeitraum_id, marktlok_id) for Z98 and ZE7 SG8 instances
1986        let mut seq_zeitraum_map: HashMap<usize, String> = HashMap::new();
1987        for (idx, z) in &seq_zeitraum {
1988            seq_zeitraum_map.insert(*idx, z.clone());
1989        }
1990
1991        let z98_data: Vec<(String, String)> = seq_quals
1992            .iter()
1993            .filter(|(_, q)| q == "Z98")
1994            .map(|(idx, _)| {
1995                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
1996                let marktlok = rff_by_instance
1997                    .get(idx)
1998                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
1999                    .map(|(_, id)| id.clone())
2000                    .unwrap_or_default();
2001                (zeitraum, marktlok)
2002            })
2003            .collect();
2004
2005        if z98_data.is_empty() {
2006            return ConditionResult::Unknown;
2007        }
2008
2009        let ze7_data: Vec<(String, String)> = seq_quals
2010            .iter()
2011            .filter(|(_, q)| q == "ZE7")
2012            .map(|(idx, _)| {
2013                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
2014                let marktlok = rff_by_instance
2015                    .get(idx)
2016                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
2017                    .map(|(_, id)| id.clone())
2018                    .unwrap_or_default();
2019                (zeitraum, marktlok)
2020            })
2021            .collect();
2022
2023        ConditionResult::from(z98_data.iter().any(|(z98_zeit, z98_lok)| {
2024            !ze7_data
2025                .iter()
2026                .any(|(ze7_zeit, ze7_lok)| ze7_zeit == z98_zeit && ze7_lok == z98_lok)
2027        }))
2028    }
2029
2030    /// [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...
2031    // 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)
2032    fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
2033        // HAND-EDITED: "derselben SG8 SEQ+Z98 mit derselben Zeitraum-ID … nicht auf ein SG8 SEQ+Z78" — compared within the enclosing SG4 when the scope
2034        // reaches an SG8; the message as before otherwise.
2035        if let Some(scope) = ctx.enclosing_scope("SG8") {
2036            let Some((_, zeitraum)) =
2037                sg4_instances::seq_at(ctx, scope).filter(|(code, _)| code == "Z98")
2038            else {
2039                return ConditionResult::False;
2040            };
2041            let pauschal = sg4_instances::other_sg8s(ctx, scope).iter().any(|p| {
2042                sg4_instances::seq_at(ctx, p)
2043                    .is_some_and(|(c, z)| c == "Z78" && !z.is_empty() && z == zeitraum)
2044                    && sg4_instances::rff_values_at(ctx, p, "Z31")
2045                        .iter()
2046                        .any(|v| v.replace(' ', "") == "9992000000018")
2047            });
2048            return ConditionResult::from(!pauschal);
2049        }
2050        let nav = match ctx.navigator() {
2051            Some(n) => n,
2052            None => return ConditionResult::Unknown,
2053        };
2054        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2055        let pauschal_id = "9992000000018";
2056        // Collect Zeitraum-IDs from Z78 SG8s that carry the pauschal RFF+Z31 ID
2057        let mut pauschal_zeitraum_ids: Vec<String> = Vec::new();
2058        for i in 0..sg8_count {
2059            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2060            let has_z78 = seqs.iter().any(|s| {
2061                s.elements
2062                    .first()
2063                    .and_then(|e| e.first())
2064                    .is_some_and(|v| v == "Z78")
2065            });
2066            if !has_z78 {
2067                continue;
2068            }
2069            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
2070            let has_pauschal = rffs.iter().any(|s| {
2071                s.elements
2072                    .first()
2073                    .and_then(|e| e.first())
2074                    .is_some_and(|v| v == "Z31")
2075                    && s.elements
2076                        .first()
2077                        .and_then(|e| e.get(1))
2078                        .is_some_and(|v| v.replace(' ', "") == pauschal_id)
2079            });
2080            if has_pauschal {
2081                for seq in &seqs {
2082                    if seq
2083                        .elements
2084                        .first()
2085                        .and_then(|e| e.first())
2086                        .is_some_and(|v| v == "Z78")
2087                    {
2088                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
2089                            if !zid.is_empty() {
2090                                pauschal_zeitraum_ids.push(zid.clone());
2091                            }
2092                        }
2093                    }
2094                }
2095            }
2096        }
2097        // For each Z98 SG8, check whether its Zeitraum-ID (SEQ.C286.DE1050) maps to a pauschal Z78
2098        for i in 0..sg8_count {
2099            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2100            for seq in &seqs {
2101                if seq
2102                    .elements
2103                    .first()
2104                    .and_then(|e| e.first())
2105                    .is_some_and(|v| v == "Z98")
2106                {
2107                    let zid = seq
2108                        .elements
2109                        .get(1)
2110                        .and_then(|e| e.first())
2111                        .map(|s| s.as_str())
2112                        .unwrap_or("");
2113                    if !zid.is_empty() && pauschal_zeitraum_ids.iter().any(|z| z == zid) {
2114                        // Z98 SG8 references a pauschal Z78 → condition is False
2115                        return ConditionResult::False;
2116                    }
2117                }
2118            }
2119        }
2120        // No Z98 SG8 references the pauschal bundle → condition is True
2121        ConditionResult::True
2122    }
2123
2124    /// [52] Wenn SG10 CCI+Z07++Z55/ Z56 (Profiltyp: sonstige verbrauchende Marktlokation / sonstige erzeugende Marktlokation) vorhanden
2125    fn evaluate_52(&self, ctx: &EvaluationContext) -> ConditionResult {
2126        let cci_segs = ctx.find_segments("CCI");
2127        let found = cci_segs.iter().any(|s| {
2128            s.elements
2129                .first()
2130                .and_then(|e| e.first())
2131                .is_some_and(|v| v == "Z07")
2132                && s.elements
2133                    .get(2)
2134                    .and_then(|e| e.first())
2135                    .is_some_and(|v| ["Z55", "Z56"].contains(&v.as_str()))
2136        });
2137        ConditionResult::from(found)
2138    }
2139
2140    /// [53] Wenn weitere Präzisierungen als über CCI+Z07 (Profiltyp) möglich sind
2141    fn evaluate_53(&self, _ctx: &EvaluationContext) -> ConditionResult {
2142        // TODO: implement
2143        ConditionResult::Unknown
2144    }
2145
2146    /// [54] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) vorhanden
2147    fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
2148        let seq_segs = ctx.find_segments("SEQ");
2149        let found = seq_segs.iter().any(|s| {
2150            s.elements
2151                .first()
2152                .and_then(|e| e.first())
2153                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
2154        });
2155        ConditionResult::from(found)
2156    }
2157
2158    /// [55] Wenn in dieser SG4 das STS+E01++A06/ A99 (Status der Antwort) vorhanden
2159    fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
2160        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2161        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2162        let found = sts_segs.iter().any(|s| {
2163            s.elements
2164                .first()
2165                .and_then(|e| e.first())
2166                .is_some_and(|v| v == "E01")
2167                && s.elements
2168                    .get(2)
2169                    .and_then(|e| e.first())
2170                    .is_some_and(|v| ["A06", "A99"].contains(&v.as_str()))
2171        });
2172        ConditionResult::from(found)
2173    }
2174
2175    /// [56] Wenn im STS+E01 im DE9013 (Status der Antwort) ein Antwortcode aus dem Cluster Zustimmung vorhanden ist
2176    fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
2177        // Check: STS+E01 DE9013 has a Zustimmung (approval) cluster code
2178        // Zustimmung codes start with A0x (A01-A07 are approval codes)
2179        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
2180        let found = sts_segs.iter().any(|s| {
2181            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
2182                [
2183                    "A01", "A02", "A03", "A04", "A05", "A06", "A07", "A08", "A09", "A10", "A11",
2184                    "A12", "A13", "A14", "A15", "A16", "A17", "A25",
2185                ]
2186                .contains(&v.as_str())
2187            })
2188        });
2189        ConditionResult::from(found)
2190    }
2191
2192    /// [57] Wenn in derselben SG8 das CCI+Z45++ZD9 (Abrechnung findet statt) vorhanden ist
2193    fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
2194        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2195        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
2196        let found = cci_segs.iter().any(|s| {
2197            s.elements
2198                .first()
2199                .and_then(|e| e.first())
2200                .is_some_and(|v| v == "Z45")
2201                && s.elements
2202                    .get(2)
2203                    .and_then(|e| e.first())
2204                    .is_some_and(|v| v == "ZD9")
2205        });
2206        ConditionResult::from(found)
2207    }
2208
2209    /// [58] Wenn in diesem CCI das DE3055 mit dem Code 293 vorhanden
2210    fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
2211        // HAND-EDITED: "in diesem CCI" — the CCI the field is in (its own segment
2212        // in tree validation), else the enclosing SG10's CCI.
2213        let has_293 =
2214            |elements: &[Vec<String>]| elements.iter().any(|e| e.iter().any(|v| v == "293"));
2215        if let Some(own) = ctx.resolved_segment.filter(|_| {
2216            ctx.scoped_find_segments_in("SG10", "CCI")
2217                .iter()
2218                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
2219        }) {
2220            return ConditionResult::from(has_293(own));
2221        }
2222        ConditionResult::from(
2223            ctx.scoped_find_segments_in("SG10", "CCI")
2224                .iter()
2225                .any(|s| has_293(&s.elements)),
2226        )
2227    }
2228
2229    /// [60] Wenn BGM+Z90 (Beendigung der Zuordnung zur Lokation) vorhanden
2230    fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
2231        let bgm_segs = ctx.find_segments("BGM");
2232        let found = bgm_segs.iter().any(|s| {
2233            s.elements
2234                .first()
2235                .and_then(|e| e.first())
2236                .is_some_and(|v| v == "Z90")
2237        });
2238        ConditionResult::from(found)
2239    }
2240
2241    /// [61] Wenn BGM+Z89 (Zuordnung zur Lokation) vorhanden
2242    fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
2243        let bgm_segs = ctx.find_segments("BGM");
2244        let found = bgm_segs.iter().any(|s| {
2245            s.elements
2246                .first()
2247                .and_then(|e| e.first())
2248                .is_some_and(|v| v == "Z89")
2249        });
2250        ConditionResult::from(found)
2251    }
2252
2253    /// [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), ...
2254    // 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)
2255    fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
2256        let nav = match ctx.navigator {
2257            Some(n) => n,
2258            None => return ConditionResult::Unknown,
2259        };
2260        use std::collections::HashMap;
2261        let mut count_by_zid: HashMap<String, u32> = HashMap::new();
2262        let sg4_count = nav.group_instance_count(&["SG4"]);
2263        for sg4_idx in 0..sg4_count {
2264            let sg8_count = nav.child_group_instance_count(&["SG4"], sg4_idx, "SG8");
2265            for sg8_idx in 0..sg8_count {
2266                let seqs =
2267                    nav.find_segments_in_child_group("SEQ", &["SG4"], sg4_idx, "SG8", sg8_idx);
2268                let zeitraum_opt = seqs
2269                    .iter()
2270                    .filter(|s| {
2271                        s.elements
2272                            .first()
2273                            .and_then(|e: &Vec<String>| e.first())
2274                            .is_some_and(|v: &String| v == "Z45")
2275                    })
2276                    .filter_map(|s| {
2277                        s.elements
2278                            .get(1)
2279                            .and_then(|e: &Vec<String>| e.first())
2280                            .filter(|v| !v.is_empty())
2281                            .cloned()
2282                    })
2283                    .next();
2284                let Some(zid) = zeitraum_opt else { continue };
2285                let pias =
2286                    nav.find_segments_in_child_group("PIA", &["SG4"], sg4_idx, "SG8", sg8_idx);
2287                let has_qualifying_pia = pias.iter().any(|s| {
2288                    s.elements
2289                        .first()
2290                        .and_then(|e: &Vec<String>| e.first())
2291                        .is_some_and(|v: &String| v == "Z02")
2292                        && s.elements
2293                            .get(1)
2294                            .and_then(|e: &Vec<String>| e.first())
2295                            .map(|code| {
2296                                code.starts_with("1-08-1")
2297                                    || code.starts_with("1-08-2")
2298                                    || code.starts_with("1-08-3")
2299                                    || code.starts_with("1-08-4")
2300                                    || code.starts_with("1-08-5")
2301                            })
2302                            .unwrap_or(false)
2303                });
2304                if has_qualifying_pia {
2305                    *count_by_zid.entry(zid).or_insert(0) += 1;
2306                }
2307            }
2308        }
2309        ConditionResult::from(count_by_zid.values().any(|&c| c > 1))
2310    }
2311
2312    /// [63] Wenn in dieser SG4 das STS+E01++A15/ A99 (Status der Antwort) vorhanden
2313    fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
2314        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2315        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2316        let found = sts_segs.iter().any(|s| {
2317            s.elements
2318                .first()
2319                .and_then(|e| e.first())
2320                .is_some_and(|v| v == "E01")
2321                && s.elements
2322                    .get(2)
2323                    .and_then(|e| e.first())
2324                    .is_some_and(|v| ["A15", "A99"].contains(&v.as_str()))
2325        });
2326        ConditionResult::from(found)
2327    }
2328
2329    /// [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...
2330    fn evaluate_64(&self, ctx: &EvaluationContext) -> ConditionResult {
2331        // Check: more than one SG8 SEQ+ZE1 has PIA+Z02 with code starting with 1-08-1/2/3/4/5
2332        let pia_segs = ctx.find_segments("PIA");
2333        let count = pia_segs
2334            .iter()
2335            .filter(|s| {
2336                s.elements
2337                    .first()
2338                    .and_then(|e| e.first())
2339                    .is_some_and(|v| v == "Z02")
2340                    && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
2341                        ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"]
2342                            .iter()
2343                            .any(|p| v.starts_with(p))
2344                    })
2345            })
2346            .count();
2347        ConditionResult::from(count > 1)
2348    }
2349
2350    /// [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...
2351    fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
2352        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2353        // Check: PIA+Z02 with code starting with 1-08-1/2/3/4/5 exists in same SG8 SEQ+ZE1
2354        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
2355        let found = pia_segs.iter().any(|s| {
2356            s.elements
2357                .first()
2358                .and_then(|e| e.first())
2359                .is_some_and(|v| v == "Z02")
2360                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
2361                    ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"]
2362                        .iter()
2363                        .any(|p| v.starts_with(p))
2364                })
2365        });
2366        ConditionResult::from(found)
2367    }
2368
2369    /// [66] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als einmal vorhanden
2370    fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
2371        let seq_segs = ctx.find_segments("SEQ");
2372        let count = seq_segs
2373            .iter()
2374            .filter(|s| {
2375                s.elements
2376                    .first()
2377                    .and_then(|e| e.first())
2378                    .is_some_and(|v| v == "ZH0")
2379            })
2380            .count();
2381        ConditionResult::from(count > 1)
2382    }
2383
2384    /// [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.
2385    fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
2386        // Check: no SG8 SEQ+Z79 has CAV+ZH9 with code 9991000002933
2387        let cav_segs = ctx.find_segments("CAV");
2388        let found = cav_segs.iter().any(|s| {
2389            s.elements
2390                .first()
2391                .and_then(|e| e.first())
2392                .is_some_and(|v| v == "ZH9")
2393                && s.elements
2394                    .first()
2395                    .and_then(|e| e.get(1))
2396                    .is_some_and(|v| v == "9991000002933")
2397        });
2398        ConditionResult::from(!found)
2399    }
2400
2401    /// [68] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als zweimal vorhanden
2402    fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
2403        let seq_segs = ctx.find_segments("SEQ");
2404        let count = seq_segs
2405            .iter()
2406            .filter(|s| {
2407                s.elements
2408                    .first()
2409                    .and_then(|e| e.first())
2410                    .is_some_and(|v| v == "ZH0")
2411            })
2412            .count();
2413        ConditionResult::from(count > 2)
2414    }
2415
2416    /// [69] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als dreimal vorhanden
2417    fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
2418        let seq_segs = ctx.find_segments("SEQ");
2419        let count = seq_segs
2420            .iter()
2421            .filter(|s| {
2422                s.elements
2423                    .first()
2424                    .and_then(|e| e.first())
2425                    .is_some_and(|v| v == "ZH0")
2426            })
2427            .count();
2428        ConditionResult::from(count > 3)
2429    }
2430
2431    /// [70] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) fünfmal vorhanden
2432    fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
2433        let seq_segs = ctx.find_segments("SEQ");
2434        let count = seq_segs
2435            .iter()
2436            .filter(|s| {
2437                s.elements
2438                    .first()
2439                    .and_then(|e| e.first())
2440                    .is_some_and(|v| v == "ZH0")
2441            })
2442            .count();
2443        ConditionResult::from(count == 5)
2444    }
2445
2446    /// [71] Wenn Antwort auf Zuordnung übermittelt wird
2447    fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
2448        // Antwort auf Zuordnung — depends on PID context
2449        ctx.external.evaluate("assignment_response")
2450    }
2451
2452    /// [72] Wenn Antwort auf Beendigung der Zuordnung übermittelt wird
2453    fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
2454        // Antwort auf Beendigung der Zuordnung — depends on PID context
2455        ctx.external.evaluate("assignment_end_response")
2456    }
2457
2458    /// [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
2459    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
2460    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
2461    // Regenerating this function should preserve the shape, but re-verify
2462    // against the committed body if generate-conditions is run.
2463    fn evaluate_73(&self, ctx: &EvaluationContext) -> ConditionResult {
2464        // [73] Wenn in derselben SG8 im PIA DE7140 ein Code aus der
2465        // Codeliste der Gruppenartikel- und Artikel-ID vorhanden ist,
2466        // der in der Spalte UTILMD/Preisangabe mit X gekennzeichnet ist.
2467        //
2468        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
2469        // narrows to the enclosing SG8 instance; otherwise falls back to
2470        // message-wide find_segments. Matches the AHB wording
2471        // "in derselben SG8 im PIA DE7140 ein Code...".
2472        use crate::eval::CodelistColumn;
2473        let any_match = ctx.scoped_find_segments_in("SG8", "PIA").iter().any(|s| {
2474            let is_z02 = s
2475                .elements
2476                .first()
2477                .and_then(|e| e.first())
2478                .is_some_and(|v| v == "Z02");
2479            if !is_z02 {
2480                return false;
2481            }
2482            let code = match s.elements.get(1).and_then(|e| e.first()) {
2483                Some(c) if !c.is_empty() => c,
2484                _ => return false,
2485            };
2486            ctx.codelist
2487                .flag(code, CodelistColumn::UtilmdPreisangabe)
2488                .unwrap_or(false)
2489        });
2490        ConditionResult::from(any_match)
2491    }
2492
2493    /// [74] Wenn in der selben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) vorhanden
2494    fn evaluate_74(&self, ctx: &EvaluationContext) -> ConditionResult {
2495        let seq_segs = ctx.find_segments("SEQ");
2496        let has_seq = seq_segs.iter().any(|s| {
2497            s.elements
2498                .first()
2499                .and_then(|e| e.first())
2500                .is_some_and(|v| v == "Z59")
2501        });
2502        let pia_segs = ctx.find_segments("PIA");
2503        let has_pia = pia_segs.iter().any(|s| {
2504            s.elements
2505                .first()
2506                .and_then(|e| e.first())
2507                .is_some_and(|v| v == "5")
2508        });
2509        ConditionResult::from(has_seq && has_pia)
2510    }
2511
2512    /// [75] Wenn in diesem PIA+5 in DE7140 der Code 9991000000721 (vgl.: Kapitel 4.2. Konfigurationsprodukte Leistungskurvendefinition der Codeliste der Konfigurationen) vorhanden
2513    fn evaluate_75(&self, ctx: &EvaluationContext) -> ConditionResult {
2514        ctx.external.evaluate("code_list_membership_check")
2515    }
2516
2517    /// [76] Wenn Summenzeitreihe auf Ebene des Bilanzierungsgebiet
2518    // 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)
2519    fn evaluate_76(&self, ctx: &EvaluationContext) -> ConditionResult {
2520        ctx.has_qualifier("SEQ", 0, "Z49")
2521    }
2522
2523    /// [77] Wenn SG8 SEQ+Z03 (Zähleinrichtungsdaten) CAV+Z30 (Identifikation/Nummer des Gerätes) nicht vorhanden
2524    fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
2525        let seq_segs = ctx.find_segments("SEQ");
2526        let found = seq_segs.iter().any(|s| {
2527            s.elements
2528                .first()
2529                .and_then(|e| e.first())
2530                .is_some_and(|v| ["Z03"].contains(&v.as_str()))
2531        });
2532        ConditionResult::from(!found)
2533    }
2534
2535    /// [78] Wenn SG4 STS+7++E02 (Transaktionsgrund: Einzug in Neuanlage) nicht vorhanden
2536    fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
2537        let sts_segs = ctx.find_segments("STS");
2538        let found = sts_segs.iter().any(|s| {
2539            s.elements
2540                .first()
2541                .and_then(|e| e.first())
2542                .is_some_and(|v| v == "7")
2543                && s.elements
2544                    .get(2)
2545                    .and_then(|e| e.first())
2546                    .is_some_and(|v| ["E02"].contains(&v.as_str()))
2547        });
2548        ConditionResult::from(!found)
2549    }
2550
2551    /// [83] Wenn in dieser SG4 das STS+E01++A08/ A16/ A99 (Status der Antwort) vorhanden
2552    fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
2553        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2554        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2555        let found = sts_segs.iter().any(|s| {
2556            s.elements
2557                .first()
2558                .and_then(|e| e.first())
2559                .is_some_and(|v| v == "E01")
2560                && s.elements
2561                    .get(2)
2562                    .and_then(|e| e.first())
2563                    .is_some_and(|v| ["A08", "A16", "A99"].contains(&v.as_str()))
2564        });
2565        ConditionResult::from(found)
2566    }
2567
2568    /// [84] Wenn SG4 STS+E01++A57 (Status der Antwort) vorhanden
2569    fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
2570        let sts_segs = ctx.find_segments("STS");
2571        let found = sts_segs.iter().any(|s| {
2572            s.elements
2573                .first()
2574                .and_then(|e| e.first())
2575                .is_some_and(|v| v == "E01")
2576                && s.elements
2577                    .get(2)
2578                    .and_then(|e| e.first())
2579                    .is_some_and(|v| ["A57"].contains(&v.as_str()))
2580        });
2581        ConditionResult::from(found)
2582    }
2583
2584    /// [85] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle M, Q, H oder J enthält
2585    fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
2586        let dtm_segs = ctx.find_segments("DTM");
2587        let found = dtm_segs.iter().any(|s| {
2588            s.elements
2589                .first()
2590                .and_then(|e| e.first())
2591                .is_some_and(|v| v == "Z01")
2592                && s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| {
2593                    v.chars()
2594                        .nth(3)
2595                        .is_some_and(|c| ['M', 'Q', 'H', 'J'].contains(&c))
2596                })
2597        });
2598        ConditionResult::from(found)
2599    }
2600
2601    /// [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...
2602    fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
2603        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" for this SG6's RFF+Z50 — compared within the enclosing SG4 when the scope
2604        // reaches an SG6; the message as before otherwise.
2605        if let Some(scope) = ctx.enclosing_scope("SG6") {
2606            let refs = sg4_instances::rff_values_at(ctx, scope, "Z50");
2607            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
2608                sg4_instances::has_cci(s, "", "ZA6")
2609            }));
2610        }
2611        // Check: RFF+Z50 references same LOC as RFF+Z18 in another SG8
2612        ctx.groups_share_qualified_value(
2613            "RFF",
2614            0,
2615            "Z50",
2616            0,
2617            2,
2618            &["SG4", "SG6"],
2619            "RFF",
2620            0,
2621            2,
2622            &["SG4", "SG8"],
2623        )
2624    }
2625
2626    /// [87] Es ist ein Monatserster 0 Uhr (gem. deutscher Zeit) anzugeben
2627    fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
2628        // Check: date value is 1st of month at 00:00 (format 303: CCYYMMDDHHmm)
2629        let dtm_segs = ctx.find_segments("DTM");
2630        let found = dtm_segs.iter().any(|s| {
2631            s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| {
2632                v.len() >= 12
2633                    && &v[6..8] == "01"  // day = 01
2634                    && &v[8..12] == "0000" // time = 00:00
2635            })
2636        });
2637        if !found {
2638            // Check if any DTM has a non-matching date (definite False) vs no DTM at all (Unknown)
2639            if dtm_segs.is_empty() {
2640                return ConditionResult::Unknown;
2641            }
2642        }
2643        ConditionResult::from(found)
2644    }
2645
2646    /// [88] Wert muss identisch mit DE2380 aus dem SG4 DTM+157 (Änderung zum) sein
2647    fn evaluate_88(&self, ctx: &EvaluationContext) -> ConditionResult {
2648        // Check: value matches DTM+157 (Änderung zum)
2649        let dtm157 = ctx.find_segments_with_qualifier("DTM", 0, "157");
2650        if dtm157.is_empty() {
2651            return ConditionResult::Unknown;
2652        }
2653        // Condition is True when DTM+157 exists (the value check is structural)
2654        ConditionResult::True
2655    }
2656
2657    /// [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+...
2658    // 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)
2659    fn evaluate_89(&self, ctx: &EvaluationContext) -> ConditionResult {
2660        let nav = match ctx.navigator {
2661            Some(n) => n,
2662            None => {
2663                return ctx.filtered_parent_child_has_qualifier(
2664                    &["SG4", "SG8"],
2665                    "SEQ",
2666                    0,
2667                    "Z01",
2668                    "SG10",
2669                    "CCI",
2670                    2,
2671                    "ZA6",
2672                )
2673            }
2674        };
2675        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2676        for i in 0..sg8_count {
2677            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2678            for j in 0..sg10_count {
2679                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2680                let has_cci_za6 = ccis.iter().any(|s| {
2681                    s.elements
2682                        .get(2)
2683                        .and_then(|e: &Vec<String>| e.first())
2684                        .is_some_and(|v: &String| v == "ZA6")
2685                });
2686                if !has_cci_za6 {
2687                    continue;
2688                }
2689                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2690                if cavs.iter().any(|s| {
2691                    s.elements
2692                        .first()
2693                        .and_then(|e: &Vec<String>| e.first())
2694                        .is_some_and(|v: &String| v == "E14")
2695                }) {
2696                    return ConditionResult::True;
2697                }
2698            }
2699        }
2700        ConditionResult::False
2701    }
2702
2703    /// [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
2704    // 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)
2705    fn evaluate_90(&self, ctx: &EvaluationContext) -> ConditionResult {
2706        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
2707        // scope reaches one; the message as before otherwise.
2708        if let Some(_scope) = ctx.enclosing_scope("SG8") {
2709            return ConditionResult::from(
2710                ctx.enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
2711                    .unwrap_or_default()
2712                    .iter()
2713                    .any(|segs| {
2714                        segs.iter()
2715                            .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
2716                            && segs
2717                                .iter()
2718                                .any(|s| s.id == "CAV" && s.get_element(0) == "E14")
2719                    })
2720                    && ctx
2721                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
2722                        .unwrap_or_default()
2723                        .iter()
2724                        .any(|segs| {
2725                            segs.iter()
2726                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
2727                                && segs
2728                                    .iter()
2729                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E02")
2730                        }),
2731            );
2732        }
2733        let nav = match ctx.navigator {
2734            Some(n) => n,
2735            None => return ConditionResult::Unknown,
2736        };
2737        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2738        for i in 0..sg8_count {
2739            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2740            let mut has_za6_e14 = false;
2741            let mut has_za6_e02 = false;
2742            for j in 0..sg10_count {
2743                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2744                let has_cci_za6 = ccis.iter().any(|s| {
2745                    s.elements
2746                        .get(2)
2747                        .and_then(|e: &Vec<String>| e.first())
2748                        .is_some_and(|v: &String| v == "ZA6")
2749                });
2750                if !has_cci_za6 {
2751                    continue;
2752                }
2753                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2754                for cav in &cavs {
2755                    if let Some(code) = cav.elements.first().and_then(|e: &Vec<String>| e.first()) {
2756                        if code == "E14" {
2757                            has_za6_e14 = true;
2758                        }
2759                        if code == "E02" {
2760                            has_za6_e02 = true;
2761                        }
2762                    }
2763                }
2764            }
2765            if has_za6_e14 && has_za6_e02 {
2766                return ConditionResult::True;
2767            }
2768        }
2769        ConditionResult::False
2770    }
2771
2772    /// [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
2773    // 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)
2774    fn evaluate_91(&self, ctx: &EvaluationContext) -> ConditionResult {
2775        match self.evaluate_90(ctx) {
2776            ConditionResult::True => ConditionResult::False,
2777            ConditionResult::False => ConditionResult::True,
2778            ConditionResult::Unknown => ConditionResult::Unknown,
2779        }
2780    }
2781
2782    /// [92] Wenn Wert innerhalb SG bzw. Segment geändert wird
2783    fn evaluate_92(&self, _ctx: &EvaluationContext) -> ConditionResult {
2784        // TODO: implement
2785        ConditionResult::Unknown
2786    }
2787
2788    /// [93] Erlaubte Codes aus der PRICAT BGM+Z32 (Preisblatt Messstellenbetrieb) des verantwortlichen MSB
2789    fn evaluate_93(&self, _ctx: &EvaluationContext) -> ConditionResult {
2790        // TODO: implement
2791        ConditionResult::Unknown
2792    }
2793
2794    /// [94] Wenn ein Segment innerhalb der SG vorhanden
2795    fn evaluate_94(&self, _ctx: &EvaluationContext) -> ConditionResult {
2796        // Structural: segment exists within SG (always true for valid messages)
2797        ConditionResult::True
2798    }
2799
2800    /// [95] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) CAV+ZE5 (E-Mobilität) vorhanden
2801    fn evaluate_95(&self, ctx: &EvaluationContext) -> ConditionResult {
2802        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
2803        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
2804        let has_cci = cci_segs.iter().any(|s| {
2805            s.elements
2806                .first()
2807                .and_then(|e| e.first())
2808                .is_some_and(|v| v == "Z17")
2809        });
2810        let cav_segs = ctx.scoped_find_segments_in("SG10", "CAV");
2811        let has_cav = cav_segs.iter().any(|s| {
2812            s.elements
2813                .first()
2814                .and_then(|e| e.first())
2815                .is_some_and(|v| ["ZE5"].contains(&v.as_str()))
2816        });
2817        ConditionResult::from(has_cci && has_cav)
2818    }
2819
2820    /// [96] Wenn SG4 STS+7++xxx+ZAP (Transaktionsgrundergänzung ruhende Marktlokation) vorhanden
2821    fn evaluate_96(&self, ctx: &EvaluationContext) -> ConditionResult {
2822        let sts_segs = ctx.find_segments("STS");
2823        let found = sts_segs.iter().any(|s| {
2824            s.elements
2825                .first()
2826                .and_then(|e| e.first())
2827                .is_some_and(|v| v == "7")
2828                && s.elements
2829                    .get(3)
2830                    .and_then(|e| e.first())
2831                    .is_some_and(|v| ["ZAP"].contains(&v.as_str()))
2832        });
2833        ConditionResult::from(found)
2834    }
2835
2836    /// [97] Wenn in einem SG10 CAV+ZH9 DE7110 der Code der Produkteigenschaft (Wertebereich) 9991000002420 (Marktprämie) vorhanden ist
2837    fn evaluate_97(&self, ctx: &EvaluationContext) -> ConditionResult {
2838        // Check: CAV+ZH9 has code 9991000002420 (Marktprämie)
2839        let cav_segs = ctx.find_segments("CAV");
2840        let found = cav_segs.iter().any(|s| {
2841            s.elements
2842                .first()
2843                .and_then(|e| e.first())
2844                .is_some_and(|v| v == "ZH9")
2845                && s.elements
2846                    .first()
2847                    .and_then(|e| e.get(1))
2848                    .is_some_and(|v| v == "9991000002420")
2849        });
2850        ConditionResult::from(found)
2851    }
2852
2853    /// [98] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle NB
2854    fn evaluate_98(&self, ctx: &EvaluationContext) -> ConditionResult {
2855        ctx.external.evaluate("sender_role_check")
2856    }
2857
2858    /// [99] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle ÜNB
2859    fn evaluate_99(&self, ctx: &EvaluationContext) -> ConditionResult {
2860        ctx.external.evaluate("sender_role_check")
2861    }
2862
2863    /// [100] Wenn SG10 CAV+TLS/ TES/ BIT/ GET/ GAT/ SOT/ WNT/ WFT/ WAT vorhanden
2864    fn evaluate_100(&self, ctx: &EvaluationContext) -> ConditionResult {
2865        let cav_segs = ctx.find_segments("CAV");
2866        let found = cav_segs.iter().any(|s| {
2867            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2868                [
2869                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
2870                ]
2871                .contains(&v.as_str())
2872            })
2873        });
2874        ConditionResult::from(found)
2875    }
2876
2877    /// [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
2878    fn evaluate_101(&self, ctx: &EvaluationContext) -> ConditionResult {
2879        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2880        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
2881        let found = cci_segs.iter().any(|s| {
2882            s.elements
2883                .get(2)
2884                .and_then(|e| e.first())
2885                .is_some_and(|v| v == "ZA6")
2886        });
2887        ConditionResult::from(found)
2888    }
2889
2890    /// [102] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+Z36 (TEP mit Ref.messung) in dieser SG8 vorhanden
2891    fn evaluate_102(&self, ctx: &EvaluationContext) -> ConditionResult {
2892        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2893        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
2894        let found = cci_segs.iter().any(|s| {
2895            s.elements
2896                .get(2)
2897                .and_then(|e| e.first())
2898                .is_some_and(|v| v == "ZA6")
2899        });
2900        ConditionResult::from(found)
2901    }
2902
2903    /// [103] Wenn SG9 QTY+265 (Arbeit / Leistung für tagesparameterabhängig e Marktlokation: Veranschlagte Jahresmenge Gesamt) in dieser SG8 vorhanden
2904    fn evaluate_103(&self, ctx: &EvaluationContext) -> ConditionResult {
2905        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2906        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2907        let found = qty_segs.iter().any(|s| {
2908            s.elements
2909                .first()
2910                .and_then(|e| e.first())
2911                .is_some_and(|v| v == "265")
2912        });
2913        ConditionResult::from(found)
2914    }
2915
2916    /// [104] Wenn SG9 QTY+Z08 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: angepasste elektrische Arbeit nach Anhang D) in dieser SG8 vorhanden
2917    fn evaluate_104(&self, ctx: &EvaluationContext) -> ConditionResult {
2918        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2919        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2920        let found = qty_segs.iter().any(|s| {
2921            s.elements
2922                .first()
2923                .and_then(|e| e.first())
2924                .is_some_and(|v| v == "Z08")
2925        });
2926        ConditionResult::from(found)
2927    }
2928
2929    /// [105] Wenn SG9 QTY+Z10 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: Leistung der Marktlokation) in dieser SG8 vorhanden
2930    fn evaluate_105(&self, ctx: &EvaluationContext) -> ConditionResult {
2931        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2932        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2933        let found = qty_segs.iter().any(|s| {
2934            s.elements
2935                .first()
2936                .and_then(|e| e.first())
2937                .is_some_and(|v| v == "Z10")
2938        });
2939        ConditionResult::from(found)
2940    }
2941
2942    /// [106] Wenn in dieser SG8 SEQ+Z01 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
2943    fn evaluate_106(&self, ctx: &EvaluationContext) -> ConditionResult {
2944        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2945        // Check: in SG8 with SEQ+Z01, SG10 has CCI+++ZA6 and CAV+E02
2946        ctx.this_group_parent_child_has_qualifier(
2947            &["SG4", "SG8"],
2948            "SEQ",
2949            0,
2950            "Z01",
2951            "SG10",
2952            "CAV",
2953            0,
2954            "E02",
2955        )
2956    }
2957
2958    /// [107] Wenn in derselben SG8 das SG10 CAV+SLS/ SES/ BIP/ GEP/ GAP/ SOP/ WNP/ WFP/ WAP vorhanden
2959    fn evaluate_107(&self, ctx: &EvaluationContext) -> ConditionResult {
2960        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2961        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
2962        let found = cav_segs.iter().any(|s| {
2963            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2964                [
2965                    "SLS", "SES", "BIP", "GEP", "GAP", "SOP", "WNP", "WFP", "WAP",
2966                ]
2967                .contains(&v.as_str())
2968            })
2969        });
2970        ConditionResult::from(found)
2971    }
2972
2973    /// [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
2974    // HAND-EDITED — do not let generate-conditions regenerate this function
2975    // without first confirming prompt.rs B6 SCOPE RULES section is in the
2976    // system prompt. Regeneration without that guidance would emit
2977    // ctx.has_qualifier (message-wide) and silently reintroduce the
2978    // per-instance bug this function fixes.
2979    // See: docs/plans/2026-04-23-ahb-codelist-and-per-instance-plan.md B7.
2980    fn evaluate_108(&self, ctx: &EvaluationContext) -> ConditionResult {
2981        // [108] Wenn in derselben SG8 ein PIA+Z02 mit einem Code aus
2982        // 1-08-1/2/3/4/5 vorhanden. Scope-aware: narrows to the enclosing
2983        // SG8 instance when GroupScope is set; falls back to message-wide.
2984        const PREFIXES: &[&str] = &["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"];
2985        let found = ctx
2986            .scoped_find_segments_in("SG8", "PIA")
2987            .iter()
2988            .filter(|s| {
2989                s.elements
2990                    .first()
2991                    .and_then(|e| e.first())
2992                    .is_some_and(|q| q == "Z02")
2993            })
2994            .any(|s| {
2995                s.elements
2996                    .get(1)
2997                    .and_then(|e| e.first())
2998                    .is_some_and(|v| PREFIXES.iter().any(|p| v.starts_with(p)))
2999            });
3000        ConditionResult::from(found)
3001    }
3002
3003    /// [110] Wenn SG10 CAV+LGS/ EGS/ BIL/ GEL/ GAL/ SOL/ WNL/ WFL / WAL vorhanden
3004    fn evaluate_110(&self, ctx: &EvaluationContext) -> ConditionResult {
3005        let cav_segs = ctx.find_segments("CAV");
3006        let found = cav_segs.iter().any(|s| {
3007            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
3008                [
3009                    "LGS", "EGS", "BIL", "GEL", "GAL", "SOL", "WNL", "WFL", "WAL",
3010                ]
3011                .contains(&v.as_str())
3012            })
3013        });
3014        ConditionResult::from(found)
3015    }
3016
3017    /// [111] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) nicht vorhanden
3018    fn evaluate_111(&self, ctx: &EvaluationContext) -> ConditionResult {
3019        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3020        // scope reaches one; the message as before otherwise.
3021        if let Some(scope) = ctx.enclosing_scope("SG8") {
3022            return ConditionResult::from(
3023                ctx.find_segments_at(scope, "SEQ")
3024                    .iter()
3025                    .any(|s| matches!(s.get_element(0), "Z59"))
3026                    && !ctx
3027                        .find_segments_at(scope, "PIA")
3028                        .iter()
3029                        .any(|s| s.get_element(0) == "5"),
3030            );
3031        }
3032        ctx.any_group_has_qualifier_without("SEQ", 0, "Z59", "PIA", 0, "5", &["SG4", "SG8"])
3033    }
3034
3035    /// [112] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das SG10 CCI+11 (Details zum Produkt der Marktlokation) nicht vorhanden
3036    fn evaluate_112(&self, ctx: &EvaluationContext) -> ConditionResult {
3037        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3038        // scope reaches one; the message as before otherwise.
3039        if let Some(scope) = ctx.enclosing_scope("SG8") {
3040            return ConditionResult::from(
3041                ctx.find_segments_at(scope, "SEQ")
3042                    .iter()
3043                    .any(|s| matches!(s.get_element(0), "Z59"))
3044                    && !ctx
3045                        .find_segments_at(scope, "CCI")
3046                        .iter()
3047                        .any(|s| s.get_element(0) == "11"),
3048            );
3049        }
3050        let result = ctx.filtered_parent_child_has_qualifier(
3051            &["SG4", "SG8"],
3052            "SEQ",
3053            0,
3054            "Z59",
3055            "SG10",
3056            "CCI",
3057            0,
3058            "11",
3059        );
3060        match result {
3061            ConditionResult::True => ConditionResult::False,
3062            ConditionResult::False => ConditionResult::True,
3063            ConditionResult::Unknown => ConditionResult::Unknown,
3064        }
3065    }
3066
3067    /// [113] Wenn es sich um eine Antwort auf die Bestellung einer Zählzeit handelt
3068    fn evaluate_113(&self, ctx: &EvaluationContext) -> ConditionResult {
3069        // Antwort auf Bestellung einer Zählzeit — depends on PID context
3070        ctx.external.evaluate("counting_time_order_response")
3071    }
3072
3073    /// [114] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E06 (Niederspannung) in dieser SG vorhanden
3074    // 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)
3075    fn evaluate_114(&self, ctx: &EvaluationContext) -> ConditionResult {
3076        let nav = match ctx.navigator {
3077            Some(n) => n,
3078            None => {
3079                return match (
3080                    ctx.has_qualifier("CCI", 0, "E03"),
3081                    ctx.has_qualifier("CAV", 0, "E06"),
3082                ) {
3083                    (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
3084                    (ConditionResult::False, _) | (_, ConditionResult::False) => {
3085                        ConditionResult::False
3086                    }
3087                    _ => ConditionResult::Unknown,
3088                };
3089            }
3090        };
3091        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3092        for i in 0..sg8_count {
3093            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3094            for j in 0..sg10_count {
3095                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3096                let has_cci_e03 = ccis.iter().any(|s| {
3097                    s.elements
3098                        .first()
3099                        .and_then(|e: &Vec<String>| e.first())
3100                        .is_some_and(|v: &String| v == "E03")
3101                });
3102                if has_cci_e03 {
3103                    let cavs =
3104                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3105                    if cavs.iter().any(|s| {
3106                        s.elements
3107                            .first()
3108                            .and_then(|e: &Vec<String>| e.first())
3109                            .is_some_and(|v: &String| v == "E06")
3110                    }) {
3111                        return ConditionResult::True;
3112                    }
3113                }
3114            }
3115        }
3116        ConditionResult::False
3117    }
3118
3119    /// [115] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E05 (Mittelspannung) in dieser SG vorhanden
3120    fn evaluate_115(&self, ctx: &EvaluationContext) -> ConditionResult {
3121        let cci_segs = ctx.find_segments("CCI");
3122        let has_cci = cci_segs.iter().any(|s| {
3123            s.elements
3124                .first()
3125                .and_then(|e| e.first())
3126                .is_some_and(|v| v == "E03")
3127        });
3128        let cav_segs = ctx.find_segments("CAV");
3129        let has_cav = cav_segs.iter().any(|s| {
3130            s.elements
3131                .first()
3132                .and_then(|e| e.first())
3133                .is_some_and(|v| v == "E05")
3134        });
3135        ConditionResult::from(has_cci && has_cav)
3136    }
3137
3138    /// [116] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E04 (Hochspannung) in dieser SG vorhanden
3139    fn evaluate_116(&self, ctx: &EvaluationContext) -> ConditionResult {
3140        let cci_segs = ctx.find_segments("CCI");
3141        let has_cci = cci_segs.iter().any(|s| {
3142            s.elements
3143                .first()
3144                .and_then(|e| e.first())
3145                .is_some_and(|v| v == "E03")
3146        });
3147        let cav_segs = ctx.find_segments("CAV");
3148        let has_cav = cav_segs.iter().any(|s| {
3149            s.elements
3150                .first()
3151                .and_then(|e| e.first())
3152                .is_some_and(|v| v == "E04")
3153        });
3154        ConditionResult::from(has_cci && has_cav)
3155    }
3156
3157    /// [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 (...
3158    // 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)
3159    fn evaluate_117(&self, ctx: &EvaluationContext) -> ConditionResult {
3160        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (TLP/TEP) — compared within the enclosing SG4 when the scope
3161        // reaches an SG8; the message as before otherwise.
3162        if let Some(scope) = ctx.enclosing_scope("SG8") {
3163            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
3164            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
3165                sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "E14")
3166            }));
3167        }
3168        let nav = match ctx.navigator {
3169            Some(n) => n,
3170            None => return ConditionResult::Unknown,
3171        };
3172        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3173        for i in 0..sg8_count {
3174            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3175            for j in 0..sg10_count {
3176                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3177                let has_cci_za6 = ccis.iter().any(|s| {
3178                    s.elements
3179                        .get(2)
3180                        .and_then(|e: &Vec<String>| e.first())
3181                        .is_some_and(|v: &String| v == "ZA6")
3182                });
3183                if has_cci_za6 {
3184                    let cavs =
3185                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3186                    if cavs.iter().any(|s| {
3187                        s.elements
3188                            .first()
3189                            .and_then(|e: &Vec<String>| e.first())
3190                            .is_some_and(|v: &String| v == "E14")
3191                    }) {
3192                        return ConditionResult::True;
3193                    }
3194                }
3195            }
3196        }
3197        ConditionResult::False
3198    }
3199
3200    /// [118] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/ SEP) vorhanden
3201    fn evaluate_118(&self, ctx: &EvaluationContext) -> ConditionResult {
3202        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3203        // scope reaches one; the message as before otherwise.
3204        if let Some(scope) = ctx.enclosing_scope("SG8") {
3205            return ConditionResult::from(
3206                ctx.find_segments_at(scope, "SEQ")
3207                    .iter()
3208                    .any(|s| matches!(s.get_element(0), "Z98"))
3209                    && ctx
3210                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
3211                        .unwrap_or_default()
3212                        .iter()
3213                        .any(|segs| {
3214                            segs.iter()
3215                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
3216                                && segs
3217                                    .iter()
3218                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E02")
3219                        }),
3220            );
3221        }
3222        let has_cci = ctx.filtered_parent_child_has_qualifier(
3223            &["SG4", "SG8"],
3224            "SEQ",
3225            0,
3226            "Z98",
3227            "SG10",
3228            "CCI",
3229            2,
3230            "ZA6",
3231        );
3232        if has_cci != ConditionResult::True {
3233            return has_cci;
3234        }
3235        ctx.filtered_parent_child_has_qualifier(
3236            &["SG4", "SG8"],
3237            "SEQ",
3238            0,
3239            "Z98",
3240            "SG10",
3241            "CAV",
3242            0,
3243            "E02",
3244        )
3245    }
3246
3247    /// [119] Wenn in der SG8 SEQ+Z01/ Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
3248    fn evaluate_119(&self, ctx: &EvaluationContext) -> ConditionResult {
3249        // Check: in SG8 with SEQ+Z01 or Z98, SG10 has CCI+++ZA6 and CAV+E02
3250        let r1 = ctx.filtered_parent_child_has_qualifier(
3251            &["SG4", "SG8"],
3252            "SEQ",
3253            0,
3254            "Z01",
3255            "SG10",
3256            "CAV",
3257            0,
3258            "E02",
3259        );
3260        if r1 == ConditionResult::True {
3261            return ConditionResult::True;
3262        }
3263        ctx.filtered_parent_child_has_qualifier(
3264            &["SG4", "SG8"],
3265            "SEQ",
3266            0,
3267            "Z98",
3268            "SG10",
3269            "CAV",
3270            0,
3271            "E02",
3272        )
3273    }
3274
3275    /// [120] Wenn von NB Abtretungserklärung benötigt wird
3276    fn evaluate_120(&self, _ctx: &EvaluationContext) -> ConditionResult {
3277        // TODO: implement
3278        ConditionResult::Unknown
3279    }
3280
3281    /// [121] Wenn in dem SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) das SG8 RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden
3282    fn evaluate_121(&self, ctx: &EvaluationContext) -> ConditionResult {
3283        let z03 =
3284            ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"]);
3285        if z03 == ConditionResult::True {
3286            return ConditionResult::True;
3287        }
3288        ctx.any_group_has_qualifier_without("SEQ", 0, "ZF5", "RFF", 0, "Z14", &["SG4", "SG8"])
3289    }
3290
3291    /// [122] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++E03 (Spannungsebene der Marktlokation) CAV+E06 (Niederspannung) vorhanden
3292    // 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)
3293    fn evaluate_122(&self, ctx: &EvaluationContext) -> ConditionResult {
3294        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3295        // scope reaches one; the message as before otherwise.
3296        if let Some(scope) = ctx.enclosing_scope("SG8") {
3297            return ConditionResult::from(
3298                ctx.find_segments_at(scope, "SEQ")
3299                    .iter()
3300                    .any(|s| matches!(s.get_element(0), "Z98"))
3301                    && ctx
3302                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
3303                        .unwrap_or_default()
3304                        .iter()
3305                        .any(|segs| {
3306                            segs.iter()
3307                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "E03")
3308                                && segs
3309                                    .iter()
3310                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E06")
3311                        }),
3312            );
3313        }
3314        // Check: in SG8 with SEQ+Z98, SG10 child has CCI+++E03 (Spannungsebene, elements[2][0]="E03")
3315        let has_cci_e03 = ctx.filtered_parent_child_has_qualifier(
3316            &["SG4", "SG8"],
3317            "SEQ",
3318            0,
3319            "Z98",
3320            "SG10",
3321            "CCI",
3322            2,
3323            "E03",
3324        );
3325        if has_cci_e03 != ConditionResult::True {
3326            return has_cci_e03;
3327        }
3328        // Also verify CAV+E06 (Niederspannung, elements[0][0]="E06") exists in the same SG8 scope
3329        ctx.any_group_has_qualifier("CAV", 0, "E06", &["SG4", "SG8"])
3330    }
3331
3332    /// [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...
3333    // 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)
3334    fn evaluate_123(&self, ctx: &EvaluationContext) -> ConditionResult {
3335        struct Sg8Z20Entry {
3336            mg_id: Option<String>,
3337        }
3338        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3339        let mut current: Option<Sg8Z20Entry> = None;
3340        for seg in ctx.segments {
3341            match seg.id.as_str() {
3342                "SEQ" => {
3343                    if let Some(entry) = current.take() {
3344                        entries.push(entry);
3345                    }
3346                    if seg
3347                        .elements
3348                        .first()
3349                        .and_then(|e: &Vec<String>| e.first())
3350                        .is_some_and(|v: &String| v == "Z20")
3351                    {
3352                        current = Some(Sg8Z20Entry { mg_id: None });
3353                    }
3354                }
3355                "RFF" if current.is_some() => {
3356                    if seg
3357                        .elements
3358                        .first()
3359                        .and_then(|e: &Vec<String>| e.first())
3360                        .is_some_and(|v: &String| v == "MG")
3361                    {
3362                        if let Some(ref mut entry) = current {
3363                            if entry.mg_id.is_none() {
3364                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3365                            }
3366                        }
3367                    }
3368                }
3369                _ => {}
3370            }
3371        }
3372        if let Some(entry) = current {
3373            entries.push(entry);
3374        }
3375        let ids: Vec<&str> = entries
3376            .iter()
3377            .filter_map(|e| e.mg_id.as_deref().filter(|s| !s.is_empty()))
3378            .collect();
3379        for i in 0..ids.len() {
3380            for j in (i + 1)..ids.len() {
3381                if ids[i] == ids[j] {
3382                    return ConditionResult::True;
3383                }
3384            }
3385        }
3386        ConditionResult::False
3387    }
3388
3389    /// [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...
3390    // 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)
3391    fn evaluate_124(&self, ctx: &EvaluationContext) -> ConditionResult {
3392        // HAND-EDITED: "noch mindestens eine weitere SG8 SEQ+Z20 mit derselben Zeitraum-ID … RFF+MG" — compared within the enclosing SG4 when the scope
3393        // reaches an SG8; the message as before otherwise.
3394        if let Some(scope) = ctx.enclosing_scope("SG8") {
3395            let Some((_, zeitraum)) = sg4_instances::seq_at(ctx, scope)
3396                .filter(|(code, zeitraum)| code == "Z20" && !zeitraum.is_empty())
3397            else {
3398                return ConditionResult::False;
3399            };
3400            let meters = sg4_instances::rff_values_at(ctx, scope, "MG");
3401            return ConditionResult::from(sg4_instances::other_sg8s(ctx, scope).iter().any(|p| {
3402                sg4_instances::seq_at(ctx, p).is_some_and(|(c, z)| c == "Z20" && z == zeitraum)
3403                    && sg4_instances::rff_values_at(ctx, p, "MG")
3404                        .iter()
3405                        .any(|m| meters.contains(m))
3406            }));
3407        }
3408        struct Sg8Z20Entry {
3409            zeitraum_id: String,
3410            mg_id: Option<String>,
3411        }
3412        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3413        let mut current: Option<Sg8Z20Entry> = None;
3414        for seg in ctx.segments {
3415            match seg.id.as_str() {
3416                "SEQ" => {
3417                    if let Some(entry) = current.take() {
3418                        entries.push(entry);
3419                    }
3420                    if seg
3421                        .elements
3422                        .first()
3423                        .and_then(|e: &Vec<String>| e.first())
3424                        .is_some_and(|v: &String| v == "Z20")
3425                    {
3426                        let zeitraum_id = seg
3427                            .elements
3428                            .get(1)
3429                            .and_then(|e: &Vec<String>| e.first())
3430                            .cloned()
3431                            .unwrap_or_default();
3432                        current = Some(Sg8Z20Entry {
3433                            zeitraum_id,
3434                            mg_id: None,
3435                        });
3436                    }
3437                }
3438                "RFF" if current.is_some() => {
3439                    if seg
3440                        .elements
3441                        .first()
3442                        .and_then(|e: &Vec<String>| e.first())
3443                        .is_some_and(|v: &String| v == "MG")
3444                    {
3445                        if let Some(ref mut entry) = current {
3446                            if entry.mg_id.is_none() {
3447                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3448                            }
3449                        }
3450                    }
3451                }
3452                _ => {}
3453            }
3454        }
3455        if let Some(entry) = current {
3456            entries.push(entry);
3457        }
3458        for i in 0..entries.len() {
3459            for j in (i + 1)..entries.len() {
3460                let ei = &entries[i];
3461                let ej = &entries[j];
3462                if !ei.zeitraum_id.is_empty()
3463                    && ei.zeitraum_id == ej.zeitraum_id
3464                    && ei.mg_id.is_some()
3465                    && !ei.mg_id.as_deref().unwrap_or("").is_empty()
3466                    && ei.mg_id == ej.mg_id
3467                {
3468                    return ConditionResult::True;
3469                }
3470            }
3471        }
3472        ConditionResult::False
3473    }
3474
3475    /// [125] Wenn in derselben SG8 SEQ+Z76/ ZC5/ ZC6 (Messstellenbetriebsabrechnungsdaten der Marktlokation) im DE4347 des PIA Z02 (Gruppenartikel-ID / Artikel-ID) vorhanden
3476    fn evaluate_125(&self, ctx: &EvaluationContext) -> ConditionResult {
3477        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3478        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
3479        let found = seq_segs.iter().any(|s| {
3480            s.elements
3481                .first()
3482                .and_then(|e| e.first())
3483                .is_some_and(|v| ["Z76", "ZC5", "ZC6"].contains(&v.as_str()))
3484        });
3485        ConditionResult::from(found)
3486    }
3487
3488    /// [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...
3489    fn evaluate_126(&self, ctx: &EvaluationContext) -> ConditionResult {
3490        // Check: RFF+Z49 or Z53 repetition count determines value
3491        // Approximate: check if RFF+Z49 or Z53 exists (counter is structural)
3492        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z49").len()
3493            + ctx.find_segments_with_qualifier("RFF", 0, "Z53").len();
3494        ConditionResult::from(count > 0)
3495    }
3496
3497    /// [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...
3498    fn evaluate_127(&self, ctx: &EvaluationContext) -> ConditionResult {
3499        // Check: RFF+Z48 or Z55 repetition count determines value
3500        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z48").len()
3501            + ctx.find_segments_with_qualifier("RFF", 0, "Z55").len();
3502        ConditionResult::from(count > 0)
3503    }
3504
3505    /// [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...
3506    fn evaluate_128(&self, ctx: &EvaluationContext) -> ConditionResult {
3507        // Check: RFF+Z47 or Z54 repetition count determines value
3508        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z47").len()
3509            + ctx.find_segments_with_qualifier("RFF", 0, "Z54").len();
3510        ConditionResult::from(count > 0)
3511    }
3512
3513    /// [129] Innerhalb eines SG4 IDE müssen alle DE3225 der SG5 LOC+Z16 (Marktlokation) den identischen Wert enthalten
3514    fn evaluate_129(&self, ctx: &EvaluationContext) -> ConditionResult {
3515        // Check: all LOC+Z16 DE3225 values are identical
3516        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z16");
3517        if loc_segs.len() <= 1 {
3518            return ConditionResult::True;
3519        }
3520        let first_val = loc_segs[0].elements.get(1).and_then(|e| e.first());
3521        let all_same = loc_segs
3522            .iter()
3523            .all(|s| s.elements.get(1).and_then(|e| e.first()) == first_val);
3524        ConditionResult::from(all_same)
3525    }
3526
3527    /// [130] Wenn an Messlokation vorhanden
3528    fn evaluate_130(&self, _ctx: &EvaluationContext) -> ConditionResult {
3529        // TODO: implement
3530        ConditionResult::Unknown
3531    }
3532
3533    /// [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...
3534    fn evaluate_131(&self, ctx: &EvaluationContext) -> ConditionResult {
3535        // Check: DTM+Z25 is in SG6 RFF with Zeitraum-ID "1" in DE1156
3536        let has_zeitraum_id_1 = ctx.find_segments("RFF").iter().any(|s| {
3537            s.elements
3538                .first()
3539                .and_then(|e| e.get(2))
3540                .is_some_and(|v| v == "1")
3541        });
3542        ConditionResult::from(has_zeitraum_id_1)
3543    }
3544
3545    /// [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...
3546    // 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)
3547    fn evaluate_132(&self, ctx: &EvaluationContext) -> ConditionResult {
3548        // Condition applies only when RFF in SG6 has Zeitraum-ID "1" in DE1156 (elements[0][2])
3549        let has_zeitraum_id_1 = ctx.find_segments("RFF").iter().any(|s| {
3550            s.elements
3551                .first()
3552                .and_then(|e| e.get(2))
3553                .is_some_and(|v: &String| v == "1")
3554        });
3555        if !has_zeitraum_id_1 {
3556            return ConditionResult::False;
3557        }
3558        // Get message date from DTM+137 (format 303: CCYYMMDDHHmm...)
3559        let msg_date_str = match ctx
3560            .find_segments_with_qualifier("DTM", 0, "137")
3561            .into_iter()
3562            .next()
3563        {
3564            Some(seg) => match seg.elements.first().and_then(|e| e.get(1)) {
3565                Some(v) if v.len() >= 8 => v.clone(),
3566                _ => return ConditionResult::Unknown,
3567            },
3568            None => return ConditionResult::Unknown,
3569        };
3570        // Get DTM+Z25 (Verwendung der Daten ab) value
3571        let usage_date_str = match ctx
3572            .find_segments_with_qualifier("DTM", 0, "Z25")
3573            .into_iter()
3574            .next()
3575        {
3576            Some(seg) => match seg.elements.first().and_then(|e| e.get(1)) {
3577                Some(v) if v.len() >= 12 => v.clone(),
3578                _ => return ConditionResult::Unknown,
3579            },
3580            None => return ConditionResult::Unknown,
3581        };
3582        // Parse YYYYMMDD from DTM+137 value
3583        let year: u32 = match msg_date_str[..4].parse() {
3584            Ok(v) => v,
3585            Err(_) => return ConditionResult::Unknown,
3586        };
3587        let month: u32 = match msg_date_str[4..6].parse() {
3588            Ok(v) => v,
3589            Err(_) => return ConditionResult::Unknown,
3590        };
3591        let day: u32 = match msg_date_str[6..8].parse() {
3592            Ok(v) => v,
3593            Err(_) => return ConditionResult::Unknown,
3594        };
3595        let days_in_month: u32 = match month {
3596            1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
3597            4 | 6 | 9 | 11 => 30,
3598            2 => {
3599                if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) {
3600                    29
3601                } else {
3602                    28
3603                }
3604            }
3605            _ => return ConditionResult::Unknown,
3606        };
3607        let (next_year, next_month, next_day): (u32, u32, u32) = if day >= days_in_month {
3608            if month == 12 {
3609                (year + 1, 1, 1)
3610            } else {
3611                (year, month + 1, 1)
3612            }
3613        } else {
3614            (year, month, day + 1)
3615        };
3616        // Expected DTM+Z25: next day at 00:00 — format 303 prefix YYYYMMDDHHMM = 12 chars
3617        let expected_prefix = format!("{:04}{:02}{:02}0000", next_year, next_month, next_day);
3618        let actual_len = usage_date_str.len().min(12);
3619        let actual_prefix = &usage_date_str[..actual_len];
3620        ConditionResult::from(actual_prefix == expected_prefix.as_str())
3621    }
3622
3623    /// [133] Wenn an der übermittelten Marktlokation / Messlokation vorhanden
3624    fn evaluate_133(&self, _ctx: &EvaluationContext) -> ConditionResult {
3625        // TODO: implement
3626        ConditionResult::Unknown
3627    }
3628
3629    /// [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 ...
3630    // 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)
3631    fn evaluate_134(&self, ctx: &EvaluationContext) -> ConditionResult {
3632        let nav = match ctx.navigator() {
3633            Some(n) => n,
3634            None => return ConditionResult::Unknown,
3635        };
3636        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
3637        // Collect (zeitraum_id, dtm_z25_value, dtm_z26_value) for every Z48/Z55 SG6
3638        let mut slices: Vec<(u32, Option<String>, Option<String>)> = Vec::new();
3639        for i in 0..sg6_count {
3640            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
3641            // RFF C506: [0][0]=qualifier, [0][1]=ref-id, [0][2]=Zeitraum-ID
3642            let maybe_zid = rffs.iter().find_map(|s| {
3643                let qual = s
3644                    .elements
3645                    .first()
3646                    .and_then(|e| e.first())
3647                    .map(|v| v.as_str())?;
3648                if qual == "Z48" || qual == "Z55" {
3649                    s.elements
3650                        .first()
3651                        .and_then(|e| e.get(2))
3652                        .and_then(|v| v.parse::<u32>().ok())
3653                } else {
3654                    None
3655                }
3656            });
3657            if let Some(zid) = maybe_zid {
3658                let dtms = nav.find_segments_in_group("DTM", &["SG4", "SG6"], i);
3659                let z25 = dtms.iter().find_map(|s| {
3660                    if s.elements
3661                        .first()
3662                        .and_then(|e| e.first())
3663                        .map(|v| v.as_str())
3664                        == Some("Z25")
3665                    {
3666                        s.elements.first().and_then(|e| e.get(1)).cloned()
3667                    } else {
3668                        None
3669                    }
3670                });
3671                let z26 = dtms.iter().find_map(|s| {
3672                    if s.elements
3673                        .first()
3674                        .and_then(|e| e.first())
3675                        .map(|v| v.as_str())
3676                        == Some("Z26")
3677                    {
3678                        s.elements.first().and_then(|e| e.get(1)).cloned()
3679                    } else {
3680                        None
3681                    }
3682                });
3683                slices.push((zid, z25, z26));
3684            }
3685        }
3686        if slices.is_empty() {
3687            return ConditionResult::Unknown;
3688        }
3689        // For each slice with Zeitraum-ID > 1: its DTM+Z25 must equal DTM+Z26 of the previous slice
3690        for i in 0..slices.len() {
3691            let (zid, ref z25, _) = slices[i];
3692            if zid <= 1 {
3693                continue;
3694            }
3695            let prev_id = zid - 1;
3696            match slices.iter().find(|(id, _, _)| *id == prev_id) {
3697                Some((_, _, prev_z26)) => match (z25, prev_z26) {
3698                    (Some(curr), Some(prev_end)) => {
3699                        if curr != prev_end {
3700                            return ConditionResult::False;
3701                        }
3702                    }
3703                    _ => return ConditionResult::Unknown,
3704                },
3705                None => return ConditionResult::Unknown,
3706            }
3707        }
3708        ConditionResult::True
3709    }
3710
3711    /// [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...
3712    // 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)
3713    fn evaluate_135(&self, ctx: &EvaluationContext) -> ConditionResult {
3714        let nav = match ctx.navigator() {
3715            Some(n) => n,
3716            None => return ConditionResult::Unknown,
3717        };
3718        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
3719        // Collect (zeitraum_id, dtm_z25_value, dtm_z26_value) for every Z47/Z54 SG6
3720        let mut slices: Vec<(u32, Option<String>, Option<String>)> = Vec::new();
3721        for i in 0..sg6_count {
3722            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
3723            // RFF C506: [0][0]=qualifier, [0][1]=ref-id, [0][2]=Zeitraum-ID
3724            let maybe_zid = rffs.iter().find_map(|s| {
3725                let qual = s
3726                    .elements
3727                    .first()
3728                    .and_then(|e| e.first())
3729                    .map(|v| v.as_str())?;
3730                if qual == "Z47" || qual == "Z54" {
3731                    s.elements
3732                        .first()
3733                        .and_then(|e| e.get(2))
3734                        .and_then(|v| v.parse::<u32>().ok())
3735                } else {
3736                    None
3737                }
3738            });
3739            if let Some(zid) = maybe_zid {
3740                let dtms = nav.find_segments_in_group("DTM", &["SG4", "SG6"], i);
3741                let z25 = dtms.iter().find_map(|s| {
3742                    if s.elements
3743                        .first()
3744                        .and_then(|e| e.first())
3745                        .map(|v| v.as_str())
3746                        == Some("Z25")
3747                    {
3748                        s.elements.first().and_then(|e| e.get(1)).cloned()
3749                    } else {
3750                        None
3751                    }
3752                });
3753                let z26 = dtms.iter().find_map(|s| {
3754                    if s.elements
3755                        .first()
3756                        .and_then(|e| e.first())
3757                        .map(|v| v.as_str())
3758                        == Some("Z26")
3759                    {
3760                        s.elements.first().and_then(|e| e.get(1)).cloned()
3761                    } else {
3762                        None
3763                    }
3764                });
3765                slices.push((zid, z25, z26));
3766            }
3767        }
3768        if slices.is_empty() {
3769            return ConditionResult::Unknown;
3770        }
3771        // For each slice with Zeitraum-ID > 1: its DTM+Z25 must equal DTM+Z26 of the previous slice
3772        for i in 0..slices.len() {
3773            let (zid, ref z25, _) = slices[i];
3774            if zid <= 1 {
3775                continue;
3776            }
3777            let prev_id = zid - 1;
3778            match slices.iter().find(|(id, _, _)| *id == prev_id) {
3779                Some((_, _, prev_z26)) => match (z25, prev_z26) {
3780                    (Some(curr), Some(prev_end)) => {
3781                        if curr != prev_end {
3782                            return ConditionResult::False;
3783                        }
3784                    }
3785                    _ => return ConditionResult::Unknown,
3786                },
3787                None => return ConditionResult::Unknown,
3788            }
3789        }
3790        ConditionResult::True
3791    }
3792
3793    /// [136] Wenn SG10 CCI+++E13 (Zählertyp) CAV+EHZ (elektronischer Haushaltszähler) vorhanden
3794    fn evaluate_136(&self, ctx: &EvaluationContext) -> ConditionResult {
3795        let cci_segs = ctx.find_segments("CCI");
3796        let found = cci_segs.iter().any(|s| {
3797            s.elements
3798                .get(2)
3799                .and_then(|e| e.first())
3800                .is_some_and(|v| v == "E13")
3801        });
3802        ConditionResult::from(found)
3803    }
3804
3805    /// [137] Nicht bei Neuanlage
3806    fn evaluate_137(&self, ctx: &EvaluationContext) -> ConditionResult {
3807        // Check: not a Neuanlage (new registration) process
3808        // STS+7 with transaktionsgrund containing Neuanlage indicators
3809        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
3810        let is_neuanlage = sts_segs.iter().any(|s| {
3811            s.elements
3812                .get(2)
3813                .and_then(|e| e.first())
3814                .is_some_and(|v| v == "E01" || v == "E03")
3815        });
3816        ConditionResult::from(!is_neuanlage)
3817    }
3818
3819    /// [138] Wenn im selben QTY im DE6063 Z35 (Abschlag) vorhanden
3820    fn evaluate_138(&self, ctx: &EvaluationContext) -> ConditionResult {
3821        let qty_segs = ctx.find_segments("QTY");
3822        let found = qty_segs.iter().any(|s| {
3823            s.elements
3824                .first()
3825                .and_then(|e| e.first())
3826                .is_some_and(|v| ["Z35"].contains(&v.as_str()))
3827        });
3828        ConditionResult::from(found)
3829    }
3830
3831    /// [139] Wenn SG10 CAV+IVA (Individuelle Abstimmung) nicht vorhanden
3832    // 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)
3833    fn evaluate_139(&self, ctx: &EvaluationContext) -> ConditionResult {
3834        let nav = match ctx.navigator {
3835            Some(n) => n,
3836            None => return ctx.lacks_qualifier("CAV", 0, "IVA"),
3837        };
3838        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3839        for i in 0..sg8_count {
3840            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3841            for j in 0..sg10_count {
3842                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3843                if cavs.iter().any(|s| {
3844                    s.elements
3845                        .first()
3846                        .and_then(|e: &Vec<String>| e.first())
3847                        .is_some_and(|v: &String| v == "IVA")
3848                }) {
3849                    return ConditionResult::False;
3850                }
3851            }
3852        }
3853        ConditionResult::True
3854    }
3855
3856    /// [140] Wenn im selben QTY im DE6063 Z34/Z35 (Zuschlag/Abschlag) vorhanden
3857    fn evaluate_140(&self, ctx: &EvaluationContext) -> ConditionResult {
3858        let qty_segs = ctx.find_segments("QTY");
3859        let found = qty_segs.iter().any(|s| {
3860            s.elements
3861                .first()
3862                .and_then(|e| e.first())
3863                .is_some_and(|v| ["Z34", "Z35"].contains(&v.as_str()))
3864        });
3865        ConditionResult::from(found)
3866    }
3867
3868    /// [141] Wenn SG10 CAV+MIW/ MPW/ MBW vorhanden
3869    fn evaluate_141(&self, ctx: &EvaluationContext) -> ConditionResult {
3870        let cav_segs = ctx.find_segments("CAV");
3871        let found = cav_segs.iter().any(|s| {
3872            s.elements
3873                .first()
3874                .and_then(|e| e.first())
3875                .is_some_and(|v| ["MIW", "MPW", "MBW"].contains(&v.as_str()))
3876        });
3877        ConditionResult::from(found)
3878    }
3879
3880    /// [142] Wenn SG8 PIA+Z01+:Z04/ Z05 (Berechnung Tagesmitteltemperatur: vom Anbieter zur Verfügung gestellte - / Äquivalente Tagesmitteltemperatur) vorhanden
3881    fn evaluate_142(&self, ctx: &EvaluationContext) -> ConditionResult {
3882        let pia_segs = ctx.find_segments("PIA");
3883        let found = pia_segs.iter().any(|s| {
3884            s.elements
3885                .first()
3886                .and_then(|e| e.first())
3887                .is_some_and(|v| v == "Z01")
3888                && s.elements
3889                    .get(1)
3890                    .and_then(|e| e.get(1))
3891                    .is_some_and(|v| ["Z04", "Z05"].contains(&v.as_str()))
3892        });
3893        ConditionResult::from(found)
3894    }
3895
3896    /// [143] Wenn STS+7++ZX3 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3897    fn evaluate_143(&self, ctx: &EvaluationContext) -> ConditionResult {
3898        let sts_segs = ctx.find_segments("STS");
3899        let found = sts_segs.iter().any(|s| {
3900            s.elements
3901                .first()
3902                .and_then(|e| e.first())
3903                .is_some_and(|v| v == "7")
3904                && s.elements
3905                    .get(2)
3906                    .and_then(|e| e.first())
3907                    .is_some_and(|v| v == "ZX3")
3908        });
3909        ConditionResult::from(found)
3910    }
3911
3912    /// [144] Wenn STS+7++ZAN (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3913    fn evaluate_144(&self, ctx: &EvaluationContext) -> ConditionResult {
3914        let sts_segs = ctx.find_segments("STS");
3915        let found = sts_segs.iter().any(|s| {
3916            s.elements
3917                .first()
3918                .and_then(|e| e.first())
3919                .is_some_and(|v| v == "7")
3920                && s.elements
3921                    .get(2)
3922                    .and_then(|e| e.first())
3923                    .is_some_and(|v| v == "ZAN")
3924        });
3925        ConditionResult::from(found)
3926    }
3927
3928    /// [145] Wenn STS+7++ZX2 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3929    fn evaluate_145(&self, ctx: &EvaluationContext) -> ConditionResult {
3930        let sts_segs = ctx.find_segments("STS");
3931        let found = sts_segs.iter().any(|s| {
3932            s.elements
3933                .first()
3934                .and_then(|e| e.first())
3935                .is_some_and(|v| v == "7")
3936                && s.elements
3937                    .get(2)
3938                    .and_then(|e| e.first())
3939                    .is_some_and(|v| v == "ZX2")
3940        });
3941        ConditionResult::from(found)
3942    }
3943
3944    /// [146] Wenn STS+7++ZA0 (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3945    fn evaluate_146(&self, ctx: &EvaluationContext) -> ConditionResult {
3946        let sts_segs = ctx.find_segments("STS");
3947        let found = sts_segs.iter().any(|s| {
3948            s.elements
3949                .first()
3950                .and_then(|e| e.first())
3951                .is_some_and(|v| v == "7")
3952                && s.elements
3953                    .get(2)
3954                    .and_then(|e| e.first())
3955                    .is_some_and(|v| v == "ZA0")
3956        });
3957        ConditionResult::from(found)
3958    }
3959
3960    /// [147] Wenn in Anfrage vorhanden
3961    fn evaluate_147(&self, _ctx: &EvaluationContext) -> ConditionResult {
3962        // TODO: implement
3963        ConditionResult::Unknown
3964    }
3965
3966    /// [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
3967    fn evaluate_148(&self, ctx: &EvaluationContext) -> ConditionResult {
3968        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3969        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
3970        let found = pia_segs.iter().any(|s| {
3971            s.elements
3972                .first()
3973                .and_then(|e| e.first())
3974                .is_some_and(|v| v == "Z02")
3975                && s.elements
3976                    .get(1)
3977                    .and_then(|e| e.first())
3978                    .is_some_and(|v| ["1-02-0-017", "1-02-0-018"].contains(&v.as_str()))
3979        });
3980        ConditionResult::from(found)
3981    }
3982
3983    /// [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...
3984    // 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)
3985    fn evaluate_149(&self, ctx: &EvaluationContext) -> ConditionResult {
3986        use std::collections::{HashMap, HashSet};
3987        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
3988        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
3989        let pia_quals = ctx.collect_group_values("PIA", 0, 0, &["SG4", "SG8"]);
3990        let pia_prods = ctx.collect_group_values("PIA", 1, 0, &["SG4", "SG8"]);
3991        // Map group index -> Zeitraum-ID for SG8s with SEQ+Z45
3992        let mut idx_to_zeitraum: HashMap<usize, String> = HashMap::new();
3993        for ((gi, q), (_, zid)) in seq_quals.iter().zip(seq_zeitraum.iter()) {
3994            if q == "Z45" && !zid.is_empty() {
3995                idx_to_zeitraum.insert(*gi, zid.clone());
3996            }
3997        }
3998        if idx_to_zeitraum.is_empty() {
3999            return ConditionResult::False;
4000        }
4001        // Map Zeitraum-ID -> set of PIA+Z02 product codes found in the same SG8 group
4002        let mut zeitraum_products: HashMap<String, HashSet<String>> = HashMap::new();
4003        for (i, (gi, qual)) in pia_quals.iter().enumerate() {
4004            if qual == "Z02" {
4005                if let Some(zid) = idx_to_zeitraum.get(gi) {
4006                    if let Some((_, prod)) = pia_prods.get(i) {
4007                        if !prod.is_empty() {
4008                            zeitraum_products
4009                                .entry(zid.clone())
4010                                .or_default()
4011                                .insert(prod.clone());
4012                        }
4013                    }
4014                }
4015            }
4016        }
4017        ConditionResult::from(
4018            zeitraum_products
4019                .values()
4020                .any(|codes| codes.contains("1-02-0-017") && codes.contains("1-02-0-018")),
4021        )
4022    }
4023
4024    /// [150] Wenn in dieser SG8 im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), die Artikel-ID 1-02-0-002 vorhanden
4025    fn evaluate_150(&self, ctx: &EvaluationContext) -> ConditionResult {
4026        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4027        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4028        let found = pia_segs.iter().any(|s| {
4029            s.elements
4030                .first()
4031                .and_then(|e| e.first())
4032                .is_some_and(|v| v == "Z02")
4033                && s.elements
4034                    .get(1)
4035                    .and_then(|e| e.first())
4036                    .is_some_and(|v| ["1-02-0-002"].contains(&v.as_str()))
4037        });
4038        ConditionResult::from(found)
4039    }
4040
4041    /// [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...
4042    fn evaluate_151(&self, ctx: &EvaluationContext) -> ConditionResult {
4043        ctx.external.evaluate("code_list_membership_check")
4044    }
4045
4046    /// [152] Wenn FTX+Z28 (IP-Range des Absenders) nicht vorhanden
4047    fn evaluate_152(&self, ctx: &EvaluationContext) -> ConditionResult {
4048        let ftx_segs = ctx.find_segments("FTX");
4049        let found = ftx_segs.iter().any(|s| {
4050            s.elements
4051                .first()
4052                .and_then(|e| e.first())
4053                .is_some_and(|v| v == "Z28")
4054        });
4055        ConditionResult::from(!found)
4056    }
4057
4058    /// [153] Wenn FTX+Z27 (IP-Adresse des Absenders) nicht vorhanden
4059    fn evaluate_153(&self, ctx: &EvaluationContext) -> ConditionResult {
4060        let ftx_segs = ctx.find_segments("FTX");
4061        let found = ftx_segs.iter().any(|s| {
4062            s.elements
4063                .first()
4064                .and_then(|e| e.first())
4065                .is_some_and(|v| v == "Z27")
4066        });
4067        ConditionResult::from(!found)
4068    }
4069
4070    /// [156] Wenn SG4 FTX+Z17 (Zieladresse URI) vorhanden
4071    fn evaluate_156(&self, ctx: &EvaluationContext) -> ConditionResult {
4072        let ftx_segs = ctx.find_segments("FTX");
4073        let found = ftx_segs.iter().any(|s| {
4074            s.elements
4075                .first()
4076                .and_then(|e| e.first())
4077                .is_some_and(|v| v == "Z17")
4078        });
4079        ConditionResult::from(found)
4080    }
4081
4082    /// [161] Wenn SG4 STS+7++E03+ZW8 (Transaktionsgrundergänzung Fall 1) vorhanden
4083    fn evaluate_161(&self, ctx: &EvaluationContext) -> ConditionResult {
4084        let sts_segs = ctx.find_segments("STS");
4085        let found = sts_segs.iter().any(|s| {
4086            s.elements
4087                .first()
4088                .and_then(|e| e.first())
4089                .is_some_and(|v| v == "7")
4090                && s.elements
4091                    .get(2)
4092                    .and_then(|e| e.first())
4093                    .is_some_and(|v| v == "E03")
4094                && s.elements
4095                    .get(3)
4096                    .and_then(|e| e.first())
4097                    .is_some_and(|v| v == "ZW8")
4098        });
4099        ConditionResult::from(found)
4100    }
4101
4102    /// [162] Wenn SG4 STS+7++E03+ZW9 (Transaktionsgrundergänzung Fall 2) vorhanden
4103    fn evaluate_162(&self, ctx: &EvaluationContext) -> ConditionResult {
4104        let sts_segs = ctx.find_segments("STS");
4105        let found = sts_segs.iter().any(|s| {
4106            s.elements
4107                .first()
4108                .and_then(|e| e.first())
4109                .is_some_and(|v| v == "7")
4110                && s.elements
4111                    .get(2)
4112                    .and_then(|e| e.first())
4113                    .is_some_and(|v| v == "E03")
4114                && s.elements
4115                    .get(3)
4116                    .and_then(|e| e.first())
4117                    .is_some_and(|v| v == "ZW9")
4118        });
4119        ConditionResult::from(found)
4120    }
4121
4122    /// [163] Wenn SG4 STS+7++E03+ZX0 (Transaktionsgrundergänzung Fall 3) vorhanden
4123    fn evaluate_163(&self, ctx: &EvaluationContext) -> ConditionResult {
4124        let sts_segs = ctx.find_segments("STS");
4125        let found = sts_segs.iter().any(|s| {
4126            s.elements
4127                .first()
4128                .and_then(|e| e.first())
4129                .is_some_and(|v| v == "7")
4130                && s.elements
4131                    .get(2)
4132                    .and_then(|e| e.first())
4133                    .is_some_and(|v| v == "E03")
4134                && s.elements
4135                    .get(3)
4136                    .and_then(|e| e.first())
4137                    .is_some_and(|v| v == "ZX0")
4138        });
4139        ConditionResult::from(found)
4140    }
4141
4142    /// [164] Wenn SG4 STS+7++E03+ZX1 (Transaktionsgrundergänzung Fall 4) vorhanden
4143    fn evaluate_164(&self, ctx: &EvaluationContext) -> ConditionResult {
4144        let sts_segs = ctx.find_segments("STS");
4145        let found = sts_segs.iter().any(|s| {
4146            s.elements
4147                .first()
4148                .and_then(|e| e.first())
4149                .is_some_and(|v| v == "7")
4150                && s.elements
4151                    .get(2)
4152                    .and_then(|e| e.first())
4153                    .is_some_and(|v| v == "E03")
4154                && s.elements
4155                    .get(3)
4156                    .and_then(|e| e.first())
4157                    .is_some_and(|v| v == "ZX1")
4158        });
4159        ConditionResult::from(found)
4160    }
4161
4162    /// [165] Wenn bekannt
4163    fn evaluate_165(&self, _ctx: &EvaluationContext) -> ConditionResult {
4164        // TODO: implement
4165        ConditionResult::Unknown
4166    }
4167
4168    /// [166] Wenn vorhanden
4169    fn evaluate_166(&self, _ctx: &EvaluationContext) -> ConditionResult {
4170        // "If present" — always true when the field is being validated
4171        ConditionResult::True
4172    }
4173
4174    /// [167] Wenn SG5 LOC+Z21 (Tranche) vorhanden
4175    fn evaluate_167(&self, ctx: &EvaluationContext) -> ConditionResult {
4176        let loc_segs = ctx.find_segments("LOC");
4177        let found = loc_segs.iter().any(|s| {
4178            s.elements
4179                .first()
4180                .and_then(|e| e.first())
4181                .is_some_and(|v| v == "Z21")
4182        });
4183        ConditionResult::from(found)
4184    }
4185
4186    /// [170] Wenn Anschrift der Marktlokation vorhanden
4187    fn evaluate_170(&self, _ctx: &EvaluationContext) -> ConditionResult {
4188        // TODO: implement
4189        ConditionResult::Unknown
4190    }
4191
4192    /// [172] Wenn im selben QTY im DE6063 Z37 (Kein Zu- und Abschlag) vorhanden
4193    fn evaluate_172(&self, ctx: &EvaluationContext) -> ConditionResult {
4194        let qty_segs = ctx.find_segments("QTY");
4195        let found = qty_segs.iter().any(|s| {
4196            s.elements
4197                .first()
4198                .and_then(|e| e.first())
4199                .is_some_and(|v| ["Z37"].contains(&v.as_str()))
4200        });
4201        ConditionResult::from(found)
4202    }
4203
4204    /// [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 (...
4205    // 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)
4206    fn evaluate_173(&self, ctx: &EvaluationContext) -> ConditionResult {
4207        // HAND-EDITED: "in derselben SG8" — this SG8, a SEQ+Z58/ZC9/ZD0/ZD6, has an
4208        // RFF+Z33 without DE1156.
4209        let kinds = ctx.scoped_find_segments_in("SG8", "SEQ");
4210        if !kinds
4211            .iter()
4212            .any(|s| matches!(s.get_element(0), "Z58" | "ZC9" | "ZD0" | "ZD6"))
4213        {
4214            return ConditionResult::Unknown;
4215        }
4216        let z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
4217        if z33.is_empty() {
4218            return ConditionResult::Unknown;
4219        }
4220        ConditionResult::from(z33.iter().any(|s| s.get_component(0, 2).is_empty()))
4221    }
4222
4223    /// [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...
4224    fn evaluate_174(&self, ctx: &EvaluationContext) -> ConditionResult {
4225        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4226        // Check: RFF+Z33 has a value in DE1156 (fortlaufende Nummer)
4227        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
4228        let found = rff_z33.iter().any(|s| {
4229            s.elements
4230                .first()
4231                .and_then(|e| e.get(2))
4232                .is_some_and(|v| !v.is_empty())
4233        });
4234        ConditionResult::from(found)
4235    }
4236
4237    /// [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...
4238    // 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)
4239    fn evaluate_175(&self, ctx: &EvaluationContext) -> ConditionResult {
4240        // HAND-EDITED: "in einem weiteren SG8 SEQ+Z58 mit derselben fortlaufenden Nummer" — compared within the enclosing SG4 when the scope
4241        // reaches an SG8; the message as before otherwise.
4242        if let Some(scope) = ctx.enclosing_scope("SG8") {
4243            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
4244                ctx,
4245                scope,
4246                &["Z58"],
4247                true,
4248                false,
4249            ));
4250        }
4251        let nav = match ctx.navigator {
4252            Some(n) => n,
4253            None => return ConditionResult::Unknown,
4254        };
4255        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4256        let mut entries: Vec<(usize, String, String)> = Vec::new();
4257        for i in 0..sg8_count {
4258            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4259            if !seqs.iter().any(|s| {
4260                s.elements
4261                    .first()
4262                    .and_then(|e: &Vec<String>| e.first())
4263                    .is_some_and(|v: &String| v == "Z58")
4264            }) {
4265                continue;
4266            }
4267            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4268            for rff in &rffs {
4269                let elems = match rff.elements.first() {
4270                    Some(e) => e,
4271                    None => continue,
4272                };
4273                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4274                    continue;
4275                }
4276                let objektcode = elems.get(1).cloned().unwrap_or_default();
4277                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4278                if !objektcode.is_empty() {
4279                    entries.push((i, objektcode, fort_nr));
4280                }
4281            }
4282        }
4283        for a in 0..entries.len() {
4284            for b in (a + 1)..entries.len() {
4285                if entries[a].0 != entries[b].0
4286                    && entries[a].1 == entries[b].1
4287                    && entries[a].2 == entries[b].2
4288                {
4289                    return ConditionResult::True;
4290                }
4291            }
4292        }
4293        ConditionResult::False
4294    }
4295
4296    /// [176] Wenn in demselben Segment das DE1153 mit dem Code Z34 vorgelagerte Messlokation vorhanden ist
4297    fn evaluate_176(&self, ctx: &EvaluationContext) -> ConditionResult {
4298        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z34"))
4299    }
4300
4301    /// [177] Wenn in demselben Segment das DE1153 mit dem Code Z35 vorgelagerte Netzlokation vorhanden ist
4302    fn evaluate_177(&self, ctx: &EvaluationContext) -> ConditionResult {
4303        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z35"))
4304    }
4305
4306    /// [178] Wenn das LOC+Z18 (Netzlokation) vorhanden ist
4307    fn evaluate_178(&self, ctx: &EvaluationContext) -> ConditionResult {
4308        let loc_segs = ctx.find_segments("LOC");
4309        let found = loc_segs.iter().any(|s| {
4310            s.elements
4311                .first()
4312                .and_then(|e| e.first())
4313                .is_some_and(|v| v == "Z18")
4314        });
4315        ConditionResult::from(found)
4316    }
4317
4318    /// [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...
4319    fn evaluate_179(&self, ctx: &EvaluationContext) -> ConditionResult {
4320        // HAND-EDITED: this SG8's Objektcode (RFF+Z33 DE1154) must be in a
4321        // sibling SG8 with the same SEQ code (Z58/ZC9/ZD0/ZD6). Unknown without
4322        // a group scope: "derselben SG8" cannot be told apart then.
4323        const KINDS: [&str; 4] = ["Z58", "ZC9", "ZD0", "ZD6"];
4324        let Some(siblings) = ctx.sibling_scopes("SG8") else {
4325            return ConditionResult::Unknown;
4326        };
4327        let seq_of = |segs: &[OwnedSegment]| {
4328            segs.iter()
4329                .find(|s| s.id == "SEQ")
4330                .map(|s| s.get_element(0).to_string())
4331        };
4332        let objektcodes = |segs: &[OwnedSegment]| -> Vec<String> {
4333            segs.iter()
4334                .filter(|s| s.id == "RFF" && s.get_element(0) == "Z33")
4335                .map(|s| s.get_component(0, 1).to_string())
4336                .filter(|v| !v.is_empty())
4337                .collect()
4338        };
4339        let own_seq = seq_of(&ctx.scoped_find_segments_in("SG8", "SEQ"));
4340        let Some(own_seq) = own_seq.filter(|q| KINDS.contains(&q.as_str())) else {
4341            return ConditionResult::False;
4342        };
4343        let own = objektcodes(&ctx.scoped_find_segments_in("SG8", "RFF"));
4344        if own.is_empty() {
4345            return ConditionResult::False;
4346        }
4347        let found = siblings.iter().any(|path| {
4348            seq_of(&ctx.find_segments_at(path, "SEQ")).as_deref() == Some(own_seq.as_str())
4349                && objektcodes(&ctx.find_segments_at(path, "RFF"))
4350                    .iter()
4351                    .any(|code| own.contains(code))
4352        });
4353        ConditionResult::from(found)
4354    }
4355
4356    /// [180] Wenn im selben QTY im DE6063 Z46 (Kein Abschlag) vorhanden
4357    fn evaluate_180(&self, ctx: &EvaluationContext) -> ConditionResult {
4358        let qty_segs = ctx.find_segments("QTY");
4359        let found = qty_segs.iter().any(|s| {
4360            s.elements
4361                .first()
4362                .and_then(|e| e.first())
4363                .is_some_and(|v| ["Z46"].contains(&v.as_str()))
4364        });
4365        ConditionResult::from(found)
4366    }
4367
4368    /// [184] Wenn in derselben SG8 SEQ+Z18 (Daten der Messlokation) ein CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
4369    fn evaluate_184(&self, ctx: &EvaluationContext) -> ConditionResult {
4370        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4371        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4372        let found = seq_segs.iter().any(|s| {
4373            s.elements
4374                .first()
4375                .and_then(|e| e.first())
4376                .is_some_and(|v| ["Z18"].contains(&v.as_str()))
4377        });
4378        ConditionResult::from(found)
4379    }
4380
4381    /// [190] Wenn in derselben SG8 SEQ+Z60/ ZG8/ ZG9/ ZE0  (Produkt-Daten der Netzlokation) PIA+5+9991000000721:Z12 vorhanden
4382    fn evaluate_190(&self, ctx: &EvaluationContext) -> ConditionResult {
4383        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4384        // Check: SG8 with SEQ+Z60/ZG8/ZG9/ZE0 has PIA+5 with code 9991000000721
4385        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4386        let found = pia_segs.iter().any(|s| {
4387            s.elements
4388                .first()
4389                .and_then(|e| e.first())
4390                .is_some_and(|v| v == "5")
4391                && s.elements
4392                    .get(1)
4393                    .and_then(|e| e.first())
4394                    .is_some_and(|v| v == "9991000000721")
4395        });
4396        ConditionResult::from(found)
4397    }
4398
4399    /// [191] Wenn im SG4 IDE+Z01 (Identifikation einer Liste) STS+E01 (Status der Antwort der Liste) nicht vorhanden
4400    fn evaluate_191(&self, ctx: &EvaluationContext) -> ConditionResult {
4401        let sts_segs = ctx.find_segments("STS");
4402        let found = sts_segs.iter().any(|s| {
4403            s.elements
4404                .first()
4405                .and_then(|e| e.first())
4406                .is_some_and(|v| v == "E01")
4407        });
4408        ConditionResult::from(!found)
4409    }
4410
4411    /// [192] Wenn SG4 STS+7++XXX+ZW4/ZAP (Transaktionsgrund / Ergänzung: Verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
4412    fn evaluate_192(&self, ctx: &EvaluationContext) -> ConditionResult {
4413        let sts_segs = ctx.find_segments("STS");
4414        let found = sts_segs.iter().any(|s| {
4415            s.elements
4416                .first()
4417                .and_then(|e| e.first())
4418                .is_some_and(|v| v == "7")
4419                && s.elements
4420                    .get(3)
4421                    .and_then(|e| e.first())
4422                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
4423        });
4424        ConditionResult::from(found)
4425    }
4426
4427    /// [193] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E06 (Niederspannung) in dieser SG vorhanden
4428    fn evaluate_193(&self, ctx: &EvaluationContext) -> ConditionResult {
4429        let nav = match ctx.navigator {
4430            Some(n) => n,
4431            None => {
4432                let cavs = ctx.find_segments("CAV");
4433                return ConditionResult::from(cavs.iter().any(|s| {
4434                    s.elements
4435                        .first()
4436                        .and_then(|e: &Vec<String>| e.first())
4437                        .is_some_and(|v: &String| v == "E06")
4438                }));
4439            }
4440        };
4441        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4442        for i in 0..sg8_count {
4443            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4444            for j in 0..sg10_count {
4445                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4446                let has_cci_zb5 = ccis.iter().any(|s| {
4447                    s.elements
4448                        .get(2)
4449                        .and_then(|e: &Vec<String>| e.first())
4450                        .is_some_and(|v: &String| v == "ZB5")
4451                });
4452                if !has_cci_zb5 {
4453                    continue;
4454                }
4455                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4456                if cavs.iter().any(|s| {
4457                    s.elements
4458                        .first()
4459                        .and_then(|e: &Vec<String>| e.first())
4460                        .is_some_and(|v: &String| v == "E06")
4461                }) {
4462                    return ConditionResult::True;
4463                }
4464            }
4465        }
4466        ConditionResult::False
4467    }
4468
4469    /// [194] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E05 (Mittelspannung) in dieser SG vorhanden
4470    fn evaluate_194(&self, ctx: &EvaluationContext) -> ConditionResult {
4471        let cci_segs = ctx.find_segments("CCI");
4472        let has_cci = cci_segs.iter().any(|s| {
4473            s.elements
4474                .iter()
4475                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4476        });
4477        let cav_segs = ctx.find_segments("CAV");
4478        let has_cav = cav_segs.iter().any(|s| {
4479            s.elements
4480                .first()
4481                .and_then(|e| e.first())
4482                .is_some_and(|v| v == "E05")
4483        });
4484        ConditionResult::from(has_cci && has_cav)
4485    }
4486
4487    /// [195] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E04 (Hochspannung) in dieser SG vorhanden
4488    fn evaluate_195(&self, ctx: &EvaluationContext) -> ConditionResult {
4489        let cci_segs = ctx.find_segments("CCI");
4490        let has_cci = cci_segs.iter().any(|s| {
4491            s.elements
4492                .iter()
4493                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4494        });
4495        let cav_segs = ctx.find_segments("CAV");
4496        let has_cav = cav_segs.iter().any(|s| {
4497            s.elements
4498                .first()
4499                .and_then(|e| e.first())
4500                .is_some_and(|v| v == "E04")
4501        });
4502        ConditionResult::from(has_cci && has_cav)
4503    }
4504
4505    /// [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...
4506    // 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)
4507    fn evaluate_196(&self, ctx: &EvaluationContext) -> ConditionResult {
4508        // HAND-EDITED: "in einem weiteren SG8 … Nummer, Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
4509        // reaches an SG8; the message as before otherwise.
4510        if let Some(scope) = ctx.enclosing_scope("SG8") {
4511            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
4512                ctx,
4513                scope,
4514                &["Z58", "ZC9", "ZD0"],
4515                true,
4516                true,
4517            ));
4518        }
4519        let nav = match ctx.navigator {
4520            Some(n) => n,
4521            None => return ConditionResult::Unknown,
4522        };
4523        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4524        let mut entries: Vec<(usize, String, String, String, String)> = Vec::new();
4525        for i in 0..sg8_count {
4526            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4527            let seq_opt = seqs.iter().find(|s| {
4528                s.elements
4529                    .first()
4530                    .and_then(|e: &Vec<String>| e.first())
4531                    .is_some_and(|v: &String| matches!(v.as_str(), "Z58" | "ZC9" | "ZD0"))
4532            });
4533            let seq = match seq_opt {
4534                Some(s) => s,
4535                None => continue,
4536            };
4537            let seq_code = seq
4538                .elements
4539                .first()
4540                .and_then(|e: &Vec<String>| e.first())
4541                .cloned()
4542                .unwrap_or_default();
4543            let zeitraum_id = seq
4544                .elements
4545                .get(1)
4546                .and_then(|e: &Vec<String>| e.first())
4547                .cloned()
4548                .unwrap_or_default();
4549            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4550            for rff in &rffs {
4551                let elems = match rff.elements.first() {
4552                    Some(e) => e,
4553                    None => continue,
4554                };
4555                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4556                    continue;
4557                }
4558                let objektcode = elems.get(1).cloned().unwrap_or_default();
4559                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4560                if !objektcode.is_empty() {
4561                    entries.push((
4562                        i,
4563                        seq_code.clone(),
4564                        zeitraum_id.clone(),
4565                        objektcode,
4566                        fort_nr,
4567                    ));
4568                }
4569            }
4570        }
4571        for a in 0..entries.len() {
4572            for b in (a + 1)..entries.len() {
4573                if entries[a].0 != entries[b].0
4574                    && entries[a].1 == entries[b].1
4575                    && entries[a].2 == entries[b].2
4576                    && entries[a].3 == entries[b].3
4577                    && entries[a].4 == entries[b].4
4578                {
4579                    return ConditionResult::True;
4580                }
4581            }
4582        }
4583        ConditionResult::False
4584    }
4585
4586    /// [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 (...
4587    // 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)
4588    fn evaluate_197(&self, ctx: &EvaluationContext) -> ConditionResult {
4589        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (SLP/SEP) — compared within the enclosing SG4 when the scope
4590        // reaches an SG8; the message as before otherwise.
4591        if let Some(scope) = ctx.enclosing_scope("SG8") {
4592            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
4593            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
4594                sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "E02")
4595            }));
4596        }
4597        let nav = match ctx.navigator {
4598            Some(n) => n,
4599            None => return ConditionResult::Unknown,
4600        };
4601        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4602        let mut qualifying_malo_ids: Vec<String> = Vec::new();
4603        for i in 0..sg8_count {
4604            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4605            if !seqs.iter().any(|s| {
4606                s.elements
4607                    .first()
4608                    .and_then(|e: &Vec<String>| e.first())
4609                    .is_some_and(|v: &String| v == "Z01")
4610            }) {
4611                continue;
4612            }
4613            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4614            let has_prog = (0..sg10_count).any(|j| {
4615                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4616                let has_za6 = ccis.iter().any(|s| {
4617                    s.elements
4618                        .get(2)
4619                        .and_then(|e: &Vec<String>| e.first())
4620                        .is_some_and(|v: &String| v == "ZA6")
4621                });
4622                if !has_za6 {
4623                    return false;
4624                }
4625                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4626                cavs.iter().any(|s| {
4627                    s.elements
4628                        .first()
4629                        .and_then(|e: &Vec<String>| e.first())
4630                        .is_some_and(|v: &String| v == "E02")
4631                })
4632            });
4633            if !has_prog {
4634                continue;
4635            }
4636            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4637            for rff in &rffs {
4638                let elems = match rff.elements.first() {
4639                    Some(e) => e,
4640                    None => continue,
4641                };
4642                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4643                    continue;
4644                }
4645                if let Some(malo_id) = elems.get(1) {
4646                    if !malo_id.is_empty() {
4647                        qualifying_malo_ids.push(malo_id.clone());
4648                    }
4649                }
4650            }
4651        }
4652        if qualifying_malo_ids.is_empty() {
4653            return ConditionResult::False;
4654        }
4655        for i in 0..sg8_count {
4656            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4657            for rff in &rffs {
4658                let elems = match rff.elements.first() {
4659                    Some(e) => e,
4660                    None => continue,
4661                };
4662                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4663                    continue;
4664                }
4665                if let Some(malo_id) = elems.get(1) {
4666                    if qualifying_malo_ids.contains(malo_id) {
4667                        return ConditionResult::True;
4668                    }
4669                }
4670            }
4671        }
4672        ConditionResult::False
4673    }
4674
4675    /// [198] Wenn SG4 STS+7++E03(Transaktionsgrund: Wechsel) vorhanden
4676    fn evaluate_198(&self, ctx: &EvaluationContext) -> ConditionResult {
4677        let sts_segs = ctx.find_segments("STS");
4678        let found = sts_segs.iter().any(|s| {
4679            s.elements
4680                .first()
4681                .and_then(|e| e.first())
4682                .is_some_and(|v| v == "7")
4683                && s.elements
4684                    .get(2)
4685                    .and_then(|e| e.first())
4686                    .is_some_and(|v| ["E03"].contains(&v.as_str()))
4687        });
4688        ConditionResult::from(found)
4689    }
4690
4691    /// [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
4692    // 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)
4693    fn evaluate_199(&self, ctx: &EvaluationContext) -> ConditionResult {
4694        let nav = match ctx.navigator {
4695            Some(n) => n,
4696            None => {
4697                return ctx.filtered_parent_child_has_qualifier(
4698                    &["SG4", "SG8"],
4699                    "SEQ",
4700                    0,
4701                    "Z01",
4702                    "SG10",
4703                    "CCI",
4704                    2,
4705                    "ZA6",
4706                )
4707            }
4708        };
4709        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4710        for i in 0..sg8_count {
4711            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4712            if !seqs.iter().any(|s| {
4713                s.elements
4714                    .first()
4715                    .and_then(|e: &Vec<String>| e.first())
4716                    .is_some_and(|v: &String| v == "Z01")
4717            }) {
4718                continue;
4719            }
4720            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4721            for j in 0..sg10_count {
4722                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4723                if !ccis.iter().any(|s| {
4724                    s.elements
4725                        .get(2)
4726                        .and_then(|e: &Vec<String>| e.first())
4727                        .is_some_and(|v: &String| v == "ZA6")
4728                }) {
4729                    continue;
4730                }
4731                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4732                if cavs.iter().any(|s| {
4733                    s.elements
4734                        .first()
4735                        .and_then(|e: &Vec<String>| e.first())
4736                        .is_some_and(|v: &String| v == "E02")
4737                }) {
4738                    return ConditionResult::True;
4739                }
4740            }
4741        }
4742        ConditionResult::False
4743    }
4744
4745    /// [201] Wenn die Marktlokation / Tranche nicht den ganzen Bilanzierungsmonat dem gleichen Tupel aus Bilanzkreis, Zeitreihentyp, Spannungsebene und Lieferant zugeordnet ist
4746    fn evaluate_201(&self, _ctx: &EvaluationContext) -> ConditionResult {
4747        // TODO: implement
4748        ConditionResult::Unknown
4749    }
4750
4751    /// [202] Wenn SG8 SEQ+Z78 RFF+Z39 (Keine standardisierte Lokationsbündelstruktur vorhanden), mit identischer Zeitraum-ID im DE1050 wie im DE3224 dieses Segments
4752    // 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)
4753    fn evaluate_202(&self, ctx: &EvaluationContext) -> ConditionResult {
4754        use std::collections::HashSet;
4755        // Collect Zeitraum-IDs from all SG5 LOC segments (DE3224 at elements[1][3])
4756        let loc_zeitraum_ids: HashSet<String> = ctx
4757            .find_segments("LOC")
4758            .into_iter()
4759            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
4760            .filter(|v| !v.is_empty())
4761            .collect();
4762        if loc_zeitraum_ids.is_empty() {
4763            return ConditionResult::False;
4764        }
4765        // Check if any SEQ+Z78 (SG8 Lokationsbündelstruktur) has DE1050 matching a LOC DE3224
4766        let matching_seq = ctx
4767            .find_segments_with_qualifier("SEQ", 0, "Z78")
4768            .into_iter()
4769            .any(|s| {
4770                s.elements
4771                    .get(1)
4772                    .and_then(|e: &Vec<String>| e.first())
4773                    .is_some_and(|v: &String| {
4774                        !v.is_empty() && loc_zeitraum_ids.contains(v.as_str())
4775                    })
4776            });
4777        if !matching_seq {
4778            return ConditionResult::False;
4779        }
4780        // Verify RFF+Z39 also exists (co-occurrence in same SG8 not verified without navigator)
4781        ctx.has_qualifier("RFF", 0, "Z39")
4782    }
4783
4784    /// [203] Wenn STS+7++E06 / Z39 / ZC6 / ZC7/ ZT6/ ZT7 vorhanden
4785    fn evaluate_203(&self, ctx: &EvaluationContext) -> ConditionResult {
4786        let sts_segs = ctx.find_segments("STS");
4787        let found = sts_segs.iter().any(|s| {
4788            s.elements
4789                .first()
4790                .and_then(|e| e.first())
4791                .is_some_and(|v| v == "7")
4792                && s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
4793                    ["E06", "Z39", "ZC6", "ZC7", "ZT6", "ZT7"].contains(&v.as_str())
4794                })
4795        });
4796        ConditionResult::from(found)
4797    }
4798
4799    /// [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...
4800    // 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)
4801    fn evaluate_204(&self, ctx: &EvaluationContext) -> ConditionResult {
4802        // True if any SG8 with Zähleinrichtungsdaten (SEQ+Z03/ZA3/ZA4) lacks RFF+Z14 (Smartmeter-Gateway)
4803        for qual in &["Z03", "ZA3", "ZA4"] {
4804            if matches!(
4805                ctx.any_group_has_qualifier_without(
4806                    "SEQ",
4807                    0,
4808                    qual,
4809                    "RFF",
4810                    0,
4811                    "Z14",
4812                    &["SG4", "SG8"]
4813                ),
4814                ConditionResult::True
4815            ) {
4816                return ConditionResult::True;
4817            }
4818        }
4819        ConditionResult::False
4820    }
4821
4822    /// [205] Wenn SG5 LOC+Z19 (Steuerbare Ressource) vorhanden
4823    fn evaluate_205(&self, ctx: &EvaluationContext) -> ConditionResult {
4824        let loc_segs = ctx.find_segments("LOC");
4825        let found = loc_segs.iter().any(|s| {
4826            s.elements
4827                .first()
4828                .and_then(|e| e.first())
4829                .is_some_and(|v| v == "Z19")
4830        });
4831        ConditionResult::from(found)
4832    }
4833
4834    /// [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
4835    fn evaluate_206(&self, ctx: &EvaluationContext) -> ConditionResult {
4836        let sts_segs = ctx.find_segments("STS");
4837        let found = sts_segs.iter().any(|s| {
4838            s.elements
4839                .first()
4840                .and_then(|e| e.first())
4841                .is_some_and(|v| v == "7")
4842                && s.elements
4843                    .get(2)
4844                    .and_then(|e| e.first())
4845                    .is_some_and(|v| ["ZG5"].contains(&v.as_str()))
4846        });
4847        ConditionResult::from(!found)
4848    }
4849
4850    /// [209] Wenn im selben Segment im DE2379 der Code 303 vorhanden ist
4851    fn evaluate_209(&self, ctx: &EvaluationContext) -> ConditionResult {
4852        ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "303"))
4853    }
4854
4855    /// [210] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
4856    fn evaluate_210(&self, ctx: &EvaluationContext) -> ConditionResult {
4857        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4858        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4859        let found = cci_segs.iter().any(|s| {
4860            s.elements
4861                .get(2)
4862                .and_then(|e| e.first())
4863                .is_some_and(|v| v == "ZA6")
4864        });
4865        ConditionResult::from(found)
4866    }
4867
4868    /// [212] Wenn im selben SG12 NAD DE3124 nicht vorhanden
4869    fn evaluate_212(&self, ctx: &EvaluationContext) -> ConditionResult {
4870        let nad_segs = ctx.find_segments("NAD");
4871        let found = nad_segs.iter().any(|s| {
4872            s.elements
4873                .get(3)
4874                .and_then(|e| e.first())
4875                .is_some_and(|v| !v.is_empty())
4876        });
4877        ConditionResult::from(!found)
4878    }
4879
4880    /// [213] Wenn SG12 NAD+Z09 (Kunde des Lieferanten) vorhanden
4881    fn evaluate_213(&self, ctx: &EvaluationContext) -> ConditionResult {
4882        let nad_segs = ctx.find_segments("NAD");
4883        let found = nad_segs.iter().any(|s| {
4884            s.elements
4885                .first()
4886                .and_then(|e| e.first())
4887                .is_some_and(|v| v == "Z09")
4888        });
4889        ConditionResult::from(found)
4890    }
4891
4892    /// [215] Wenn in derselben SG8 (Zähleinrichtungsdaten) SG10 CCI+++E13 CAV+MME (Zählertyp: mME) vorhanden
4893    fn evaluate_215(&self, ctx: &EvaluationContext) -> ConditionResult {
4894        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8 (its
4895        // SG10s included). Both segments are looked for anywhere in that SG8,
4896        // not within one SG10.
4897        let has_cci_e13 = ctx.scoped_find_segments_in("SG8", "CCI").iter().any(|s| {
4898            s.elements
4899                .get(2)
4900                .and_then(|e| e.first())
4901                .is_some_and(|v| v == "E13")
4902        });
4903        let has_cav_mme = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
4904            s.elements
4905                .first()
4906                .and_then(|e| e.first())
4907                .is_some_and(|v| v == "MME")
4908        });
4909        ConditionResult::from(has_cci_e13 && has_cav_mme)
4910    }
4911
4912    /// [216] Wenn CCI+++Z88 (Netznutzung) CAV+Z74:::Z08 (Netznutzungsvertrag: Direkter Vertrag zwischen Kunden und NB) vorhanden
4913    fn evaluate_216(&self, ctx: &EvaluationContext) -> ConditionResult {
4914        let cci_segs = ctx.find_segments("CCI");
4915        let found = cci_segs.iter().any(|s| {
4916            s.elements
4917                .get(2)
4918                .and_then(|e| e.first())
4919                .is_some_and(|v| v == "Z88")
4920        });
4921        ConditionResult::from(found)
4922    }
4923
4924    /// [217] Wenn SG6 RFF+Z49 (Verwendungszeitraum der Daten: \"Gültige Daten\") vorhanden
4925    fn evaluate_217(&self, ctx: &EvaluationContext) -> ConditionResult {
4926        let rff_segs = ctx.find_segments("RFF");
4927        let found = rff_segs.iter().any(|s| {
4928            s.elements
4929                .first()
4930                .and_then(|e| e.first())
4931                .is_some_and(|v| v == "Z49")
4932        });
4933        ConditionResult::from(found)
4934    }
4935
4936    /// [219] Wenn SG12 NAD+Z65 (Informativer Kunde des Lieferanten) vorhanden
4937    fn evaluate_219(&self, ctx: &EvaluationContext) -> ConditionResult {
4938        let nad_segs = ctx.find_segments("NAD");
4939        let found = nad_segs.iter().any(|s| {
4940            s.elements
4941                .first()
4942                .and_then(|e| e.first())
4943                .is_some_and(|v| v == "Z65")
4944        });
4945        ConditionResult::from(found)
4946    }
4947
4948    /// [220] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) in dieser SG8 vorhanden
4949    fn evaluate_220(&self, ctx: &EvaluationContext) -> ConditionResult {
4950        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4951        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4952        let has_seq = seq_segs.iter().any(|s| {
4953            s.elements
4954                .first()
4955                .and_then(|e| e.first())
4956                .is_some_and(|v| ["Z01", "Z98"].contains(&v.as_str()))
4957        });
4958        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4959        let has_cci = cci_segs.iter().any(|s| {
4960            s.elements
4961                .first()
4962                .and_then(|e| e.first())
4963                .is_some_and(|v| v == "Z30")
4964                && s.elements
4965                    .get(2)
4966                    .and_then(|e| e.first())
4967                    .is_some_and(|v| v == "Z06")
4968        });
4969        ConditionResult::from(has_seq && has_cci)
4970    }
4971
4972    /// [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)
4973    fn evaluate_221(&self, ctx: &EvaluationContext) -> ConditionResult {
4974        // Check: no SG8 with SEQ+Z15 has RFF+Z18 referencing same Marktlokation
4975        let has_seq_z15 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z15").is_empty();
4976        if !has_seq_z15 {
4977            return ConditionResult::True;
4978        }
4979        // Approximate: SEQ+Z15 exists, check if it references via RFF+Z18
4980        ctx.any_group_has_qualifier_without("SEQ", 0, "Z15", "RFF", 0, "Z18", &["SG4", "SG8"])
4981    }
4982
4983    /// [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...
4984    // HAND-EDITED (mako-twin, 55043): the generated version only checked that
4985    // some RFF+Z18 names some LOC+Z16 and dropped the Erzeugung clause, so it
4986    // held for every message with a Marktlokation. The scope never narrows to
4987    // one SG8 (only top-level groups are scoped), so "this SG8" is any SG8
4988    // other than SEQ+Z01, as in the neighbouring conditions.
4989    fn evaluate_223(&self, ctx: &EvaluationContext) -> ConditionResult {
4990        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (Erzeugung) — compared within the enclosing SG4 when the scope
4991        // reaches an SG8; the message as before otherwise.
4992        if let Some(scope) = ctx.enclosing_scope("SG8") {
4993            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
4994            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
4995                sg4_instances::has_cci(s, "Z30", "Z06")
4996            }));
4997        }
4998        let Some(blocks) = sg8_blocks(ctx) else {
4999            return ConditionResult::Unknown;
5000        };
5001        let loc_ids: Vec<String> = ctx
5002            .find_segments_with_qualifier("LOC", 0, "Z16")
5003            .iter()
5004            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
5005            .collect();
5006        let malo_refs = |b: &Sg8Block| -> Vec<String> {
5007            b.malo_refs
5008                .iter()
5009                .filter(|id| loc_ids.contains(id))
5010                .cloned()
5011                .collect()
5012        };
5013        // Marktlokationen whose SEQ+Z01 carries SG10 CCI+Z30++Z06 (Erzeugung).
5014        let erzeugung: Vec<String> = blocks
5015            .iter()
5016            .filter(|b| b.seq == "Z01")
5017            .filter(|b| {
5018                b.sg10.iter().any(|(ccis, _)| {
5019                    ccis.iter().any(|c| {
5020                        component(c, 0, 0) == Some("Z30") && component(c, 2, 0) == Some("Z06")
5021                    })
5022                })
5023            })
5024            .flat_map(|b| malo_refs(b))
5025            .collect();
5026        let referenced = blocks
5027            .iter()
5028            .filter(|b| b.seq != "Z01")
5029            .any(|b| malo_refs(b).iter().any(|id| erzeugung.contains(id)));
5030        ConditionResult::from(referenced)
5031    }
5032
5033    /// [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)
5034    fn evaluate_224(&self, ctx: &EvaluationContext) -> ConditionResult {
5035        // Check: no SG8 with SEQ+ZE7 has RFF+Z18 referencing same Marktlokation
5036        let has_seq_ze7 = !ctx.find_segments_with_qualifier("SEQ", 0, "ZE7").is_empty();
5037        if !has_seq_ze7 {
5038            return ConditionResult::True;
5039        }
5040        ctx.any_group_has_qualifier_without("SEQ", 0, "ZE7", "RFF", 0, "Z18", &["SG4", "SG8"])
5041    }
5042
5043    /// [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
5044    fn evaluate_227(&self, ctx: &EvaluationContext) -> ConditionResult {
5045        // Check: in SG8 with SEQ+Z98, SG10 has CCI+++ZA6 and CAV+E14 or ETK
5046        let r1 = ctx.filtered_parent_child_has_qualifier(
5047            &["SG4", "SG8"],
5048            "SEQ",
5049            0,
5050            "Z98",
5051            "SG10",
5052            "CAV",
5053            0,
5054            "E14",
5055        );
5056        if r1 == ConditionResult::True {
5057            return ConditionResult::True;
5058        }
5059        ctx.filtered_parent_child_has_qualifier(
5060            &["SG4", "SG8"],
5061            "SEQ",
5062            0,
5063            "Z98",
5064            "SG10",
5065            "CAV",
5066            0,
5067            "ETK",
5068        )
5069    }
5070
5071    /// [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)...
5072    // 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)
5073    fn evaluate_229(&self, ctx: &EvaluationContext) -> ConditionResult {
5074        // HAND-EDITED: "in einem weiteren SG8 … Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
5075        // reaches an SG8; the message as before otherwise.
5076        if let Some(scope) = ctx.enclosing_scope("SG8") {
5077            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
5078                ctx,
5079                scope,
5080                &["Z58", "ZC9", "ZD0", "ZD6"],
5081                false,
5082                true,
5083            ));
5084        }
5085        use std::collections::HashSet;
5086        let target_quals: HashSet<&str> = ["Z58", "ZC9", "ZD0", "ZD6"].iter().cloned().collect();
5087        // Collect per-SG8-instance: SEQ qualifier and Zeitraum-ID
5088        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
5089        let seq_zeitraums = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
5090        // Collect per-SG8-instance: RFF qualifier and DE1154 value
5091        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
5092        let rff_codes = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
5093        // Detect duplicate (seq_qual, zeitraum_id, objektcode) combinations across SG8 instances
5094        let mut seen: HashSet<(String, String, String)> = HashSet::new();
5095        for (idx, seq_qual) in &seq_quals {
5096            if seq_qual.is_empty() || !target_quals.contains(seq_qual.as_str()) {
5097                continue;
5098            }
5099            let zeitraum_id = seq_zeitraums
5100                .iter()
5101                .find(|(i, _)| i == idx)
5102                .map(|(_, v)| v.as_str())
5103                .unwrap_or("");
5104            // Find RFF+Z33 in this SG8 instance (correlation by instance index)
5105            for (ridx, rff_qual) in rff_quals.iter() {
5106                if ridx != idx || rff_qual != "Z33" {
5107                    continue;
5108                }
5109                if let Some((_, obj_code)) = rff_codes.iter().find(|(i, _)| i == ridx) {
5110                    if obj_code.is_empty() {
5111                        continue;
5112                    }
5113                    let key = (seq_qual.clone(), zeitraum_id.to_string(), obj_code.clone());
5114                    if seen.contains(&key) {
5115                        return ConditionResult::True;
5116                    }
5117                    seen.insert(key);
5118                }
5119            }
5120        }
5121        ConditionResult::False
5122    }
5123
5124    /// [232] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z52 (iMS) vorhanden
5125    fn evaluate_232(&self, ctx: &EvaluationContext) -> ConditionResult {
5126        let cci_segs = ctx.find_segments("CCI");
5127        let found = cci_segs.iter().any(|s| {
5128            s.elements
5129                .get(2)
5130                .and_then(|e| e.first())
5131                .is_some_and(|v| v == "Z83")
5132        });
5133        ConditionResult::from(found)
5134    }
5135
5136    /// [233] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle NB
5137    fn evaluate_233(&self, ctx: &EvaluationContext) -> ConditionResult {
5138        ctx.external.evaluate("recipient_role_check")
5139    }
5140
5141    /// [234] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z53 (kME/ mME) vorhanden
5142    fn evaluate_234(&self, ctx: &EvaluationContext) -> ConditionResult {
5143        let cci_segs = ctx.find_segments("CCI");
5144        let found = cci_segs.iter().any(|s| {
5145            s.elements
5146                .get(2)
5147                .and_then(|e| e.first())
5148                .is_some_and(|v| v == "Z83")
5149        });
5150        ConditionResult::from(found)
5151    }
5152
5153    /// [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
5154    fn evaluate_237(&self, ctx: &EvaluationContext) -> ConditionResult {
5155        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5156        // Check: PIA+Z02 with code starting with 1-08 and containing AGS
5157        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5158        let found = pia_segs.iter().any(|s| {
5159            s.elements
5160                .first()
5161                .and_then(|e| e.first())
5162                .is_some_and(|v| v == "Z02")
5163                && s.elements
5164                    .get(1)
5165                    .and_then(|e| e.first())
5166                    .is_some_and(|v| v.starts_with("1-08") && v.contains("AGS"))
5167        });
5168        ConditionResult::from(found)
5169    }
5170
5171    /// [238] Wenn SG4 IDE+24 (Vorgang) nicht vorhanden
5172    fn evaluate_238(&self, ctx: &EvaluationContext) -> ConditionResult {
5173        let ide_segs = ctx.find_segments("IDE");
5174        let found = ide_segs.iter().any(|s| {
5175            s.elements
5176                .first()
5177                .and_then(|e| e.first())
5178                .is_some_and(|v| v == "24")
5179        });
5180        ConditionResult::from(!found)
5181    }
5182
5183    /// [239] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+Z65 (Wärme/Kälte) vorhanden
5184    fn evaluate_239(&self, ctx: &EvaluationContext) -> ConditionResult {
5185        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
5186        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
5187        let found = cci_segs.iter().any(|s| {
5188            s.elements
5189                .first()
5190                .and_then(|e| e.first())
5191                .is_some_and(|v| v == "Z17")
5192        });
5193        ConditionResult::from(found)
5194    }
5195
5196    /// [240] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) CCI+Z22++Z91 (Veräußerungsform der erzeugenden Marktlokation:: Marktprämie) vorhanden
5197    fn evaluate_240(&self, ctx: &EvaluationContext) -> ConditionResult {
5198        let seq_segs = ctx.find_segments("SEQ");
5199        let found = seq_segs.iter().any(|s| {
5200            s.elements
5201                .first()
5202                .and_then(|e| e.first())
5203                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
5204        });
5205        ConditionResult::from(found)
5206    }
5207
5208    /// [241] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle MSB
5209    fn evaluate_241(&self, ctx: &EvaluationContext) -> ConditionResult {
5210        ctx.external.evaluate("recipient_role_check")
5211    }
5212
5213    /// [243] Wenn SG10 CCI+6++ZA8 (Aggreg.verantw. NB) in dieser SG8 vorhanden
5214    fn evaluate_243(&self, ctx: &EvaluationContext) -> ConditionResult {
5215        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5216        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5217        let found = cci_segs.iter().any(|s| {
5218            s.elements
5219                .first()
5220                .and_then(|e| e.first())
5221                .is_some_and(|v| v == "6")
5222                && s.elements
5223                    .get(2)
5224                    .and_then(|e| e.first())
5225                    .is_some_and(|v| v == "ZA8")
5226        });
5227        ConditionResult::from(found)
5228    }
5229
5230    /// [244] Wenn SG10 CCI+6++ZA9 (Aggreg.verantw. ÜNB) in dieser SG8 vorhanden
5231    fn evaluate_244(&self, ctx: &EvaluationContext) -> ConditionResult {
5232        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5233        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5234        let found = cci_segs.iter().any(|s| {
5235            s.elements
5236                .first()
5237                .and_then(|e| e.first())
5238                .is_some_and(|v| v == "6")
5239                && s.elements
5240                    .get(2)
5241                    .and_then(|e| e.first())
5242                    .is_some_and(|v| v == "ZA9")
5243        });
5244        ConditionResult::from(found)
5245    }
5246
5247    /// [248] Wenn die Inbetriebnahme der verbrauchenden Marktlokation innerhalb der letzten drei Jahre war und die Energie der Marktlokation mit iMS ausgestatteten Messlokationen erfolgt
5248    fn evaluate_248(&self, _ctx: &EvaluationContext) -> ConditionResult {
5249        // TODO: implement
5250        ConditionResult::Unknown
5251    }
5252
5253    /// [249] Innerhalb eines SG4 IDE müssen alle DE1131 der SG4 STS+E01 (Status der Antwort) den identischen Wert enthalten
5254    fn evaluate_249(&self, ctx: &EvaluationContext) -> ConditionResult {
5255        // Check: all STS+E01 DE1131 values are identical
5256        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
5257        if sts_segs.len() <= 1 {
5258            return ConditionResult::True;
5259        }
5260        let first_val = sts_segs[0].elements.first().and_then(|e| e.get(1));
5261        let all_same = sts_segs
5262            .iter()
5263            .all(|s| s.elements.first().and_then(|e| e.get(1)) == first_val);
5264        ConditionResult::from(all_same)
5265    }
5266
5267    /// [251] Wenn MP-ID in SG2 NAD+MS (Nachrichtensender) in der Rolle BIKO
5268    fn evaluate_251(&self, ctx: &EvaluationContext) -> ConditionResult {
5269        ctx.external.evaluate("sender_role_check")
5270    }
5271
5272    /// [252] Wenn DE0068 vorhanden
5273    fn evaluate_252(&self, ctx: &EvaluationContext) -> ConditionResult {
5274        let unb_segs = ctx.find_segments("UNB");
5275        let found = unb_segs.iter().any(|s| {
5276            s.elements
5277                .get(4)
5278                .and_then(|e| e.first())
5279                .is_some_and(|v| !v.is_empty())
5280        });
5281        ConditionResult::from(found)
5282    }
5283
5284    /// [254] Es ist der Code \"AUA\" einzutragen
5285    fn evaluate_254(&self, _ctx: &EvaluationContext) -> ConditionResult {
5286        // Check: the code "AUA" is present
5287        ConditionResult::True // AUA is always the required code when this condition applies
5288    }
5289
5290    /// [255] Wenn Marktlokation am Redispatch 2.0 teilnimmt
5291    fn evaluate_255(&self, _ctx: &EvaluationContext) -> ConditionResult {
5292        // TODO: implement
5293        ConditionResult::Unknown
5294    }
5295
5296    /// [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
5297    fn evaluate_256(&self, ctx: &EvaluationContext) -> ConditionResult {
5298        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5299        // Check: PIA+5 has OBIS code matching 1-?:1.8.? or 1-?:2.8.? pattern
5300        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5301        let found = pia_segs.iter().any(|s| {
5302            s.elements
5303                .first()
5304                .and_then(|e| e.first())
5305                .is_some_and(|v| v == "5")
5306                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5307                    (v.contains(":1.8.") || v.contains(":2.8.")) && v.starts_with("1-")
5308                })
5309        });
5310        ConditionResult::from(found)
5311    }
5312
5313    /// [257] Wenn in derselben SG8 SEQ (OBIS-Daten der Marktlokation) das PIA+5+1-b?:1.9.e vorhanden
5314    fn evaluate_257(&self, ctx: &EvaluationContext) -> ConditionResult {
5315        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5316        // Check: PIA+5 has OBIS code matching 1-?:1.9.? pattern
5317        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5318        let found = pia_segs.iter().any(|s| {
5319            s.elements
5320                .first()
5321                .and_then(|e| e.first())
5322                .is_some_and(|v| v == "5")
5323                && s.elements
5324                    .get(1)
5325                    .and_then(|e| e.first())
5326                    .is_some_and(|v| v.contains(":1.9.") && v.starts_with("1-"))
5327        });
5328        ConditionResult::from(found)
5329    }
5330
5331    /// [259] Wenn in diesem PIA der Code im DE7140 mit 1-01, 1-03, 1-05 enthalten ist
5332    fn evaluate_259(&self, ctx: &EvaluationContext) -> ConditionResult {
5333        let pia_segs = ctx.find_segments("PIA");
5334        let found = pia_segs.iter().any(|s| {
5335            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5336                let prefixes = ["1-01", "1-03", "1-05"];
5337                prefixes.iter().any(|p| v.starts_with(p))
5338            })
5339        });
5340        ConditionResult::from(found)
5341    }
5342
5343    /// [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
5344    fn evaluate_261(&self, ctx: &EvaluationContext) -> ConditionResult {
5345        let pia_segs = ctx.find_segments("PIA");
5346        let found = pia_segs.iter().any(|s| {
5347            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5348                let prefixes = [
5349                    "1-07-1", "2", "1-10-1", "2", "4", "5", "6", "7", "8", "9", "1-11-1",
5350                ];
5351                prefixes.iter().any(|p| v.starts_with(p))
5352            })
5353        });
5354        ConditionResult::from(found)
5355    }
5356
5357    /// [262] Wenn Produkt an der Marktlokation vorhanden ist
5358    fn evaluate_262(&self, _ctx: &EvaluationContext) -> ConditionResult {
5359        // TODO: implement
5360        ConditionResult::Unknown
5361    }
5362
5363    /// [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
5364    // HAND-EDITED (mako-twin, 55043): the generated version looked for
5365    // CAV+E14/ETK in any SG10 under SEQ+Z01/Z98, ignoring CCI+++ZA6; the AHB
5366    // asks for CAV+Z36 (TEP m. Referenzmessung) in the SG10 CCI+++ZA6.
5367    fn evaluate_265(&self, ctx: &EvaluationContext) -> ConditionResult {
5368        let Some(blocks) = sg8_blocks(ctx) else {
5369            return ConditionResult::Unknown;
5370        };
5371        let found = blocks
5372            .iter()
5373            .filter(|b| b.seq == "Z01" || b.seq == "Z98")
5374            .any(|b| {
5375                b.sg10.iter().any(|(ccis, cavs)| {
5376                    ccis.iter().any(|c| component(c, 2, 0) == Some("ZA6"))
5377                        && cavs.iter().any(|c| component(c, 0, 0) == Some("Z36"))
5378                })
5379            });
5380        ConditionResult::from(found)
5381    }
5382
5383    /// [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...
5384    // 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)
5385    fn evaluate_266(&self, ctx: &EvaluationContext) -> ConditionResult {
5386        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8" — compared within the enclosing SG4 when the scope
5387        // reaches an SG8; the message as before otherwise.
5388        if let Some(scope) = ctx.enclosing_scope("SG8") {
5389            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
5390            return ConditionResult::from(z01s.iter().any(|p| {
5391                sg4_instances::sg10_at(ctx, p, |s| {
5392                    sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "Z36")
5393                })
5394            }));
5395        }
5396        let nav = match ctx.navigator {
5397            Some(n) => n,
5398            None => {
5399                // Fallback: check any SG8+SEQ+Z01 child SG10 has CAV+Z36
5400                return ctx.filtered_parent_child_has_qualifier(
5401                    &["SG4", "SG8"],
5402                    "SEQ",
5403                    0,
5404                    "Z01",
5405                    "SG10",
5406                    "CAV",
5407                    0,
5408                    "Z36",
5409                );
5410            }
5411        };
5412        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5413        for i in 0..sg8_count {
5414            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
5415            for j in 0..sg10_count {
5416                // CCI+++ZA6 means elements[2][0] == "ZA6"
5417                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
5418                let has_cci_za6 = ccis.iter().any(|s| {
5419                    s.elements
5420                        .get(2)
5421                        .and_then(|e: &Vec<String>| e.first())
5422                        .is_some_and(|v: &String| v == "ZA6")
5423                });
5424                if has_cci_za6 {
5425                    let cavs =
5426                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
5427                    if cavs.iter().any(|s| {
5428                        s.elements
5429                            .first()
5430                            .and_then(|e: &Vec<String>| e.first())
5431                            .is_some_and(|v: &String| v == "Z36")
5432                    }) {
5433                        return ConditionResult::True;
5434                    }
5435                }
5436            }
5437        }
5438        ConditionResult::False
5439    }
5440
5441    /// [267] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
5442    fn evaluate_267(&self, ctx: &EvaluationContext) -> ConditionResult {
5443        let seq_segs = ctx.find_segments("SEQ");
5444        let found = seq_segs.iter().any(|s| {
5445            s.elements
5446                .first()
5447                .and_then(|e| e.first())
5448                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
5449        });
5450        ConditionResult::from(!found)
5451    }
5452
5453    /// [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
5454    fn evaluate_268(&self, _ctx: &EvaluationContext) -> ConditionResult {
5455        // Most European countries have postal codes — default to true
5456        ConditionResult::True
5457    }
5458
5459    /// [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
5460    fn evaluate_269(&self, _ctx: &EvaluationContext) -> ConditionResult {
5461        // TODO: implement
5462        ConditionResult::Unknown
5463    }
5464
5465    /// [270] Wenn in diesem PIA der Code im DE7140 mit 1-02, 1-04, 1-06, 1-09 beginnt
5466    fn evaluate_270(&self, ctx: &EvaluationContext) -> ConditionResult {
5467        let pia_segs = ctx.find_segments("PIA");
5468        let found = pia_segs.iter().any(|s| {
5469            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5470                let prefixes = ["1-02", "1-04", "1-06", "1-09"];
5471                prefixes.iter().any(|p| v.starts_with(p))
5472            })
5473        });
5474        ConditionResult::from(found)
5475    }
5476
5477    /// [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 ...
5478    fn evaluate_273(&self, ctx: &EvaluationContext) -> ConditionResult {
5479        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5480        // Check: PIA+5 has OBIS code matching energy quantity patterns in SG8 SEQ+Z20
5481        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5482        let found = pia_segs.iter().any(|s| {
5483            s.elements
5484                .first()
5485                .and_then(|e| e.first())
5486                .is_some_and(|v| v == "5")
5487                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5488                    v.starts_with("1-")
5489                        && (v.contains(":1.8.") || v.contains(":2.8.") || v.contains(":1.29."))
5490                })
5491        });
5492        ConditionResult::from(found)
5493    }
5494
5495    /// [279] Wenn STS+7++xxx+ZW4 / ZAP (Transaktionsgrundergänzung  verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
5496    fn evaluate_279(&self, ctx: &EvaluationContext) -> ConditionResult {
5497        let sts_segs = ctx.find_segments("STS");
5498        let found = sts_segs.iter().any(|s| {
5499            s.elements
5500                .first()
5501                .and_then(|e| e.first())
5502                .is_some_and(|v| v == "7")
5503                && s.elements
5504                    .get(3)
5505                    .and_then(|e| e.first())
5506                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
5507        });
5508        ConditionResult::from(found)
5509    }
5510
5511    /// [280] Nur MP-ID aus Sparte Strom
5512    fn evaluate_280(&self, ctx: &EvaluationContext) -> ConditionResult {
5513        // Check: Marktpartner-ID is from Sparte Strom
5514        // Approximate: check NAD+MS C082 for Strom MP-ID format
5515        ctx.external.evaluate("sector_check")
5516    }
5517
5518    /// [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
5519    fn evaluate_282(&self, ctx: &EvaluationContext) -> ConditionResult {
5520        // Check: PIA DE7140 has one of the specific codes
5521        let pia_segs = ctx.find_segments("PIA");
5522        let found = pia_segs.iter().any(|s| {
5523            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5524                v.starts_with("1-01-6-005")
5525                    || v.starts_with("1-01-9-001")
5526                    || v.starts_with("1-01-9-002")
5527                    || v.starts_with("1-07-3-001")
5528                    || v.starts_with("1-08-1-001")
5529                    || v.starts_with("1-08-3-001")
5530                    || v.starts_with("1-08-4-001")
5531                    || v.starts_with("1-08-4-002")
5532                    || v.starts_with("1-08-4-003")
5533                    || v.starts_with("1-08-5")
5534            })
5535        });
5536        ConditionResult::from(found)
5537    }
5538
5539    /// [284] Erlaubte Codes aus der Codeliste der Gruppenartikel- und Artikel-ID sind in der Spalte UTILMD/Codeverwendung mit X gekennzeichnet
5540    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
5541    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
5542    // Regenerating this function should preserve the shape, but re-verify
5543    // against the committed body if generate-conditions is run.
5544    fn evaluate_284(&self, ctx: &EvaluationContext) -> ConditionResult {
5545        // [284] Erlaubte Codes aus der Codeliste der Gruppenartikel-
5546        // und Artikel-ID mit X in der Spalte UTILMD/Codeverwendung.
5547        //
5548        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
5549        // narrows to the enclosing SG8 instance; otherwise falls back to
5550        // message-wide find_segments. Matches the AHB wording
5551        // "in derselben SG8 im PIA DE7140 ein Code...".
5552        use crate::eval::CodelistColumn;
5553        let any_match = ctx.scoped_find_segments("PIA").iter().any(|s| {
5554            let is_z02 = s
5555                .elements
5556                .first()
5557                .and_then(|e| e.first())
5558                .is_some_and(|v| v == "Z02");
5559            if !is_z02 {
5560                return false;
5561            }
5562            let code = match s.elements.get(1).and_then(|e| e.first()) {
5563                Some(c) if !c.is_empty() => c,
5564                _ => return false,
5565            };
5566            ctx.codelist
5567                .flag(code, CodelistColumn::UtilmdCodeverwendung)
5568                .unwrap_or(false)
5569        });
5570        ConditionResult::from(any_match)
5571    }
5572
5573    /// [285] Wenn in diesem PIA der Code im DE7140 mit 1-08-3-AGS angegeben ist
5574    fn evaluate_285(&self, ctx: &EvaluationContext) -> ConditionResult {
5575        // Check: PIA DE7140 starts with 1-08-3-AGS
5576        let pia_segs = ctx.find_segments("PIA");
5577        let found = pia_segs.iter().any(|s| {
5578            s.elements
5579                .get(1)
5580                .and_then(|e| e.first())
5581                .is_some_and(|v| v.contains("1-08-3") && v.contains("AGS"))
5582        });
5583        ConditionResult::from(found)
5584    }
5585
5586    /// [286] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/2/4/5-AGS-KG
5587    fn evaluate_286(&self, ctx: &EvaluationContext) -> ConditionResult {
5588        // Check: PIA DE7140 matches 1-08-1/2/4/5-AGS-KG pattern
5589        let pia_segs = ctx.find_segments("PIA");
5590        let found = pia_segs.iter().any(|s| {
5591            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5592                (v.starts_with("1-08-1")
5593                    || v.starts_with("1-08-2")
5594                    || v.starts_with("1-08-4")
5595                    || v.starts_with("1-08-5"))
5596                    && v.contains("AGS")
5597                    && v.contains("KG")
5598            })
5599        });
5600        ConditionResult::from(found)
5601    }
5602
5603    /// [287] Erlaubte Codes aus der PRICAT BGM+ Z70 (Preisblatt Netznutzung) des verantwortlichen NB
5604    fn evaluate_287(&self, _ctx: &EvaluationContext) -> ConditionResult {
5605        // TODO: implement
5606        ConditionResult::Unknown
5607    }
5608
5609    /// [288] Wenn in derselben SG8 (OBIS-Daten der Marktlokation) das PIA+5+1-b?:2.9.e vorhanden
5610    fn evaluate_288(&self, ctx: &EvaluationContext) -> ConditionResult {
5611        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5612        // Check: PIA+5 has OBIS code matching 1-?:2.9.? pattern
5613        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5614        let found = pia_segs.iter().any(|s| {
5615            s.elements
5616                .first()
5617                .and_then(|e| e.first())
5618                .is_some_and(|v| v == "5")
5619                && s.elements
5620                    .get(1)
5621                    .and_then(|e| e.first())
5622                    .is_some_and(|v| v.contains(":2.9.") && v.starts_with("1-"))
5623        });
5624        ConditionResult::from(found)
5625    }
5626
5627    /// [291] Wenn in derselben SG8 (Zuordnung Lokation zum Objektcode des Lokationsbündels) das RFF+Z37 vorhanden
5628    fn evaluate_291(&self, ctx: &EvaluationContext) -> ConditionResult {
5629        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5630        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
5631        let found = rff_segs.iter().any(|s| {
5632            s.elements
5633                .first()
5634                .and_then(|e| e.first())
5635                .is_some_and(|v| v == "Z37")
5636        });
5637        ConditionResult::from(found)
5638    }
5639
5640    /// [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...
5641    fn evaluate_292(&self, ctx: &EvaluationContext) -> ConditionResult {
5642        ctx.external.evaluate("code_list_membership_check")
5643    }
5644
5645    /// [293] Wenn SG5 LOC+Z22 (ruhende Marktlokation) vorhanden
5646    fn evaluate_293(&self, ctx: &EvaluationContext) -> ConditionResult {
5647        let loc_segs = ctx.find_segments("LOC");
5648        let found = loc_segs.iter().any(|s| {
5649            s.elements
5650                .first()
5651                .and_then(|e| e.first())
5652                .is_some_and(|v| v == "Z22")
5653        });
5654        ConditionResult::from(found)
5655    }
5656
5657    /// [294] Wenn SG5 LOC+Z18 (Netzlokation) vorhanden
5658    fn evaluate_294(&self, ctx: &EvaluationContext) -> ConditionResult {
5659        let loc_segs = ctx.find_segments("LOC");
5660        let found = loc_segs.iter().any(|s| {
5661            s.elements
5662                .first()
5663                .and_then(|e| e.first())
5664                .is_some_and(|v| v == "Z18")
5665        });
5666        ConditionResult::from(found)
5667    }
5668
5669    /// [295] Wenn SG5 LOC+Z15 (MaBiS-Zählpunkt) nicht vorhanden
5670    fn evaluate_295(&self, ctx: &EvaluationContext) -> ConditionResult {
5671        let loc_segs = ctx.find_segments("LOC");
5672        let found = loc_segs.iter().any(|s| {
5673            s.elements
5674                .first()
5675                .and_then(|e| e.first())
5676                .is_some_and(|v| v == "Z15")
5677        });
5678        ConditionResult::from(!found)
5679    }
5680
5681    /// [296] Wenn SG5 LOC+Z20 (Technische Ressource) vorhanden
5682    fn evaluate_296(&self, ctx: &EvaluationContext) -> ConditionResult {
5683        let loc_segs = ctx.find_segments("LOC");
5684        let found = loc_segs.iter().any(|s| {
5685            s.elements
5686                .first()
5687                .and_then(|e| e.first())
5688                .is_some_and(|v| v == "Z20")
5689        });
5690        ConditionResult::from(found)
5691    }
5692
5693    /// [297] #kv# Wenn SG5 LOC+Z18 (Netzlokation) nicht vorhanden
5694    fn evaluate_297(&self, ctx: &EvaluationContext) -> ConditionResult {
5695        let loc_segs = ctx.find_segments("LOC");
5696        let found = loc_segs.iter().any(|s| {
5697            s.elements
5698                .first()
5699                .and_then(|e| e.first())
5700                .is_some_and(|v| v == "Z18")
5701        });
5702        ConditionResult::from(!found)
5703    }
5704
5705    /// [298] Wenn SG5 LOC+Z20 (Technische Ressource) nicht vorhanden
5706    fn evaluate_298(&self, ctx: &EvaluationContext) -> ConditionResult {
5707        let loc_segs = ctx.find_segments("LOC");
5708        let found = loc_segs.iter().any(|s| {
5709            s.elements
5710                .first()
5711                .and_then(|e| e.first())
5712                .is_some_and(|v| v == "Z20")
5713        });
5714        ConditionResult::from(!found)
5715    }
5716
5717    /// [299] #kv# Wenn SG5 LOC+Z19 (Steuerbare Ressource) nicht vorhanden
5718    fn evaluate_299(&self, ctx: &EvaluationContext) -> ConditionResult {
5719        let loc_segs = ctx.find_segments("LOC");
5720        let found = loc_segs.iter().any(|s| {
5721            s.elements
5722                .first()
5723                .and_then(|e| e.first())
5724                .is_some_and(|v| v == "Z19")
5725        });
5726        ConditionResult::from(!found)
5727    }
5728
5729    /// [300] Wenn SG8 SEQ+Z15/ ZE7 (Daten der Tranche) nicht vorhanden
5730    fn evaluate_300(&self, ctx: &EvaluationContext) -> ConditionResult {
5731        let seq_segs = ctx.find_segments("SEQ");
5732        let found = seq_segs.iter().any(|s| {
5733            s.elements
5734                .first()
5735                .and_then(|e| e.first())
5736                .is_some_and(|v| ["Z15", "ZE7"].contains(&v.as_str()))
5737        });
5738        ConditionResult::from(!found)
5739    }
5740
5741    /// [301] Wenn BGM+E03 (Änderungsmeldung) vorhanden
5742    fn evaluate_301(&self, ctx: &EvaluationContext) -> ConditionResult {
5743        let bgm_segs = ctx.find_segments("BGM");
5744        let found = bgm_segs.iter().any(|s| {
5745            s.elements
5746                .first()
5747                .and_then(|e| e.first())
5748                .is_some_and(|v| v == "E03")
5749        });
5750        ConditionResult::from(found)
5751    }
5752
5753    /// [302] Wenn SG8 SEQ+Z08 (Profilschardaten) nicht vorhanden
5754    fn evaluate_302(&self, ctx: &EvaluationContext) -> ConditionResult {
5755        let seq_segs = ctx.find_segments("SEQ");
5756        let found = seq_segs.iter().any(|s| {
5757            s.elements
5758                .first()
5759                .and_then(|e| e.first())
5760                .is_some_and(|v| ["Z08"].contains(&v.as_str()))
5761        });
5762        ConditionResult::from(!found)
5763    }
5764
5765    /// [303] Wenn SG8 SEQ+Z21 (Profildaten) nicht vorhanden
5766    fn evaluate_303(&self, ctx: &EvaluationContext) -> ConditionResult {
5767        let seq_segs = ctx.find_segments("SEQ");
5768        let found = seq_segs.iter().any(|s| {
5769            s.elements
5770                .first()
5771                .and_then(|e| e.first())
5772                .is_some_and(|v| ["Z21"].contains(&v.as_str()))
5773        });
5774        ConditionResult::from(!found)
5775    }
5776
5777    /// [304] Wenn SG8 SEQ+Z22 (Daten der Summenzeitreihe) vorhanden
5778    fn evaluate_304(&self, ctx: &EvaluationContext) -> ConditionResult {
5779        let seq_segs = ctx.find_segments("SEQ");
5780        let found = seq_segs.iter().any(|s| {
5781            s.elements
5782                .first()
5783                .and_then(|e| e.first())
5784                .is_some_and(|v| ["Z22"].contains(&v.as_str()))
5785        });
5786        ConditionResult::from(found)
5787    }
5788
5789    /// [305] Wenn SG8 SEQ+Z24 (Daten der Überführungszeitreihe) vorhanden
5790    fn evaluate_305(&self, ctx: &EvaluationContext) -> ConditionResult {
5791        let seq_segs = ctx.find_segments("SEQ");
5792        let found = seq_segs.iter().any(|s| {
5793            s.elements
5794                .first()
5795                .and_then(|e| e.first())
5796                .is_some_and(|v| ["Z24"].contains(&v.as_str()))
5797        });
5798        ConditionResult::from(found)
5799    }
5800
5801    /// [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
5802    fn evaluate_306(&self, ctx: &EvaluationContext) -> ConditionResult {
5803        // HAND-EDITED: "SG5 LOC+Z22 mit derselben Zeitraum-ID wie in dieser SG8 … des SEQ+Z58" — compared within the enclosing SG4 when the scope
5804        // reaches an SG8; the message as before otherwise.
5805        if let Some(scope) = ctx.enclosing_scope("SG8") {
5806            return ConditionResult::from(
5807                sg4_instances::seq_at(ctx, scope)
5808                    .filter(|(code, zeitraum)| code == "Z58" && !zeitraum.is_empty())
5809                    .is_some_and(|(_, zeitraum)| {
5810                        sg4_instances::ruhende_malo_with_zeitraum(ctx, &zeitraum)
5811                    }),
5812            );
5813        }
5814        use std::collections::HashSet;
5815        // Collect Zeitraum-IDs from SG5 LOC+Z22 (Ruhende Marktlokation) — DE3224 at elements[1][3]
5816        let loc_z22_ids: HashSet<String> = ctx
5817            .find_segments_with_qualifier("LOC", 0, "Z22")
5818            .into_iter()
5819            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
5820            .filter(|v| !v.is_empty())
5821            .collect();
5822        if loc_z22_ids.is_empty() {
5823            return ConditionResult::False;
5824        }
5825        // Check if any SG8 SEQ+Z58 has DE1050 (elements[1][0]) matching a LOC+Z22 DE3224
5826        let has_match = ctx
5827            .find_segments_with_qualifier("SEQ", 0, "Z58")
5828            .iter()
5829            .any(|s| {
5830                s.elements
5831                    .get(1)
5832                    .and_then(|e: &Vec<String>| e.first())
5833                    .is_some_and(|v: &String| !v.is_empty() && loc_z22_ids.contains(v.as_str()))
5834            });
5835        ConditionResult::from(has_match)
5836    }
5837
5838    /// [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
5839    // 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)
5840    fn evaluate_307(&self, ctx: &EvaluationContext) -> ConditionResult {
5841        // HAND-EDITED: "SG5 LOC+Z22 mit derselben Zeitraum-ID … nicht vorhanden wie in dieser SG8 … des SEQ+Z58" — compared within the enclosing SG4 when the scope
5842        // reaches an SG8; the message as before otherwise.
5843        if let Some(scope) = ctx.enclosing_scope("SG8") {
5844            return ConditionResult::from(
5845                sg4_instances::seq_at(ctx, scope)
5846                    .filter(|(code, zeitraum)| code == "Z58" && !zeitraum.is_empty())
5847                    .is_some_and(|(_, zeitraum)| {
5848                        !sg4_instances::ruhende_malo_with_zeitraum(ctx, &zeitraum)
5849                    }),
5850            );
5851        }
5852        let seq_segs = ctx.find_segments_with_qualifier("SEQ", 0, "Z58");
5853        if seq_segs.is_empty() {
5854            return ConditionResult::Unknown;
5855        }
5856        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z22");
5857        let loc_z22_zeitraum_ids: Vec<&str> = loc_segs
5858            .iter()
5859            .filter_map(|s| s.elements.get(1).and_then(|e| e.get(3)).map(|v| v.as_str()))
5860            .filter(|v| !v.is_empty())
5861            .collect();
5862        ConditionResult::from(seq_segs.iter().any(|seq| {
5863            let zid = seq
5864                .elements
5865                .get(1)
5866                .and_then(|e: &Vec<String>| e.first())
5867                .map(|v| v.as_str())
5868                .unwrap_or("");
5869            !zid.is_empty() && !loc_z22_zeitraum_ids.contains(&zid)
5870        }))
5871    }
5872
5873    /// [309] Wenn SG5 LOC+Z22 (ruhende Marktlokation) nicht vorhanden
5874    fn evaluate_309(&self, ctx: &EvaluationContext) -> ConditionResult {
5875        let loc_segs = ctx.find_segments("LOC");
5876        let found = loc_segs.iter().any(|s| {
5877            s.elements
5878                .first()
5879                .and_then(|e| e.first())
5880                .is_some_and(|v| v == "Z22")
5881        });
5882        ConditionResult::from(!found)
5883    }
5884
5885    /// [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...
5886    fn evaluate_314(&self, ctx: &EvaluationContext) -> ConditionResult {
5887        ctx.external.evaluate("code_list_membership_check")
5888    }
5889
5890    /// [315] Wenn BGM+Z88 (Datenclearing) vorhanden
5891    fn evaluate_315(&self, ctx: &EvaluationContext) -> ConditionResult {
5892        ctx.external.evaluate("data_clearing_required")
5893    }
5894
5895    /// [316] Wenn in derselben SG8 das RFF+Z14 (Smartmeter-Gateway) nicht vorhanden
5896    fn evaluate_316(&self, ctx: &EvaluationContext) -> ConditionResult {
5897        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5898        ctx.this_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
5899    }
5900
5901    /// [317] Es ist derselbe Wert wie im DE2380 von DTM+92 (Datum Vertragsbeginn) einzutragen
5902    fn evaluate_317(&self, ctx: &EvaluationContext) -> ConditionResult {
5903        // Check: DTM+92 (Datum Vertragsbeginn) exists — value identity is structural
5904        let found = !ctx.find_segments_with_qualifier("DTM", 0, "92").is_empty();
5905        if found {
5906            ConditionResult::True
5907        } else {
5908            ConditionResult::Unknown
5909        }
5910    }
5911
5912    /// [318] Es ist derselbe Wert wie im DE2380 von DTM+93 (Datum Vertragende) einzutragen
5913    fn evaluate_318(&self, ctx: &EvaluationContext) -> ConditionResult {
5914        // Check: DTM+93 (Datum Vertragende) exists — value identity is structural
5915        let found = !ctx.find_segments_with_qualifier("DTM", 0, "93").is_empty();
5916        if found {
5917            ConditionResult::True
5918        } else {
5919            ConditionResult::Unknown
5920        }
5921    }
5922
5923    /// [321] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
5924    fn evaluate_321(&self, ctx: &EvaluationContext) -> ConditionResult {
5925        let com_segs = ctx.find_segments("COM");
5926        let has_em = com_segs.iter().any(|s| {
5927            s.elements
5928                .first()
5929                .and_then(|e| e.get(1))
5930                .is_some_and(|v| v == "EM")
5931        });
5932        ConditionResult::from(has_em)
5933    }
5934
5935    /// [322] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
5936    fn evaluate_322(&self, ctx: &EvaluationContext) -> ConditionResult {
5937        let com_segs = ctx.find_segments("COM");
5938        let has_phone = com_segs.iter().any(|s| {
5939            s.elements
5940                .first()
5941                .and_then(|e| e.get(1))
5942                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
5943        });
5944        ConditionResult::from(has_phone)
5945    }
5946
5947    /// [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...
5948    fn evaluate_323(&self, ctx: &EvaluationContext) -> ConditionResult {
5949        ctx.external.evaluate("code_list_membership_check")
5950    }
5951
5952    /// [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...
5953    // 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)
5954    fn evaluate_327(&self, ctx: &EvaluationContext) -> ConditionResult {
5955        // HAND-EDITED: "die Zeitraum-ID im DE1050 dieser SG8 SEQ … einer SG6 RFF+Z47/Z48/Z49" — compared within the enclosing SG4 when the scope
5956        // reaches an SG8; the message as before otherwise.
5957        if let Some(scope) = ctx.enclosing_scope("SG8") {
5958            let Some((_, zeitraum)) =
5959                sg4_instances::seq_at(ctx, scope).filter(|(_, z)| !z.is_empty())
5960            else {
5961                return ConditionResult::False;
5962            };
5963            let sg6s = ctx.scopes_under("SG4", "SG6").unwrap_or_default();
5964            return ConditionResult::from(sg6s.iter().any(|p| {
5965                ctx.find_segments_at(p, "RFF").iter().any(|s| {
5966                    matches!(s.get_element(0), "Z47" | "Z48" | "Z49")
5967                        && s.get_component(0, 2) == zeitraum
5968                }) && ctx.find_segments_at(p, "DTM").iter().any(|s| {
5969                    s.get_element(0) == "Z25"
5970                        && s.get_component(0, 1).replace("?+", "+").as_str() >= "202212312300+00"
5971                })
5972            }));
5973        }
5974        const THRESHOLD: &str = "202212312300+00";
5975        let seq_zeitraum_ids: Vec<String> = ctx
5976            .find_segments("SEQ")
5977            .iter()
5978            .filter_map(|s| {
5979                s.elements
5980                    .get(1)
5981                    .and_then(|e: &Vec<String>| e.first())
5982                    .cloned()
5983            })
5984            .filter(|v| !v.is_empty())
5985            .collect();
5986        if seq_zeitraum_ids.is_empty() {
5987            return ConditionResult::Unknown;
5988        }
5989        let rff_segs = ctx.find_segments("RFF");
5990        let matching_rff = rff_segs.iter().any(|rff| {
5991            let qual = rff
5992                .elements
5993                .first()
5994                .and_then(|e: &Vec<String>| e.first())
5995                .map(|v| v.as_str())
5996                .unwrap_or("");
5997            if !["Z47", "Z48", "Z49"].contains(&qual) {
5998                return false;
5999            }
6000            let zid = rff
6001                .elements
6002                .first()
6003                .and_then(|e| e.get(2))
6004                .map(|v| v.as_str())
6005                .unwrap_or("");
6006            !zid.is_empty() && seq_zeitraum_ids.iter().any(|id| id.as_str() == zid)
6007        });
6008        if !matching_rff {
6009            return ConditionResult::False;
6010        }
6011        for dtm in ctx.find_segments_with_qualifier("DTM", 0, "Z25") {
6012            let val = dtm
6013                .elements
6014                .first()
6015                .and_then(|e| e.get(1))
6016                .map(|v| v.replace("?+", "+"))
6017                .unwrap_or_default();
6018            if !val.is_empty() && val.as_str() >= THRESHOLD {
6019                return ConditionResult::True;
6020            }
6021        }
6022        ConditionResult::False
6023    }
6024
6025    /// [329] Wenn in dieser SG4 IDE+24 das STS+E01++A03: E_0096 bzw. A04:E_0097 (Status der Antwort) nicht vorhanden
6026    fn evaluate_329(&self, ctx: &EvaluationContext) -> ConditionResult {
6027        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6028        // Check: STS+E01 does NOT have A03 with E_0096 or A04 with E_0097
6029        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6030        let has_a03_e0096 = sts_segs.iter().any(|s| {
6031            s.elements
6032                .get(2)
6033                .and_then(|e| e.first())
6034                .is_some_and(|v| v == "A03")
6035                && s.elements
6036                    .get(2)
6037                    .and_then(|e| e.get(1))
6038                    .is_some_and(|v| v == "E_0096")
6039        });
6040        let has_a04_e0097 = sts_segs.iter().any(|s| {
6041            s.elements
6042                .get(2)
6043                .and_then(|e| e.first())
6044                .is_some_and(|v| v == "A04")
6045                && s.elements
6046                    .get(2)
6047                    .and_then(|e| e.get(1))
6048                    .is_some_and(|v| v == "E_0097")
6049        });
6050        ConditionResult::from(!has_a03_e0096 && !has_a04_e0097)
6051    }
6052
6053    /// [331] Wenn im SG8 SEQ+Z98 (Daten der Marktlokation) das SG10 CCI+6++ZA9 (Aggreg. verantw. ÜNB) nicht vorhanden
6054    fn evaluate_331(&self, ctx: &EvaluationContext) -> ConditionResult {
6055        let seq_segs = ctx.find_segments("SEQ");
6056        let has_seq = seq_segs.iter().any(|s| {
6057            s.elements
6058                .first()
6059                .and_then(|e| e.first())
6060                .is_some_and(|v| v == "Z98")
6061        });
6062        let cci_segs = ctx.find_segments("CCI");
6063        let has_cci = cci_segs.iter().any(|s| {
6064            s.elements
6065                .first()
6066                .and_then(|e| e.first())
6067                .is_some_and(|v| v == "6")
6068                && s.elements
6069                    .get(2)
6070                    .and_then(|e| e.first())
6071                    .is_some_and(|v| v == "ZA9")
6072        });
6073        ConditionResult::from(!(has_seq && has_cci))
6074    }
6075
6076    /// [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
6077    // 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)
6078    fn evaluate_332(&self, ctx: &EvaluationContext) -> ConditionResult {
6079        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8 … nicht vorhanden" — compared within the enclosing SG4 when the scope
6080        // reaches an SG8; the message as before otherwise.
6081        if let Some(scope) = ctx.enclosing_scope("SG8") {
6082            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
6083            return ConditionResult::from(z01s.iter().any(|p| {
6084                !sg4_instances::sg10_at(ctx, p, |s| sg4_instances::has_cci(s, "6", "ZA9"))
6085            }));
6086        }
6087        let nav = match ctx.navigator {
6088            Some(n) => n,
6089            None => {
6090                if ctx.find_segments_with_qualifier("SEQ", 0, "Z01").is_empty() {
6091                    return ConditionResult::Unknown;
6092                }
6093                let has_cci_za9 = ctx
6094                    .find_segments_with_qualifier("CCI", 0, "6")
6095                    .iter()
6096                    .any(|s| {
6097                        s.elements
6098                            .get(2)
6099                            .and_then(|e: &Vec<String>| e.first())
6100                            .map(|v| v.as_str())
6101                            == Some("ZA9")
6102                    });
6103                return ConditionResult::from(!has_cci_za9);
6104            }
6105        };
6106        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6107        if sg8_count == 0 {
6108            return ConditionResult::Unknown;
6109        }
6110        for sg8_idx in 0..sg8_count {
6111            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], sg8_idx, "SG10");
6112            let mut has_cci_za9 = false;
6113            for sg10_idx in 0..sg10_count {
6114                let ccis = nav.find_segments_in_child_group(
6115                    "CCI",
6116                    &["SG4", "SG8"],
6117                    sg8_idx,
6118                    "SG10",
6119                    sg10_idx,
6120                );
6121                if ccis.iter().any(|s| {
6122                    s.elements
6123                        .first()
6124                        .and_then(|e: &Vec<String>| e.first())
6125                        .map(|v| v.as_str())
6126                        == Some("6")
6127                        && s.elements
6128                            .get(2)
6129                            .and_then(|e: &Vec<String>| e.first())
6130                            .map(|v| v.as_str())
6131                            == Some("ZA9")
6132                }) {
6133                    has_cci_za9 = true;
6134                    break;
6135                }
6136            }
6137            if !has_cci_za9 {
6138                return ConditionResult::True;
6139            }
6140        }
6141        ConditionResult::False
6142    }
6143
6144    /// [333] Wenn in diesem RFF \"Referenz auf die Lokationsbündelstruktur\" im DE1153 der Code Z31 \"Lokationsbündelstruktur\" vorhanden ist
6145    fn evaluate_333(&self, ctx: &EvaluationContext) -> ConditionResult {
6146        let rff_segs = ctx.find_segments("RFF");
6147        let found = rff_segs.iter().any(|s| {
6148            s.elements
6149                .first()
6150                .and_then(|e| e.first())
6151                .is_some_and(|v| v == "Z31")
6152        });
6153        ConditionResult::from(found)
6154    }
6155
6156    /// [334] Nur MP-ID aus Sparte Gas
6157    fn evaluate_334(&self, ctx: &EvaluationContext) -> ConditionResult {
6158        // Check: Marktpartner-ID is from Sparte Gas
6159        ctx.external.evaluate("sector_check")
6160    }
6161
6162    /// [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...
6163    // 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)
6164    fn evaluate_335(&self, ctx: &EvaluationContext) -> ConditionResult {
6165        // HAND-EDITED: the Zeitraum-ID of *this* SG8's SEQ+Z58 (DE1050), looked
6166        // for in the RFF+Z31 of any SG8 SEQ+Z78 of the message.
6167        let z58_ids: Vec<String> = ctx
6168            .scoped_find_segments_in_with_qualifier("SG8", "SEQ", 0, "Z58")
6169            .iter()
6170            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
6171            .filter(|v| !v.is_empty())
6172            .collect();
6173        if z58_ids.is_empty() {
6174            return ConditionResult::Unknown;
6175        }
6176        if ctx.find_segments_with_qualifier("SEQ", 0, "Z78").is_empty() {
6177            return ConditionResult::False;
6178        }
6179        let rff_z31_ids: Vec<String> = ctx
6180            .find_segments_with_qualifier("RFF", 0, "Z31")
6181            .iter()
6182            .filter_map(|rff| rff.elements.first().and_then(|e| e.get(1)).cloned())
6183            .filter(|v| !v.is_empty())
6184            .collect();
6185        ConditionResult::from(z58_ids.iter().any(|id| rff_z31_ids.contains(id)))
6186    }
6187
6188    /// [336] Wenn in Änderungsmeldung gefüllt
6189    fn evaluate_336(&self, _ctx: &EvaluationContext) -> ConditionResult {
6190        // TODO: implement
6191        ConditionResult::Unknown
6192    }
6193
6194    /// [337] Wenn SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96 vorhanden
6195    fn evaluate_337(&self, ctx: &EvaluationContext) -> ConditionResult {
6196        let cci_segs = ctx.find_segments("CCI");
6197        let has_cci = cci_segs.iter().any(|s| {
6198            s.elements
6199                .get(2)
6200                .and_then(|e| e.first())
6201                .is_some_and(|v| v == "ZB4")
6202        });
6203        let cav_segs = ctx.find_segments("CAV");
6204        let has_cav = cav_segs.iter().any(|s| {
6205            s.elements
6206                .first()
6207                .and_then(|e| e.first())
6208                .is_some_and(|v| ["Z95", "Z96"].contains(&v.as_str()))
6209        });
6210        ConditionResult::from(has_cci && has_cav)
6211    }
6212
6213    /// [338] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96/ Z97/ Z99/ ZA1/ ZA3/ ZA4/ZG7 vorhanden
6214    fn evaluate_338(&self, ctx: &EvaluationContext) -> ConditionResult {
6215        let cci_segs = ctx.find_segments("CCI");
6216        let has_cci = cci_segs.iter().any(|s| {
6217            s.elements
6218                .get(2)
6219                .and_then(|e| e.first())
6220                .is_some_and(|v| v == "ZB4")
6221        });
6222        let cav_segs = ctx.find_segments("CAV");
6223        let has_cav = cav_segs.iter().any(|s| {
6224            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6225                ["Z95", "Z96", "Z97", "Z99", "ZA1", "ZA3", "ZA4", "ZG7"].contains(&v.as_str())
6226            })
6227        });
6228        ConditionResult::from(has_cci && has_cav)
6229    }
6230
6231    /// [339] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z97/ Z98/ Z99/ ZA0/ ZA1/ ZA2/ ZA3/ ZA6/ ZG7 vorhanden
6232    fn evaluate_339(&self, ctx: &EvaluationContext) -> ConditionResult {
6233        let cci_segs = ctx.find_segments("CCI");
6234        let has_cci = cci_segs.iter().any(|s| {
6235            s.elements
6236                .get(2)
6237                .and_then(|e| e.first())
6238                .is_some_and(|v| v == "ZB4")
6239        });
6240        let cav_segs = ctx.find_segments("CAV");
6241        let has_cav = cav_segs.iter().any(|s| {
6242            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6243                [
6244                    "Z97", "Z98", "Z99", "ZA0", "ZA1", "ZA2", "ZA3", "ZA6", "ZG7",
6245                ]
6246                .contains(&v.as_str())
6247            })
6248        });
6249        ConditionResult::from(has_cci && has_cav)
6250    }
6251
6252    /// [340] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z97/ Z98/ Z99/ ZA1/ ZA2/ ZA3/ ZA4/ ZA5/ ZA6 vorhanden
6253    fn evaluate_340(&self, ctx: &EvaluationContext) -> ConditionResult {
6254        let cci_segs = ctx.find_segments("CCI");
6255        let has_cci = cci_segs.iter().any(|s| {
6256            s.elements
6257                .get(2)
6258                .and_then(|e| e.first())
6259                .is_some_and(|v| v == "ZB4")
6260        });
6261        let cav_segs = ctx.find_segments("CAV");
6262        let has_cav = cav_segs.iter().any(|s| {
6263            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6264                [
6265                    "Z95", "Z97", "Z98", "Z99", "ZA1", "ZA2", "ZA3", "ZA4", "ZA5", "ZA6",
6266                ]
6267                .contains(&v.as_str())
6268            })
6269        });
6270        ConditionResult::from(has_cci && has_cav)
6271    }
6272
6273    /// [341] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z96/ ZA0/ ZG7 vorhanden
6274    fn evaluate_341(&self, ctx: &EvaluationContext) -> ConditionResult {
6275        let cci_segs = ctx.find_segments("CCI");
6276        let has_cci = cci_segs.iter().any(|s| {
6277            s.elements
6278                .get(2)
6279                .and_then(|e| e.first())
6280                .is_some_and(|v| v == "ZB4")
6281        });
6282        let cav_segs = ctx.find_segments("CAV");
6283        let has_cav = cav_segs.iter().any(|s| {
6284            s.elements
6285                .first()
6286                .and_then(|e| e.first())
6287                .is_some_and(|v| ["Z96", "ZA0", "ZG7"].contains(&v.as_str()))
6288        });
6289        ConditionResult::from(has_cci && has_cav)
6290    }
6291
6292    /// [342] Wenn aktiver MaBiS-ZP für den Betrachtungszeitraum vorhanden
6293    fn evaluate_342(&self, _ctx: &EvaluationContext) -> ConditionResult {
6294        // TODO: implement
6295        ConditionResult::Unknown
6296    }
6297
6298    /// [344] Wenn in dieser SG8 das SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+ZF1 (Netzgangzeitreihe (NGZ)) vorhanden
6299    fn evaluate_344(&self, ctx: &EvaluationContext) -> ConditionResult {
6300        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6301        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6302        let has_cci = cci_segs.iter().any(|s| {
6303            s.elements
6304                .get(2)
6305                .and_then(|e| e.first())
6306                .is_some_and(|v| v == "ZB4")
6307        });
6308        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
6309        let has_cav = cav_segs.iter().any(|s| {
6310            s.elements
6311                .first()
6312                .and_then(|e| e.first())
6313                .is_some_and(|v| ["ZF1"].contains(&v.as_str()))
6314        });
6315        ConditionResult::from(has_cci && has_cav)
6316    }
6317
6318    /// [345] Wenn SG5 LOC+Z17 (Messlokation) vorhanden
6319    fn evaluate_345(&self, ctx: &EvaluationContext) -> ConditionResult {
6320        let loc_segs = ctx.find_segments("LOC");
6321        let found = loc_segs.iter().any(|s| {
6322            s.elements
6323                .first()
6324                .and_then(|e| e.first())
6325                .is_some_and(|v| v == "Z17")
6326        });
6327        ConditionResult::from(found)
6328    }
6329
6330    /// [346] Wenn SG10 CCI+++E13 (Zählertyp) CAV+MME (mME) vorhanden
6331    fn evaluate_346(&self, ctx: &EvaluationContext) -> ConditionResult {
6332        let cci_segs = ctx.find_segments("CCI");
6333        let found = cci_segs.iter().any(|s| {
6334            s.elements
6335                .get(2)
6336                .and_then(|e| e.first())
6337                .is_some_and(|v| v == "E13")
6338        });
6339        ConditionResult::from(found)
6340    }
6341
6342    /// [347] Wenn SG5 LOC+Z16 (Marktlokation) nicht vorhanden
6343    fn evaluate_347(&self, ctx: &EvaluationContext) -> ConditionResult {
6344        let loc_segs = ctx.find_segments("LOC");
6345        let found = loc_segs.iter().any(|s| {
6346            s.elements
6347                .first()
6348                .and_then(|e| e.first())
6349                .is_some_and(|v| v == "Z16")
6350        });
6351        ConditionResult::from(!found)
6352    }
6353
6354    /// [348] Wenn SG5 LOC+Z21 (Tranche) nicht vorhanden
6355    fn evaluate_348(&self, ctx: &EvaluationContext) -> ConditionResult {
6356        let loc_segs = ctx.find_segments("LOC");
6357        let found = loc_segs.iter().any(|s| {
6358            s.elements
6359                .first()
6360                .and_then(|e| e.first())
6361                .is_some_and(|v| v == "Z21")
6362        });
6363        ConditionResult::from(!found)
6364    }
6365
6366    /// [349] Wenn SG5 LOC+Z17 (Messlokation) nicht vorhanden
6367    fn evaluate_349(&self, ctx: &EvaluationContext) -> ConditionResult {
6368        let loc_segs = ctx.find_segments("LOC");
6369        let found = loc_segs.iter().any(|s| {
6370            s.elements
6371                .first()
6372                .and_then(|e| e.first())
6373                .is_some_and(|v| v == "Z17")
6374        });
6375        ConditionResult::from(!found)
6376    }
6377
6378    /// [350] Wenn SG8 SEQ+Z78/ZD5 RFF+Z31 (Referenz auf die Lokationsbündelstruktur) vorhanden
6379    fn evaluate_350(&self, ctx: &EvaluationContext) -> ConditionResult {
6380        let seq_segs = ctx.find_segments("SEQ");
6381        let has_seq = seq_segs.iter().any(|s| {
6382            s.elements
6383                .first()
6384                .and_then(|e| e.first())
6385                .is_some_and(|v| ["Z78", "ZD5"].contains(&v.as_str()))
6386        });
6387        let rff_segs = ctx.find_segments("RFF");
6388        let has_rff = rff_segs.iter().any(|s| {
6389            s.elements
6390                .first()
6391                .and_then(|e| e.first())
6392                .is_some_and(|v| v == "Z31")
6393        });
6394        ConditionResult::from(has_seq && has_rff)
6395    }
6396
6397    /// [351] Wenn SG4 STS+E01++A04/ A05 /A13 /A14 (Status der Antwort) vorhanden
6398    fn evaluate_351(&self, ctx: &EvaluationContext) -> ConditionResult {
6399        let sts_segs = ctx.find_segments("STS");
6400        let found = sts_segs.iter().any(|s| {
6401            s.elements
6402                .first()
6403                .and_then(|e| e.first())
6404                .is_some_and(|v| v == "E01")
6405                && s.elements
6406                    .get(2)
6407                    .and_then(|e| e.first())
6408                    .is_some_and(|v| ["A04", "A05", "A13", "A14"].contains(&v.as_str()))
6409        });
6410        ConditionResult::from(found)
6411    }
6412
6413    /// [352] Wenn SG4 STS++A05/ A06/ A14 /A15 (Status der Antwort) vorhanden
6414    fn evaluate_352(&self, ctx: &EvaluationContext) -> ConditionResult {
6415        let sts_segs = ctx.find_segments("STS");
6416        let found = sts_segs.iter().any(|s| {
6417            s.elements
6418                .get(1)
6419                .and_then(|e| e.first())
6420                .is_some_and(|v| ["A05", "A06", "A14", "A15"].contains(&v.as_str()))
6421        });
6422        ConditionResult::from(found)
6423    }
6424
6425    /// [355] Wenn SG4 STS+E01++A45 (Status der Antwort) vorhanden
6426    fn evaluate_355(&self, ctx: &EvaluationContext) -> ConditionResult {
6427        let sts_segs = ctx.find_segments("STS");
6428        let found = sts_segs.iter().any(|s| {
6429            s.elements
6430                .first()
6431                .and_then(|e| e.first())
6432                .is_some_and(|v| v == "E01")
6433                && s.elements
6434                    .get(2)
6435                    .and_then(|e| e.first())
6436                    .is_some_and(|v| ["A45"].contains(&v.as_str()))
6437        });
6438        ConditionResult::from(found)
6439    }
6440
6441    /// [356] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6442    fn evaluate_356(&self, ctx: &EvaluationContext) -> ConditionResult {
6443        let sts_segs = ctx.find_segments("STS");
6444        let found = sts_segs.iter().any(|s| {
6445            s.elements
6446                .first()
6447                .and_then(|e| e.first())
6448                .is_some_and(|v| v == "E01")
6449                && s.elements
6450                    .get(2)
6451                    .and_then(|e| e.first())
6452                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6453        });
6454        ConditionResult::from(found)
6455    }
6456
6457    /// [357] Wenn SG4 STS+E01++A03/ A09/ A12/ A17 (Status der Antwort) vorhanden
6458    fn evaluate_357(&self, ctx: &EvaluationContext) -> ConditionResult {
6459        let sts_segs = ctx.find_segments("STS");
6460        let found = sts_segs.iter().any(|s| {
6461            s.elements
6462                .first()
6463                .and_then(|e| e.first())
6464                .is_some_and(|v| v == "E01")
6465                && s.elements
6466                    .get(2)
6467                    .and_then(|e| e.first())
6468                    .is_some_and(|v| ["A03", "A09", "A12", "A17"].contains(&v.as_str()))
6469        });
6470        ConditionResult::from(found)
6471    }
6472
6473    /// [358] Wenn SG4 STS+E01++A06 (Status der Antwort) vorhanden
6474    fn evaluate_358(&self, ctx: &EvaluationContext) -> ConditionResult {
6475        let sts_segs = ctx.find_segments("STS");
6476        let found = sts_segs.iter().any(|s| {
6477            s.elements
6478                .first()
6479                .and_then(|e| e.first())
6480                .is_some_and(|v| v == "E01")
6481                && s.elements
6482                    .get(2)
6483                    .and_then(|e| e.first())
6484                    .is_some_and(|v| ["A06"].contains(&v.as_str()))
6485        });
6486        ConditionResult::from(found)
6487    }
6488
6489    /// [359] Es sind nur Antwortcodes aus dem Cluster Ablehnung erlaubt
6490    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6491    // Preserve across regeneration until the codegen prompt is updated.
6492    fn evaluate_359(&self, ctx: &EvaluationContext) -> ConditionResult {
6493        all_sts_e01_in_cluster(ctx, Cluster::is_ablehnung)
6494    }
6495
6496    /// [360] Es sind nur Antwortcodes aus dem Cluster Zustimmung erlaubt
6497    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6498    // Preserve across regeneration until the codegen prompt is updated.
6499    fn evaluate_360(&self, ctx: &EvaluationContext) -> ConditionResult {
6500        all_sts_e01_in_cluster(ctx, Cluster::is_zustimmung)
6501    }
6502
6503    /// [363] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6504    fn evaluate_363(&self, ctx: &EvaluationContext) -> ConditionResult {
6505        let sts_segs = ctx.find_segments("STS");
6506        let found = sts_segs.iter().any(|s| {
6507            s.elements
6508                .first()
6509                .and_then(|e| e.first())
6510                .is_some_and(|v| v == "E01")
6511                && s.elements
6512                    .get(2)
6513                    .and_then(|e| e.first())
6514                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6515        });
6516        ConditionResult::from(found)
6517    }
6518
6519    /// [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
6520    fn evaluate_365(&self, ctx: &EvaluationContext) -> ConditionResult {
6521        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6522        // Check: STS+E01 does NOT have A04 with E_0047/E_0014 or A03 with E_0049/E_0004
6523        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6524        let has_excluded = sts_segs.iter().any(|s| {
6525            let code = s
6526                .elements
6527                .get(2)
6528                .and_then(|e| e.first())
6529                .map(|v| v.as_str());
6530            let ebd = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
6531            matches!(
6532                (code, ebd),
6533                (Some("A04"), Some("E_0047"))
6534                    | (Some("A04"), Some("E_0014"))
6535                    | (Some("A03"), Some("E_0049"))
6536                    | (Some("A03"), Some("E_0004"))
6537            )
6538        });
6539        ConditionResult::from(!has_excluded)
6540    }
6541
6542    /// [366] Bis auf den Code A30 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6543    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6544    // Preserve across regeneration until the codegen prompt is updated.
6545    fn evaluate_366(&self, ctx: &EvaluationContext) -> ConditionResult {
6546        ebd_ablehnung_except(ctx, "E_0624", &["A30"])
6547    }
6548
6549    /// [367] Wenn SG4 STS+E01++A05/ A14 (Status der Antwort) vorhanden
6550    fn evaluate_367(&self, ctx: &EvaluationContext) -> ConditionResult {
6551        let sts_segs = ctx.find_segments("STS");
6552        let found = sts_segs.iter().any(|s| {
6553            s.elements
6554                .first()
6555                .and_then(|e| e.first())
6556                .is_some_and(|v| v == "E01")
6557                && s.elements
6558                    .get(2)
6559                    .and_then(|e| e.first())
6560                    .is_some_and(|v| ["A05", "A14"].contains(&v.as_str()))
6561        });
6562        ConditionResult::from(found)
6563    }
6564
6565    /// [368] Bis auf den Code A41 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6566    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6567    // Preserve across regeneration until the codegen prompt is updated.
6568    fn evaluate_368(&self, ctx: &EvaluationContext) -> ConditionResult {
6569        ebd_ablehnung_except(ctx, "E_0624", &["A41"])
6570    }
6571
6572    /// [370] Wenn SG10 CAV+TLS/TES/BIT/GET/GAT/SOT/WNT/WFT/WAT
6573    fn evaluate_370(&self, ctx: &EvaluationContext) -> ConditionResult {
6574        let cav_segs = ctx.find_segments("CAV");
6575        let found = cav_segs.iter().any(|s| {
6576            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6577                [
6578                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
6579                ]
6580                .contains(&v.as_str())
6581            })
6582        });
6583        ConditionResult::from(found)
6584    }
6585
6586    /// [371] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
6587    fn evaluate_371(&self, ctx: &EvaluationContext) -> ConditionResult {
6588        let seq_segs = ctx.find_segments("SEQ");
6589        let found = seq_segs.iter().any(|s| {
6590            s.elements
6591                .first()
6592                .and_then(|e| e.first())
6593                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
6594        });
6595        ConditionResult::from(!found)
6596    }
6597
6598    /// [372] Wenn SG10 CAV+TES/BIT/GET/GAT/SOT/WNT/WFT/WAT vorhanden
6599    fn evaluate_372(&self, ctx: &EvaluationContext) -> ConditionResult {
6600        let cav_segs = ctx.find_segments("CAV");
6601        let found = cav_segs.iter().any(|s| {
6602            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6603                ["TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT"].contains(&v.as_str())
6604            })
6605        });
6606        ConditionResult::from(found)
6607    }
6608
6609    /// [373] Wenn ein Referenzprofil an der Marktlokation vorhanden ist
6610    fn evaluate_373(&self, _ctx: &EvaluationContext) -> ConditionResult {
6611        // TODO: implement
6612        ConditionResult::Unknown
6613    }
6614
6615    /// [375] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z24 (Daten der Überführungszeitreihe) nicht vorhanden
6616    fn evaluate_375(&self, ctx: &EvaluationContext) -> ConditionResult {
6617        // Check: no SG8 SEQ+Z24 (Daten der Überführungszeitreihe) exists
6618        ctx.lacks_qualifier("SEQ", 0, "Z24")
6619    }
6620
6621    /// [376] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z22 (Daten der Summenzeitreihe) nicht vorhanden
6622    fn evaluate_376(&self, ctx: &EvaluationContext) -> ConditionResult {
6623        // Check: no SG8 SEQ+Z22 (Daten der Summenzeitreihe) exists
6624        ctx.lacks_qualifier("SEQ", 0, "Z22")
6625    }
6626
6627    /// [377] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS vorhanden
6628    // 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)
6629    fn evaluate_377(&self, ctx: &EvaluationContext) -> ConditionResult {
6630        let nav = match ctx.navigator {
6631            Some(n) => n,
6632            None => return ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]),
6633        };
6634        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6635        for i in 0..sg8_count {
6636            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6637            for j in 0..sg10_count {
6638                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6639                let has_cci = ccis.iter().any(|s| {
6640                    s.elements
6641                        .first()
6642                        .and_then(|e: &Vec<String>| e.first())
6643                        .is_some_and(|v: &String| v == "15")
6644                        && s.elements
6645                            .get(2)
6646                            .and_then(|e: &Vec<String>| e.first())
6647                            .is_some_and(|v: &String| v == "Z21")
6648                });
6649                if has_cci {
6650                    let cavs =
6651                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6652                    if cavs.iter().any(|s| {
6653                        s.elements
6654                            .first()
6655                            .and_then(|e: &Vec<String>| e.first())
6656                            .is_some_and(|v: &String| v == "AUS")
6657                    }) {
6658                        return ConditionResult::True;
6659                    }
6660                }
6661            }
6662        }
6663        ConditionResult::False
6664    }
6665
6666    /// [378] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS nicht vorhanden
6667    // 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)
6668    fn evaluate_378(&self, ctx: &EvaluationContext) -> ConditionResult {
6669        let nav = match ctx.navigator {
6670            Some(n) => n,
6671            None => {
6672                let r = ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]);
6673                return match r {
6674                    ConditionResult::True => ConditionResult::False,
6675                    ConditionResult::False => ConditionResult::True,
6676                    ConditionResult::Unknown => ConditionResult::Unknown,
6677                };
6678            }
6679        };
6680        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6681        for i in 0..sg8_count {
6682            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6683            for j in 0..sg10_count {
6684                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6685                let has_cci = ccis.iter().any(|s| {
6686                    s.elements
6687                        .first()
6688                        .and_then(|e: &Vec<String>| e.first())
6689                        .is_some_and(|v: &String| v == "15")
6690                        && s.elements
6691                            .get(2)
6692                            .and_then(|e: &Vec<String>| e.first())
6693                            .is_some_and(|v: &String| v == "Z21")
6694                });
6695                if has_cci {
6696                    let cavs =
6697                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6698                    if cavs.iter().any(|s| {
6699                        s.elements
6700                            .first()
6701                            .and_then(|e: &Vec<String>| e.first())
6702                            .is_some_and(|v: &String| v == "AUS")
6703                    }) {
6704                        return ConditionResult::False;
6705                    }
6706                }
6707            }
6708        }
6709        ConditionResult::True
6710    }
6711
6712    /// [379] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+ Code für EEG-Überführungszeitreihen (ausgenommen AU1) vorhanden
6713    fn evaluate_379(&self, ctx: &EvaluationContext) -> ConditionResult {
6714        let cci_segs = ctx.find_segments("CCI");
6715        let found = cci_segs.iter().any(|s| {
6716            s.elements
6717                .first()
6718                .and_then(|e| e.first())
6719                .is_some_and(|v| v == "15")
6720                && s.elements
6721                    .get(2)
6722                    .and_then(|e| e.first())
6723                    .is_some_and(|v| ["Z21"].contains(&v.as_str()))
6724        });
6725        ConditionResult::from(found)
6726    }
6727
6728    /// [380] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS/AU1 vorhanden
6729    // 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)
6730    fn evaluate_380(&self, ctx: &EvaluationContext) -> ConditionResult {
6731        let nav = match ctx.navigator {
6732            Some(n) => n,
6733            None => {
6734                return ctx.any_group_has_any_qualifier("CAV", 0, &["AUS", "AU1"], &["SG4", "SG8"])
6735            }
6736        };
6737        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6738        for i in 0..sg8_count {
6739            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6740            for j in 0..sg10_count {
6741                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6742                let has_cci = ccis.iter().any(|s| {
6743                    s.elements
6744                        .first()
6745                        .and_then(|e: &Vec<String>| e.first())
6746                        .is_some_and(|v: &String| v == "15")
6747                        && s.elements
6748                            .get(2)
6749                            .and_then(|e: &Vec<String>| e.first())
6750                            .is_some_and(|v: &String| v == "Z21")
6751                });
6752                if has_cci {
6753                    let cavs =
6754                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6755                    if cavs.iter().any(|s| {
6756                        s.elements
6757                            .first()
6758                            .and_then(|e: &Vec<String>| e.first())
6759                            .is_some_and(|v: &String| v == "AUS" || v == "AU1")
6760                    }) {
6761                        return ConditionResult::True;
6762                    }
6763                }
6764            }
6765        }
6766        ConditionResult::False
6767    }
6768
6769    /// [384] Wenn in derselben SG8 SG10 CCI+Z37++ZD1 (Basis zur Bildung der Tranchengröße) (Prozentual) vorhanden
6770    fn evaluate_384(&self, ctx: &EvaluationContext) -> ConditionResult {
6771        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6772        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6773        let found = cci_segs.iter().any(|s| {
6774            s.elements
6775                .first()
6776                .and_then(|e| e.first())
6777                .is_some_and(|v| v == "Z37")
6778                && s.elements
6779                    .get(2)
6780                    .and_then(|e| e.first())
6781                    .is_some_and(|v| v == "ZD1")
6782        });
6783        ConditionResult::from(found)
6784    }
6785
6786    /// [386] Wenn mehr als eine SG8 SEQ+Z02/ ZE3 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden
6787    fn evaluate_386(&self, ctx: &EvaluationContext) -> ConditionResult {
6788        // Check: more than one SG8 SEQ+Z02/ZE3 with OBIS energy quantity in PIA+5
6789        let seq_count = ctx
6790            .find_segments("SEQ")
6791            .iter()
6792            .filter(|s| {
6793                s.elements
6794                    .first()
6795                    .and_then(|e| e.first())
6796                    .is_some_and(|v| v == "Z02" || v == "ZE3")
6797            })
6798            .count();
6799        ConditionResult::from(seq_count > 1)
6800    }
6801
6802    /// [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...
6803    fn evaluate_387(&self, ctx: &EvaluationContext) -> ConditionResult {
6804        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6805        // Check: same SG8 (OBIS Daten) has OBIS energy quantity code in PIA+5
6806        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6807        let found = pia_segs.iter().any(|s| {
6808            s.elements
6809                .first()
6810                .and_then(|e| e.first())
6811                .is_some_and(|v| v == "5")
6812                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6813                    v.starts_with("1-")
6814                        && (v.contains(":1.8.")
6815                            || v.contains(":2.8.")
6816                            || v.contains(":1.9.")
6817                            || v.contains(":2.9."))
6818                })
6819        });
6820        ConditionResult::from(found)
6821    }
6822
6823    /// [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...
6824    fn evaluate_388(&self, ctx: &EvaluationContext) -> ConditionResult {
6825        // Check: at least one RFF+Z49 (Zeitraum) exists
6826        let found = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6827        ConditionResult::from(found)
6828    }
6829
6830    /// [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...
6831    fn evaluate_391(&self, ctx: &EvaluationContext) -> ConditionResult {
6832        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6833        // Check: OBIS Zählerstand (meter reading) code in PIA+5 within SEQ+Z20
6834        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6835        let found = pia_segs.iter().any(|s| {
6836            s.elements
6837                .first()
6838                .and_then(|e| e.first())
6839                .is_some_and(|v| v == "5")
6840                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6841                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
6842                })
6843        });
6844        ConditionResult::from(found)
6845    }
6846
6847    /// [392] Wenn in diesem PIA der Code im Format n1-n2-n1 angegeben ist
6848    fn evaluate_392(&self, ctx: &EvaluationContext) -> ConditionResult {
6849        // Check: PIA code in format n1-n2-n1 (e.g., "1-08-1")
6850        let pia_segs = ctx.find_segments("PIA");
6851        let found = pia_segs.iter().any(|s| {
6852            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6853                let parts: Vec<&str> = v.split('-').collect();
6854                parts.len() == 3
6855                    && parts[0].len() == 1
6856                    && parts[0].chars().all(|c| c.is_ascii_digit())
6857                    && parts[1].len() == 2
6858                    && parts[1].chars().all(|c| c.is_ascii_digit())
6859                    && parts[2].len() == 1
6860                    && parts[2].chars().all(|c| c.is_ascii_digit())
6861            })
6862        });
6863        ConditionResult::from(found)
6864    }
6865
6866    /// [393] Wenn in diesem PIA der Code im Format n1-n2-n1-n3 angegeben ist
6867    fn evaluate_393(&self, ctx: &EvaluationContext) -> ConditionResult {
6868        // Check: PIA code in format n1-n2-n1-n3 (e.g., "1-08-1-001")
6869        let pia_segs = ctx.find_segments("PIA");
6870        let found = pia_segs.iter().any(|s| {
6871            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6872                let parts: Vec<&str> = v.split('-').collect();
6873                parts.len() == 4
6874                    && parts[0].len() == 1
6875                    && parts[0].chars().all(|c| c.is_ascii_digit())
6876                    && parts[1].len() == 2
6877                    && parts[1].chars().all(|c| c.is_ascii_digit())
6878                    && parts[2].len() == 1
6879                    && parts[2].chars().all(|c| c.is_ascii_digit())
6880                    && parts[3].len() == 3
6881                    && parts[3].chars().all(|c| c.is_ascii_digit())
6882            })
6883        });
6884        ConditionResult::from(found)
6885    }
6886
6887    /// [394] Wenn in diesem PIA der Code im Format n1-n2-n1-n8-n2 angegeben ist
6888    fn evaluate_394(&self, ctx: &EvaluationContext) -> ConditionResult {
6889        // Check: PIA code in format n1-n2-n1-n8-n2
6890        let pia_segs = ctx.find_segments("PIA");
6891        let found = pia_segs.iter().any(|s| {
6892            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6893                let parts: Vec<&str> = v.split('-').collect();
6894                parts.len() == 5
6895                    && parts[0].len() == 1
6896                    && parts[0].chars().all(|c| c.is_ascii_digit())
6897                    && parts[1].len() == 2
6898                    && parts[1].chars().all(|c| c.is_ascii_digit())
6899                    && parts[2].len() == 1
6900                    && parts[2].chars().all(|c| c.is_ascii_digit())
6901                    && parts[3].len() == 8
6902                    && parts[3].chars().all(|c| c.is_ascii_digit())
6903                    && parts[4].len() == 2
6904                    && parts[4].chars().all(|c| c.is_ascii_digit())
6905            })
6906        });
6907        ConditionResult::from(found)
6908    }
6909
6910    /// [395] Wenn in diesem PIA der Code im Format n1-n2-n1-n8 angegeben ist
6911    fn evaluate_395(&self, ctx: &EvaluationContext) -> ConditionResult {
6912        // Check: PIA code in format n1-n2-n1-n8
6913        let pia_segs = ctx.find_segments("PIA");
6914        let found = pia_segs.iter().any(|s| {
6915            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6916                let parts: Vec<&str> = v.split('-').collect();
6917                parts.len() == 4
6918                    && parts[0].len() == 1
6919                    && parts[0].chars().all(|c| c.is_ascii_digit())
6920                    && parts[1].len() == 2
6921                    && parts[1].chars().all(|c| c.is_ascii_digit())
6922                    && parts[2].len() == 1
6923                    && parts[2].chars().all(|c| c.is_ascii_digit())
6924                    && parts[3].len() == 8
6925                    && parts[3].chars().all(|c| c.is_ascii_digit())
6926            })
6927        });
6928        ConditionResult::from(found)
6929    }
6930
6931    /// [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
6932    fn evaluate_396(&self, ctx: &EvaluationContext) -> ConditionResult {
6933        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6934        // Check: OBIS Wirkarbeit und 1/4 Stunde code in PIA+5
6935        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6936        let found = pia_segs.iter().any(|s| {
6937            s.elements
6938                .first()
6939                .and_then(|e| e.first())
6940                .is_some_and(|v| v == "5")
6941                && s.elements
6942                    .get(1)
6943                    .and_then(|e| e.first())
6944                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
6945        });
6946        ConditionResult::from(found)
6947    }
6948
6949    /// [397] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/1-08-4 beginnt
6950    fn evaluate_397(&self, ctx: &EvaluationContext) -> ConditionResult {
6951        let pia_segs = ctx.find_segments("PIA");
6952        let found = pia_segs.iter().any(|s| {
6953            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6954                let prefixes = ["1-08-1", "1-08-4"];
6955                prefixes.iter().any(|p| v.starts_with(p))
6956            })
6957        });
6958        ConditionResult::from(found)
6959    }
6960
6961    /// [398] Wenn in diesem PIA der Code im DE7140 mit 1-08-2/1-08-5 beginnt
6962    fn evaluate_398(&self, ctx: &EvaluationContext) -> ConditionResult {
6963        let pia_segs = ctx.find_segments("PIA");
6964        let found = pia_segs.iter().any(|s| {
6965            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6966                let prefixes = ["1-08-2", "1-08-5"];
6967                prefixes.iter().any(|p| v.starts_with(p))
6968            })
6969        });
6970        ConditionResult::from(found)
6971    }
6972
6973    /// [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...
6974    // 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)
6975    fn evaluate_399(&self, ctx: &EvaluationContext) -> ConditionResult {
6976        let nav = match ctx.navigator() {
6977            Some(n) => n,
6978            None => return ConditionResult::Unknown,
6979        };
6980        // Collect DE3224 values from LOC segments in SG5 — LOC.C517.DE3224 = elements[1][3]
6981        // These serve as the Zeitraum-IDs referenced by the condition's "dieses Segments"
6982        let sg5_count = nav.group_instance_count(&["SG4", "SG5"]);
6983        let mut loc_zeitraum_ids: Vec<String> = Vec::new();
6984        for i in 0..sg5_count {
6985            let locs = nav.find_segments_in_group("LOC", &["SG4", "SG5"], i);
6986            for loc in &locs {
6987                if let Some(zid) = loc.elements.get(1).and_then(|e| e.get(3)) {
6988                    if !zid.is_empty() {
6989                        loc_zeitraum_ids.push(zid.clone());
6990                    }
6991                }
6992            }
6993        }
6994        if loc_zeitraum_ids.is_empty() {
6995            return ConditionResult::Unknown;
6996        }
6997        let pauschal_id = "9992000000018";
6998        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6999        // Find any Z78 SG8 whose Zeitraum-ID (SEQ.C286.DE1050) matches a LOC DE3224
7000        // and that has RFF+Z31 with DE1154 != pauschal_id
7001        for i in 0..sg8_count {
7002            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
7003            for seq in &seqs {
7004                if !seq
7005                    .elements
7006                    .first()
7007                    .and_then(|e| e.first())
7008                    .is_some_and(|v| v == "Z78")
7009                {
7010                    continue;
7011                }
7012                let zid = seq
7013                    .elements
7014                    .get(1)
7015                    .and_then(|e| e.first())
7016                    .map(|s| s.as_str())
7017                    .unwrap_or("");
7018                if !loc_zeitraum_ids.iter().any(|z| z == zid) {
7019                    continue;
7020                }
7021                // Matching Z78 SG8 found — check RFF+Z31 DE1154
7022                let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
7023                for rff in &rffs {
7024                    let qual = rff
7025                        .elements
7026                        .first()
7027                        .and_then(|e| e.first())
7028                        .map(|v| v.as_str())
7029                        .unwrap_or("");
7030                    if qual == "Z31" {
7031                        let id = rff
7032                            .elements
7033                            .first()
7034                            .and_then(|e| e.get(1))
7035                            .map(|v| v.as_str())
7036                            .unwrap_or("");
7037                        let normalized = id.replace(' ', "");
7038                        if !normalized.is_empty() && normalized != pauschal_id {
7039                            return ConditionResult::True;
7040                        }
7041                    }
7042                }
7043            }
7044        }
7045        ConditionResult::False
7046    }
7047
7048    /// [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+...
7049    fn evaluate_401(&self, ctx: &EvaluationContext) -> ConditionResult {
7050        // Check: this DTM+Z25 is not in SG6 RFF+Z49 or Z53
7051        // Approximate: check if RFF+Z49 or Z53 exists
7052        let has_z49 = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
7053        let has_z53 = !ctx.find_segments_with_qualifier("RFF", 0, "Z53").is_empty();
7054        ConditionResult::from(!has_z49 && !has_z53)
7055    }
7056
7057    /// [402] Wenn in derselben SG8  (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl der Werteart \"Zählerstand\" im PIA+5 vorhanden
7058    fn evaluate_402(&self, ctx: &EvaluationContext) -> ConditionResult {
7059        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
7060        // Check: OBIS Zählerstand code in PIA+5
7061        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7062        let found = pia_segs.iter().any(|s| {
7063            s.elements
7064                .first()
7065                .and_then(|e| e.first())
7066                .is_some_and(|v| v == "5")
7067                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7068                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
7069                })
7070        });
7071        ConditionResult::from(found)
7072    }
7073
7074    /// [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
7075    // 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)
7076    fn evaluate_403(&self, ctx: &EvaluationContext) -> ConditionResult {
7077        let sender_mp_id: String = match ctx.find_segments_with_qualifier("NAD", 0, "MS").first() {
7078            Some(nad) => match nad.elements.get(1).and_then(|e: &Vec<String>| e.first()) {
7079                Some(id) if !id.is_empty() => id.clone(),
7080                _ => return ConditionResult::Unknown,
7081            },
7082            None => return ConditionResult::Unknown,
7083        };
7084        let nav = match ctx.navigator {
7085            Some(n) => n,
7086            None => return ConditionResult::Unknown,
7087        };
7088        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7089        for i in 0..sg8_count {
7090            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7091            for j in 0..sg10_count {
7092                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7093                let has_cci_zb3 = ccis.iter().any(|s| {
7094                    s.elements
7095                        .get(2)
7096                        .and_then(|e: &Vec<String>| e.first())
7097                        .is_some_and(|v: &String| v == "ZB3")
7098                });
7099                if !has_cci_zb3 {
7100                    continue;
7101                }
7102                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
7103                for cav in &cavs {
7104                    if cav
7105                        .elements
7106                        .first()
7107                        .and_then(|e: &Vec<String>| e.first())
7108                        .is_some_and(|v: &String| v == "Z91")
7109                    {
7110                        if let Some(msb_id) = cav.elements.first().and_then(|e| e.get(1)) {
7111                            if !msb_id.is_empty() {
7112                                return ConditionResult::from(
7113                                    sender_mp_id.as_str() != msb_id.as_str(),
7114                                );
7115                            }
7116                        }
7117                    }
7118                }
7119            }
7120        }
7121        ConditionResult::Unknown
7122    }
7123
7124    /// [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
7125    // 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)
7126    fn evaluate_404(&self, ctx: &EvaluationContext) -> ConditionResult {
7127        let mr_nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
7128        let mr_mp_id = match mr_nads.first() {
7129            Some(nad) => nad
7130                .elements
7131                .get(1)
7132                .and_then(|e: &Vec<String>| e.first())
7133                .cloned()
7134                .unwrap_or_default(),
7135            None => return ConditionResult::Unknown,
7136        };
7137        if mr_mp_id.is_empty() {
7138            return ConditionResult::Unknown;
7139        }
7140
7141        let nav = match ctx.navigator {
7142            Some(n) => n,
7143            None => {
7144                let cavs = ctx.find_segments("CAV");
7145                let same_id_exists = cavs.iter().any(|s| {
7146                    s.elements
7147                        .first()
7148                        .and_then(|e: &Vec<String>| e.first())
7149                        .is_some_and(|v: &String| v == "Z91")
7150                        && s.elements
7151                            .first()
7152                            .and_then(|e| e.get(1))
7153                            .is_some_and(|v: &String| v == &mr_mp_id)
7154                });
7155                return ConditionResult::from(!same_id_exists);
7156            }
7157        };
7158
7159        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7160        for i in 0..sg8_count {
7161            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7162            for j in 0..sg10_count {
7163                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7164                let has_cci_zb3 = ccis.iter().any(|s| {
7165                    s.elements
7166                        .get(2)
7167                        .and_then(|e: &Vec<String>| e.first())
7168                        .is_some_and(|v: &String| v == "ZB3")
7169                });
7170                if !has_cci_zb3 {
7171                    continue;
7172                }
7173                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
7174                for cav in &cavs {
7175                    if cav
7176                        .elements
7177                        .first()
7178                        .and_then(|e: &Vec<String>| e.first())
7179                        .is_some_and(|v: &String| v == "Z91")
7180                    {
7181                        let cav_mp_id = cav
7182                            .elements
7183                            .first()
7184                            .and_then(|e| e.get(1))
7185                            .map(|s| s.as_str())
7186                            .unwrap_or("");
7187                        if cav_mp_id == mr_mp_id.as_str() {
7188                            return ConditionResult::False;
7189                        }
7190                    }
7191                }
7192            }
7193        }
7194
7195        ConditionResult::True
7196    }
7197
7198    /// [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
7199    fn evaluate_405(&self, ctx: &EvaluationContext) -> ConditionResult {
7200        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7201        // Check: OBIS Wirkarbeit kumuliert code in PIA+5
7202        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7203        let found = pia_segs.iter().any(|s| {
7204            s.elements
7205                .first()
7206                .and_then(|e| e.first())
7207                .is_some_and(|v| v == "5")
7208                && s.elements
7209                    .get(1)
7210                    .and_then(|e| e.first())
7211                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7212        });
7213        ConditionResult::from(found)
7214    }
7215
7216    /// [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
7217    fn evaluate_406(&self, ctx: &EvaluationContext) -> ConditionResult {
7218        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7219        // Check: OBIS Wirkarbeit höchste 1/4 Stunde code in PIA+5
7220        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7221        let found = pia_segs.iter().any(|s| {
7222            s.elements
7223                .first()
7224                .and_then(|e| e.first())
7225                .is_some_and(|v| v == "5")
7226                && s.elements
7227                    .get(1)
7228                    .and_then(|e| e.first())
7229                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7230        });
7231        ConditionResult::from(found)
7232    }
7233
7234    /// [407] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Blindarbeit im PIA+5 vorhanden
7235    fn evaluate_407(&self, ctx: &EvaluationContext) -> ConditionResult {
7236        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7237        // Check: OBIS Blindarbeit code in PIA+5
7238        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7239        let found = pia_segs.iter().any(|s| {
7240            s.elements
7241                .first()
7242                .and_then(|e| e.first())
7243                .is_some_and(|v| v == "5")
7244                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7245                    v.starts_with("1-") && (v.contains(":3.8.") || v.contains(":4.8."))
7246                })
7247        });
7248        ConditionResult::from(found)
7249    }
7250
7251    /// [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...
7252    fn evaluate_408(&self, ctx: &EvaluationContext) -> ConditionResult {
7253        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7254        // reaches an SG8; the message as before otherwise.
7255        if let Some(scope) = ctx.enclosing_scope("SG8") {
7256            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7257                ctx,
7258                scope,
7259                is_obis_wirkarbeit_kumuliert,
7260            ));
7261        }
7262        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7263        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7264        ConditionResult::from(has_seq_z02)
7265    }
7266
7267    /// [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 ...
7268    fn evaluate_409(&self, ctx: &EvaluationContext) -> ConditionResult {
7269        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7270        // reaches an SG8; the message as before otherwise.
7271        if let Some(scope) = ctx.enclosing_scope("SG8") {
7272            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7273                ctx,
7274                scope,
7275                is_obis_wirkarbeit_kumuliert,
7276            ));
7277        }
7278        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7279        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7280        ConditionResult::from(!has_seq_z02)
7281    }
7282
7283    /// [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...
7284    fn evaluate_410(&self, ctx: &EvaluationContext) -> ConditionResult {
7285        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7286        // reaches an SG8; the message as before otherwise.
7287        if let Some(scope) = ctx.enclosing_scope("SG8") {
7288            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7289                ctx,
7290                scope,
7291                is_obis_wirkarbeit_quarter_hour,
7292            ));
7293        }
7294        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7295        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7296        ConditionResult::from(has_seq_z02)
7297    }
7298
7299    /// [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 ...
7300    fn evaluate_411(&self, ctx: &EvaluationContext) -> ConditionResult {
7301        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7302        // reaches an SG8; the message as before otherwise.
7303        if let Some(scope) = ctx.enclosing_scope("SG8") {
7304            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7305                ctx,
7306                scope,
7307                is_obis_wirkarbeit_quarter_hour,
7308            ));
7309        }
7310        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7311        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7312        ConditionResult::from(!has_seq_z02)
7313    }
7314
7315    /// [412] Es sind ausschließlich Konfigurationsprodukt-Codes der \"EDI@Energy Codeliste der Konfigurationen\" aus Kapitel 4.2 Konfigurationsprodukte Leistungskurvendefinition erlaubt
7316    fn evaluate_412(&self, ctx: &EvaluationContext) -> ConditionResult {
7317        ctx.external.evaluate("code_list_membership_check")
7318    }
7319
7320    /// [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
7321    fn evaluate_413(&self, ctx: &EvaluationContext) -> ConditionResult {
7322        // Check: SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7323        let found = ctx.find_segments("SEQ").iter().any(|s| {
7324            s.elements
7325                .first()
7326                .and_then(|e| e.first())
7327                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7328        });
7329        ConditionResult::from(found)
7330    }
7331
7332    /// [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
7333    fn evaluate_414(&self, ctx: &EvaluationContext) -> ConditionResult {
7334        // Check: no SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7335        let found = ctx.find_segments("SEQ").iter().any(|s| {
7336            s.elements
7337                .first()
7338                .and_then(|e| e.first())
7339                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7340        });
7341        ConditionResult::from(!found)
7342    }
7343
7344    /// [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
7345    fn evaluate_415(&self, ctx: &EvaluationContext) -> ConditionResult {
7346        // Check: another SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7347        let count = ctx
7348            .find_segments("SEQ")
7349            .iter()
7350            .filter(|s| {
7351                s.elements
7352                    .first()
7353                    .and_then(|e| e.first())
7354                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7355            })
7356            .count();
7357        ConditionResult::from(count > 1)
7358    }
7359
7360    /// [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
7361    fn evaluate_416(&self, ctx: &EvaluationContext) -> ConditionResult {
7362        // Check: no other SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7363        let count = ctx
7364            .find_segments("SEQ")
7365            .iter()
7366            .filter(|s| {
7367                s.elements
7368                    .first()
7369                    .and_then(|e| e.first())
7370                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7371            })
7372            .count();
7373        ConditionResult::from(count <= 1)
7374    }
7375
7376    /// [417] Wenn für den erforderlichen Wert keine Zählzeit benötigt wird
7377    fn evaluate_417(&self, _ctx: &EvaluationContext) -> ConditionResult {
7378        // TODO: implement
7379        ConditionResult::Unknown
7380    }
7381
7382    /// [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
7383    fn evaluate_419(&self, _ctx: &EvaluationContext) -> ConditionResult {
7384        // TODO: implement
7385        ConditionResult::Unknown
7386    }
7387
7388    /// [420] Wenn in dieser SG8 das RFF+Z14 (Smartmeter-Gateway) vorhanden ist
7389    fn evaluate_420(&self, ctx: &EvaluationContext) -> ConditionResult {
7390        // HAND-EDITED: "in dieser SG8" — scoped to the enclosing SG8.
7391        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7392        let found = rff_segs.iter().any(|s| {
7393            s.elements
7394                .first()
7395                .and_then(|e| e.first())
7396                .is_some_and(|v| v == "Z14")
7397        });
7398        ConditionResult::from(found)
7399    }
7400
7401    /// [421] Wenn in dieser SG8 das SG10 CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden ist
7402    fn evaluate_421(&self, ctx: &EvaluationContext) -> ConditionResult {
7403        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing group.
7404        // Check: CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden
7405        ctx.this_group_has_qualifier("CCI", 0, "Z39", &["SG4", "SG8"])
7406    }
7407
7408    /// [425] Messprodukt-Code aus den Kapitel 2.1 \"Standard-Messprodukte der Marktlokation\" der Codeliste der Konfigurationen
7409    fn evaluate_425(&self, ctx: &EvaluationContext) -> ConditionResult {
7410        ctx.external.evaluate("code_list_membership_check")
7411    }
7412
7413    /// [426] Messprodukt-Code aus den Kapitel 2.2 \"Standard-Messprodukte der Tranche\" der Codeliste der Konfigurationen
7414    fn evaluate_426(&self, ctx: &EvaluationContext) -> ConditionResult {
7415        ctx.external.evaluate("code_list_membership_check")
7416    }
7417
7418    /// [427] Messprodukt-Code aus den Kapitel 2.3 \"Standard-Messprodukte der Messlokation\" der Codeliste der Konfigurationen
7419    fn evaluate_427(&self, ctx: &EvaluationContext) -> ConditionResult {
7420        ctx.external.evaluate("code_list_membership_check")
7421    }
7422
7423    /// [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...
7424    fn evaluate_428(&self, ctx: &EvaluationContext) -> ConditionResult {
7425        ctx.external.evaluate("code_list_membership_check")
7426    }
7427
7428    /// [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...
7429    fn evaluate_429(&self, ctx: &EvaluationContext) -> ConditionResult {
7430        ctx.external.evaluate("code_list_membership_check")
7431    }
7432
7433    /// [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...
7434    fn evaluate_430(&self, ctx: &EvaluationContext) -> ConditionResult {
7435        // Check: another SG8 SEQ+Z27 exists with matching RFF+Z18
7436        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7437        ConditionResult::from(seq_z27_count > 1)
7438    }
7439
7440    /// [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+...
7441    fn evaluate_431(&self, ctx: &EvaluationContext) -> ConditionResult {
7442        // Check: no other SG8 SEQ+Z27 exists with matching RFF+Z18
7443        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7444        ConditionResult::from(seq_z27_count <= 1)
7445    }
7446
7447    /// [432] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
7448    fn evaluate_432(&self, ctx: &EvaluationContext) -> ConditionResult {
7449        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7450        // Check: PIA+5 with code 9991000000078 (Wirkarbeit Lastgang) exists
7451        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7452        let found = pia_segs.iter().any(|s| {
7453            s.elements
7454                .first()
7455                .and_then(|e| e.first())
7456                .is_some_and(|v| v == "5")
7457                && s.elements
7458                    .get(1)
7459                    .and_then(|e| e.first())
7460                    .is_some_and(|v| v == "9991000000078")
7461        });
7462        ConditionResult::from(found)
7463    }
7464
7465    /// [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 ...
7466    fn evaluate_433(&self, ctx: &EvaluationContext) -> ConditionResult {
7467        ctx.external.evaluate("code_list_membership_check")
7468    }
7469
7470    /// [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...
7471    fn evaluate_435(&self, ctx: &EvaluationContext) -> ConditionResult {
7472        ctx.external.evaluate("code_list_membership_check")
7473    }
7474
7475    /// [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
7476    fn evaluate_436(&self, ctx: &EvaluationContext) -> ConditionResult {
7477        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7478        // Check: PIA+5 with code 9991000000086 (Wirkarbeit höchste 1/4h Leistung) exists
7479        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7480        let found = pia_segs.iter().any(|s| {
7481            s.elements
7482                .first()
7483                .and_then(|e| e.first())
7484                .is_some_and(|v| v == "5")
7485                && s.elements
7486                    .get(1)
7487                    .and_then(|e| e.first())
7488                    .is_some_and(|v| v == "9991000000086")
7489        });
7490        ConditionResult::from(found)
7491    }
7492
7493    /// [437] Wenn in dieser SG4 das STS+E01++A04 / A23 (Status der Antwort) vorhanden
7494    fn evaluate_437(&self, ctx: &EvaluationContext) -> ConditionResult {
7495        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
7496        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
7497        let found = sts_segs.iter().any(|s| {
7498            s.elements
7499                .first()
7500                .and_then(|e| e.first())
7501                .is_some_and(|v| v == "E01")
7502                && s.elements
7503                    .get(2)
7504                    .and_then(|e| e.first())
7505                    .is_some_and(|v| ["A04", "A23"].contains(&v.as_str()))
7506        });
7507        ConditionResult::from(found)
7508    }
7509
7510    /// [438] Wenn im selben CCI im DE7059 der Code Z39 (Code der Zählzeit) vorhanden ist
7511    fn evaluate_438(&self, ctx: &EvaluationContext) -> ConditionResult {
7512        // HAND-EDITED: "im selben CCI" — the CCI the field is in (its own segment
7513        // in tree validation), else the enclosing SG10's CCI.
7514        let is_z39 = |elements: &[Vec<String>]| {
7515            elements
7516                .first()
7517                .and_then(|e| e.first())
7518                .is_some_and(|v| v == "Z39")
7519        };
7520        if let Some(own) = ctx.resolved_segment.filter(|_| {
7521            ctx.scoped_find_segments_in("SG10", "CCI")
7522                .iter()
7523                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
7524        }) {
7525            return ConditionResult::from(is_z39(own));
7526        }
7527        ConditionResult::from(
7528            ctx.scoped_find_segments_in("SG10", "CCI")
7529                .iter()
7530                .any(|s| is_z39(&s.elements)),
7531        )
7532    }
7533
7534    /// [440] Wenn in demselben RFF der Code Z35 (vorgelagerte Netzlokation) im DE1153 vorhanden
7535    fn evaluate_440(&self, ctx: &EvaluationContext) -> ConditionResult {
7536        // Check: RFF with qualifier Z35 (vorgelagerte Netzlokation) exists
7537        ctx.has_qualifier("RFF", 0, "Z35")
7538    }
7539
7540    /// [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
7541    fn evaluate_441(&self, ctx: &EvaluationContext) -> ConditionResult {
7542        // Check: SG8 SEQ+Z03/ZF5 device without RFF+Z14 (SMGW reference)
7543        ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
7544    }
7545
7546    /// [442] Wenn in demselben RFF der Code Z34 (vorgelagerte Messlokation) im DE1153 vorhanden
7547    fn evaluate_442(&self, ctx: &EvaluationContext) -> ConditionResult {
7548        // Check: RFF with qualifier Z34 (vorgelagerte Messlokation) exists
7549        ctx.has_qualifier("RFF", 0, "Z34")
7550    }
7551
7552    /// [444] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das PIA+5 (Produkt-Daten der Netzlokation) nicht vorhanden
7553    // 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)
7554    fn evaluate_444(&self, ctx: &EvaluationContext) -> ConditionResult {
7555        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7556        // scope reaches one; the message as before otherwise.
7557        if let Some(scope) = ctx.enclosing_scope("SG8") {
7558            return ConditionResult::from(
7559                ctx.find_segments_at(scope, "SEQ")
7560                    .iter()
7561                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7562                    && !ctx
7563                        .find_segments_at(scope, "PIA")
7564                        .iter()
7565                        .any(|s| s.get_element(0) == "5"),
7566            );
7567        }
7568        for code in ["Z60", "ZE0", "ZG8", "ZG9"] {
7569            if matches!(
7570                ctx.any_group_has_qualifier_without("SEQ", 0, code, "PIA", 0, "5", &["SG4", "SG8"]),
7571                ConditionResult::True
7572            ) {
7573                return ConditionResult::True;
7574            }
7575        }
7576        ConditionResult::False
7577    }
7578
7579    /// [445] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das SG10 CCI+11 (Details zum Produkt der Netzlokation) nicht vorhanden
7580    // 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)
7581    fn evaluate_445(&self, ctx: &EvaluationContext) -> ConditionResult {
7582        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7583        // scope reaches one; the message as before otherwise.
7584        if let Some(scope) = ctx.enclosing_scope("SG8") {
7585            return ConditionResult::from(
7586                ctx.find_segments_at(scope, "SEQ")
7587                    .iter()
7588                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7589                    && !ctx
7590                        .find_segments_at(scope, "CCI")
7591                        .iter()
7592                        .any(|s| s.get_element(0) == "11"),
7593            );
7594        }
7595        let nav = match ctx.navigator {
7596            Some(n) => n,
7597            None => {
7598                let has_cci11 = ["Z60", "ZE0", "ZG8", "ZG9"].iter().any(|code| {
7599                    matches!(
7600                        ctx.filtered_parent_child_has_qualifier(
7601                            &["SG4", "SG8"],
7602                            "SEQ",
7603                            0,
7604                            code,
7605                            "SG10",
7606                            "CCI",
7607                            0,
7608                            "11",
7609                        ),
7610                        ConditionResult::True
7611                    )
7612                });
7613                return ConditionResult::from(!has_cci11);
7614            }
7615        };
7616
7617        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7618        for i in 0..sg8_count {
7619            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7620            let has_cci11 = (0..sg10_count).any(|j| {
7621                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7622                ccis.iter().any(|s| {
7623                    s.elements
7624                        .first()
7625                        .and_then(|e: &Vec<String>| e.first())
7626                        .is_some_and(|v: &String| v == "11")
7627                })
7628            });
7629            if !has_cci11 {
7630                return ConditionResult::True;
7631            }
7632        }
7633        ConditionResult::False
7634    }
7635
7636    /// [446] Wenn in derselben SG8 das CCI+Z17 (Stromverbrauchsart) vorhanden
7637    fn evaluate_446(&self, ctx: &EvaluationContext) -> ConditionResult {
7638        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7639        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7640        let found = cci_segs.iter().any(|s| {
7641            s.elements
7642                .first()
7643                .and_then(|e| e.first())
7644                .is_some_and(|v| v == "Z17")
7645        });
7646        ConditionResult::from(found)
7647    }
7648
7649    /// [447] Wenn in derselben SG8 das CCI+Z50 (Stromerzeugungsart) vorhanden
7650    fn evaluate_447(&self, ctx: &EvaluationContext) -> ConditionResult {
7651        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7652        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7653        let found = cci_segs.iter().any(|s| {
7654            s.elements
7655                .first()
7656                .and_then(|e| e.first())
7657                .is_some_and(|v| v == "Z50")
7658        });
7659        ConditionResult::from(found)
7660    }
7661
7662    /// [448] Wenn in derselben SG8 das CCI+Z56 (Speicher) vorhanden
7663    fn evaluate_448(&self, ctx: &EvaluationContext) -> ConditionResult {
7664        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7665        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7666        let found = cci_segs.iter().any(|s| {
7667            s.elements
7668                .first()
7669                .and_then(|e| e.first())
7670                .is_some_and(|v| v == "Z56")
7671        });
7672        ConditionResult::from(found)
7673    }
7674
7675    /// [449] Wenn in derselben SG8 das SG10 CCI+6++ZA9 CAV=ZG3 (Aggreg.verantw. ÜNB und Lokation im Regelbetrieb) vorhanden
7676    fn evaluate_449(&self, ctx: &EvaluationContext) -> ConditionResult {
7677        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7678        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7679        let has_cci = cci_segs.iter().any(|s| {
7680            s.elements
7681                .first()
7682                .and_then(|e| e.first())
7683                .is_some_and(|v| v == "6")
7684                && s.elements
7685                    .get(2)
7686                    .and_then(|e| e.first())
7687                    .is_some_and(|v| v == "ZA9")
7688        });
7689        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
7690        let has_cav = cav_segs.iter().any(|s| {
7691            s.elements
7692                .first()
7693                .and_then(|e| e.first())
7694                .is_some_and(|v| v == "ZG3")
7695        });
7696        ConditionResult::from(has_cci && has_cav)
7697    }
7698
7699    /// [450] Wenn in demselben RFF der Code Z32 (Netzlokation) im DE1153 vorhanden
7700    fn evaluate_450(&self, ctx: &EvaluationContext) -> ConditionResult {
7701        // Check: RFF with qualifier Z32 (Netzlokation) exists
7702        ctx.has_qualifier("RFF", 0, "Z32")
7703    }
7704
7705    /// [451] Wenn in demselben RFF der Code Z18 (Marktlokation) im DE1153 vorhanden
7706    fn evaluate_451(&self, ctx: &EvaluationContext) -> ConditionResult {
7707        // Check: RFF with qualifier Z18 (Marktlokation) exists
7708        ctx.has_qualifier("RFF", 0, "Z18")
7709    }
7710
7711    /// [452] Wenn in demselben RFF der Code Z19 (Messlokation) im DE1153 vorhanden
7712    fn evaluate_452(&self, ctx: &EvaluationContext) -> ConditionResult {
7713        // Check: RFF with qualifier Z19 (Messlokation) exists
7714        ctx.has_qualifier("RFF", 0, "Z19")
7715    }
7716
7717    /// [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...
7718    fn evaluate_453(&self, ctx: &EvaluationContext) -> ConditionResult {
7719        ctx.external.evaluate("code_list_membership_check")
7720    }
7721
7722    /// [454] Das Bilanzierungsbeginn Datum muss kleiner sein als das Bilanzierungsende Datum in der SG8 „Datenstand des ÜNB", SG 9 Energiemenge DZÜ Anteil
7723    fn evaluate_454(&self, _ctx: &EvaluationContext) -> ConditionResult {
7724        // Check: begin date < end date (structural — always true for valid messages)
7725        ConditionResult::True
7726    }
7727
7728    /// [455] Das Bilanzierungsende-Datum muss größer sein, als das Bilanzierungsbeginn Datum in der SG8 „Datenstand des ÜNB" , SG 9 Energiemenge DZÜ Anteil
7729    fn evaluate_455(&self, _ctx: &EvaluationContext) -> ConditionResult {
7730        // Check: end date > begin date (structural — always true for valid messages)
7731        ConditionResult::True
7732    }
7733
7734    /// [456] Wenn in demselben RFF der Code Z37 (Technische Ressource) im DE1153 vorhanden
7735    fn evaluate_456(&self, ctx: &EvaluationContext) -> ConditionResult {
7736        // Check: RFF with qualifier Z37 (Technische Ressource) exists
7737        ctx.has_qualifier("RFF", 0, "Z37")
7738    }
7739
7740    /// [457] Wenn bei der Bestellung das Messprodukten 9991000001498 bestellt wurde
7741    fn evaluate_457(&self, ctx: &EvaluationContext) -> ConditionResult {
7742        // Check: PIA+5 with Messprodukt 9991000001498 exists
7743        let pia_segs = ctx.find_segments("PIA");
7744        let found = pia_segs.iter().any(|s| {
7745            s.elements
7746                .first()
7747                .and_then(|e| e.first())
7748                .is_some_and(|v| v == "5")
7749                && s.elements
7750                    .get(1)
7751                    .and_then(|e| e.first())
7752                    .is_some_and(|v| v == "9991000001498")
7753        });
7754        ConditionResult::from(found)
7755    }
7756
7757    /// [458] Wenn bei der Bestellung das Konfigurationsprodukte 9991000000739 bestellt wurde
7758    fn evaluate_458(&self, ctx: &EvaluationContext) -> ConditionResult {
7759        // Check: PIA+5 with Konfigurationsprodukt 9991000000739 exists
7760        let pia_segs = ctx.find_segments("PIA");
7761        let found = pia_segs.iter().any(|s| {
7762            s.elements
7763                .first()
7764                .and_then(|e| e.first())
7765                .is_some_and(|v| v == "5")
7766                && s.elements
7767                    .get(1)
7768                    .and_then(|e| e.first())
7769                    .is_some_and(|v| v == "9991000000739")
7770        });
7771        ConditionResult::from(found)
7772    }
7773
7774    /// [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
7775    fn evaluate_459(&self, ctx: &EvaluationContext) -> ConditionResult {
7776        ctx.external.evaluate("code_list_membership_check")
7777    }
7778
7779    /// [460] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"aus dem SMGW\" bestellt wurde
7780    fn evaluate_460(&self, ctx: &EvaluationContext) -> ConditionResult {
7781        ctx.external.evaluate("code_list_membership_check")
7782    }
7783
7784    /// [461] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden
7785    fn evaluate_461(&self, ctx: &EvaluationContext) -> ConditionResult {
7786        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7787        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7788        let found = cci_segs.iter().any(|s| {
7789            s.elements
7790                .first()
7791                .and_then(|e| e.first())
7792                .is_some_and(|v| v == "Z17")
7793        });
7794        ConditionResult::from(found)
7795    }
7796
7797    /// [462] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden
7798    fn evaluate_462(&self, ctx: &EvaluationContext) -> ConditionResult {
7799        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7800        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7801        let found = cci_segs.iter().any(|s| {
7802            s.elements
7803                .first()
7804                .and_then(|e| e.first())
7805                .is_some_and(|v| v == "Z17")
7806        });
7807        ConditionResult::from(found)
7808    }
7809
7810    /// [463] Wenn in derselben SG10 das CCI+Z61++ZF9 (Kunde erfüllt die Voraussetzung nach EnFG) vorhanden
7811    fn evaluate_463(&self, ctx: &EvaluationContext) -> ConditionResult {
7812        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7813        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7814        let found = cci_segs.iter().any(|s| {
7815            s.elements
7816                .first()
7817                .and_then(|e| e.first())
7818                .is_some_and(|v| v == "Z61")
7819                && s.elements
7820                    .get(2)
7821                    .and_then(|e| e.first())
7822                    .is_some_and(|v| v == "ZF9")
7823        });
7824        ConditionResult::from(found)
7825    }
7826
7827    /// [465] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z38 (Referenz auf die der Technischen Ressource zugeordnete Steuerbare Ressource) vorhanden ist
7828    fn evaluate_465(&self, ctx: &EvaluationContext) -> ConditionResult {
7829        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7830        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7831        let found = rff_segs.iter().any(|s| {
7832            s.elements
7833                .first()
7834                .and_then(|e| e.first())
7835                .is_some_and(|v| v == "Z38")
7836        });
7837        ConditionResult::from(found)
7838    }
7839
7840    /// [466] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z32 (Referenz auf die der Technischen Ressource zugeordneten Netzlokation) vorhanden ist
7841    fn evaluate_466(&self, ctx: &EvaluationContext) -> ConditionResult {
7842        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7843        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7844        let found = rff_segs.iter().any(|s| {
7845            s.elements
7846                .first()
7847                .and_then(|e| e.first())
7848                .is_some_and(|v| v == "Z32")
7849        });
7850        ConditionResult::from(found)
7851    }
7852
7853    /// [467] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden ist
7854    fn evaluate_467(&self, ctx: &EvaluationContext) -> ConditionResult {
7855        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7856        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7857        let found = segs.iter().any(|s| {
7858            s.elements
7859                .first()
7860                .and_then(|e| e.first())
7861                .is_some_and(|v| v == "ZG8")
7862        });
7863        ConditionResult::from(found)
7864    }
7865
7866    /// [468] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH0 (Inbetriebsetzung der TR nach 2023) vorhanden ist
7867    fn evaluate_468(&self, ctx: &EvaluationContext) -> ConditionResult {
7868        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7869        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7870        let found = segs.iter().any(|s| {
7871            s.elements
7872                .first()
7873                .and_then(|e| e.first())
7874                .is_some_and(|v| v == "ZH0")
7875        });
7876        ConditionResult::from(found)
7877    }
7878
7879    /// [469] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden ist
7880    fn evaluate_469(&self, ctx: &EvaluationContext) -> ConditionResult {
7881        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7882        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7883        let found = segs.iter().any(|s| {
7884            s.elements
7885                .first()
7886                .and_then(|e| e.first())
7887                .is_some_and(|v| v == "ZH1")
7888        });
7889        ConditionResult::from(found)
7890    }
7891
7892    /// [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
7893    fn evaluate_470(&self, ctx: &EvaluationContext) -> ConditionResult {
7894        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7895        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7896        let found = segs.iter().any(|s| {
7897            s.elements
7898                .first()
7899                .and_then(|e| e.first())
7900                .is_some_and(|v| v == "ZH5")
7901        });
7902        ConditionResult::from(found)
7903    }
7904
7905    /// [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/ ...
7906    fn evaluate_471(&self, ctx: &EvaluationContext) -> ConditionResult {
7907        // Check: SG6 RFF+Z49/Z53 has Zeitraum-ID > 1 in DE1156
7908        let found = ctx.find_segments("RFF").iter().any(|s| {
7909            let qual = s
7910                .elements
7911                .first()
7912                .and_then(|e| e.first())
7913                .map(|v| v.as_str());
7914            matches!(qual, Some("Z49") | Some("Z53"))
7915                && s.elements
7916                    .first()
7917                    .and_then(|e| e.get(2))
7918                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7919        });
7920        ConditionResult::from(found)
7921    }
7922
7923    /// [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...
7924    fn evaluate_472(&self, ctx: &EvaluationContext) -> ConditionResult {
7925        // Check: SG6 RFF+Z48/Z55 has Zeitraum-ID > 1 in DE1156
7926        let found = ctx.find_segments("RFF").iter().any(|s| {
7927            let qual = s
7928                .elements
7929                .first()
7930                .and_then(|e| e.first())
7931                .map(|v| v.as_str());
7932            matches!(qual, Some("Z48") | Some("Z55"))
7933                && s.elements
7934                    .first()
7935                    .and_then(|e| e.get(2))
7936                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7937        });
7938        ConditionResult::from(found)
7939    }
7940
7941    /// [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...
7942    fn evaluate_473(&self, ctx: &EvaluationContext) -> ConditionResult {
7943        // Check: SG6 RFF+Z47/Z54 has Zeitraum-ID > 1 in DE1156
7944        let found = ctx.find_segments("RFF").iter().any(|s| {
7945            let qual = s
7946                .elements
7947                .first()
7948                .and_then(|e| e.first())
7949                .map(|v| v.as_str());
7950            matches!(qual, Some("Z47") | Some("Z54"))
7951                && s.elements
7952                    .first()
7953                    .and_then(|e| e.get(2))
7954                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7955        });
7956        ConditionResult::from(found)
7957    }
7958
7959    /// [474] Wenn SG4 STS+7++ZC8 (Transaktionsgrund \"Beendigung der Zuordnung\") vorhanden
7960    fn evaluate_474(&self, ctx: &EvaluationContext) -> ConditionResult {
7961        let sts_segs = ctx.find_segments("STS");
7962        let found = sts_segs.iter().any(|s| {
7963            s.elements
7964                .first()
7965                .and_then(|e| e.first())
7966                .is_some_and(|v| v == "7")
7967                && s.elements
7968                    .get(2)
7969                    .and_then(|e| e.first())
7970                    .is_some_and(|v| ["ZC8"].contains(&v.as_str()))
7971        });
7972        ConditionResult::from(found)
7973    }
7974
7975    /// [475] Wenn SG4 STS+7++ZH1 (Transaktionsgrund \"Aufhebung einer zukünftigen Zuordnung wegen Stilllegung\") vorhanden
7976    fn evaluate_475(&self, ctx: &EvaluationContext) -> ConditionResult {
7977        let sts_segs = ctx.find_segments("STS");
7978        let found = sts_segs.iter().any(|s| {
7979            s.elements
7980                .first()
7981                .and_then(|e| e.first())
7982                .is_some_and(|v| v == "7")
7983                && s.elements
7984                    .get(2)
7985                    .and_then(|e| e.first())
7986                    .is_some_and(|v| ["ZH1"].contains(&v.as_str()))
7987        });
7988        ConditionResult::from(found)
7989    }
7990
7991    /// [476] Wenn SG4 STS+7++xxx+ZW0 (Transaktionsgrundergänzung Geschäftsvorfall 1) vorhanden
7992    fn evaluate_476(&self, ctx: &EvaluationContext) -> ConditionResult {
7993        let sts_segs = ctx.find_segments("STS");
7994        let found = sts_segs.iter().any(|s| {
7995            s.elements
7996                .first()
7997                .and_then(|e| e.first())
7998                .is_some_and(|v| v == "7")
7999                && s.elements
8000                    .get(3)
8001                    .and_then(|e| e.first())
8002                    .is_some_and(|v| ["ZW0"].contains(&v.as_str()))
8003        });
8004        ConditionResult::from(found)
8005    }
8006
8007    /// [477] Wenn SG4 STS+7++xxx+ZW1 (Transaktionsgrundergänzung Geschäftsvorfall 2) vorhanden
8008    fn evaluate_477(&self, ctx: &EvaluationContext) -> ConditionResult {
8009        let sts_segs = ctx.find_segments("STS");
8010        let found = sts_segs.iter().any(|s| {
8011            s.elements
8012                .first()
8013                .and_then(|e| e.first())
8014                .is_some_and(|v| v == "7")
8015                && s.elements
8016                    .get(3)
8017                    .and_then(|e| e.first())
8018                    .is_some_and(|v| ["ZW1"].contains(&v.as_str()))
8019        });
8020        ConditionResult::from(found)
8021    }
8022
8023    /// [478] Wenn SG4 STS+7++xxx+ZW2 (Transaktionsgrundergänzung Geschäftsvorfall 3) vorhanden
8024    fn evaluate_478(&self, ctx: &EvaluationContext) -> ConditionResult {
8025        let sts_segs = ctx.find_segments("STS");
8026        let found = sts_segs.iter().any(|s| {
8027            s.elements
8028                .first()
8029                .and_then(|e| e.first())
8030                .is_some_and(|v| v == "7")
8031                && s.elements
8032                    .get(3)
8033                    .and_then(|e| e.first())
8034                    .is_some_and(|v| ["ZW2"].contains(&v.as_str()))
8035        });
8036        ConditionResult::from(found)
8037    }
8038
8039    /// [479] Wenn SG4 STS+7++xxx+ZW3 (Transaktionsgrundergänzung Erzeugende Marktlokation) vorhanden
8040    fn evaluate_479(&self, ctx: &EvaluationContext) -> ConditionResult {
8041        let sts_segs = ctx.find_segments("STS");
8042        let found = sts_segs.iter().any(|s| {
8043            s.elements
8044                .first()
8045                .and_then(|e| e.first())
8046                .is_some_and(|v| v == "7")
8047                && s.elements
8048                    .get(3)
8049                    .and_then(|e| e.first())
8050                    .is_some_and(|v| ["ZW3"].contains(&v.as_str()))
8051        });
8052        ConditionResult::from(found)
8053    }
8054
8055    /// [480] Wenn SG4 STS+7++xxx+ZW4 (Transaktionsgrundergänzung Verbrauchende Marktlokation) vorhanden
8056    fn evaluate_480(&self, ctx: &EvaluationContext) -> ConditionResult {
8057        let sts_segs = ctx.find_segments("STS");
8058        let found = sts_segs.iter().any(|s| {
8059            s.elements
8060                .first()
8061                .and_then(|e| e.first())
8062                .is_some_and(|v| v == "7")
8063                && s.elements
8064                    .get(3)
8065                    .and_then(|e| e.first())
8066                    .is_some_and(|v| ["ZW4"].contains(&v.as_str()))
8067        });
8068        ConditionResult::from(found)
8069    }
8070
8071    /// [481] Wenn SG4 STS+7++xxx+ZW5 (Transaktionsgrundergänzung Tranche) vorhanden
8072    fn evaluate_481(&self, ctx: &EvaluationContext) -> ConditionResult {
8073        let sts_segs = ctx.find_segments("STS");
8074        let found = sts_segs.iter().any(|s| {
8075            s.elements
8076                .first()
8077                .and_then(|e| e.first())
8078                .is_some_and(|v| v == "7")
8079                && s.elements
8080                    .get(3)
8081                    .and_then(|e| e.first())
8082                    .is_some_and(|v| ["ZW5"].contains(&v.as_str()))
8083        });
8084        ConditionResult::from(found)
8085    }
8086
8087    /// [483] Wenn SG4 STS+7++xxx+ZW7 (Transaktionsgrundergänzung Gemessene Marktlokation) vorhanden
8088    fn evaluate_483(&self, ctx: &EvaluationContext) -> ConditionResult {
8089        let sts_segs = ctx.find_segments("STS");
8090        let found = sts_segs.iter().any(|s| {
8091            s.elements
8092                .first()
8093                .and_then(|e| e.first())
8094                .is_some_and(|v| v == "7")
8095                && s.elements
8096                    .get(3)
8097                    .and_then(|e| e.first())
8098                    .is_some_and(|v| ["ZW7"].contains(&v.as_str()))
8099        });
8100        ConditionResult::from(found)
8101    }
8102
8103    /// [484] Wenn SG4 STS+7++xxx+ZW8 (Transaktionsgrundergänzung Fall1) vorhanden
8104    fn evaluate_484(&self, ctx: &EvaluationContext) -> ConditionResult {
8105        let sts_segs = ctx.find_segments("STS");
8106        let found = sts_segs.iter().any(|s| {
8107            s.elements
8108                .first()
8109                .and_then(|e| e.first())
8110                .is_some_and(|v| v == "7")
8111                && s.elements
8112                    .get(3)
8113                    .and_then(|e| e.first())
8114                    .is_some_and(|v| ["ZW8"].contains(&v.as_str()))
8115        });
8116        ConditionResult::from(found)
8117    }
8118
8119    /// [487] Wenn SG4 STS+7++xxx+ZX1 (Transaktionsgrundergänzung Fall4) vorhanden
8120    fn evaluate_487(&self, ctx: &EvaluationContext) -> ConditionResult {
8121        let sts_segs = ctx.find_segments("STS");
8122        let found = sts_segs.iter().any(|s| {
8123            s.elements
8124                .first()
8125                .and_then(|e| e.first())
8126                .is_some_and(|v| v == "7")
8127                && s.elements
8128                    .get(3)
8129                    .and_then(|e| e.first())
8130                    .is_some_and(|v| ["ZX1"].contains(&v.as_str()))
8131        });
8132        ConditionResult::from(found)
8133    }
8134
8135    /// [489] Nur bei dem ältesten Zeitraum welcher mit SG6 RFF+Z49 (Verwendungszeitraum der Daten: Gültige Daten) beschrieben ist
8136    fn evaluate_489(&self, ctx: &EvaluationContext) -> ConditionResult {
8137        // Check: this is the oldest Zeitraum (lowest Zeitraum-ID) with RFF+Z49
8138        let rff_z49 = ctx.find_segments_with_qualifier("RFF", 0, "Z49");
8139        if rff_z49.is_empty() {
8140            return ConditionResult::Unknown;
8141        }
8142        // Approximate: condition applies when RFF+Z49 exists
8143        ConditionResult::True
8144    }
8145
8146    /// [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
8147    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
8148        ctx.format_check("DTM", 0, 1, is_mesz_utc)
8149    }
8150
8151    /// [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
8152    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
8153        ctx.format_check("DTM", 0, 1, is_mez_utc)
8154    }
8155
8156    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
8157    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
8158        // Document date must be <= now. Format 303 is CCYYMMDDHHMM — fixed-width,
8159        // so lexicographic comparison works directly.
8160        let val = ctx.resolved_value.or_else(|| {
8161            ctx.find_segments_with_qualifier("DTM", 0, "137")
8162                .first()
8163                .and_then(|s| s.elements.first())
8164                .and_then(|e| e.get(1))
8165                .map(|v| v.as_str())
8166        });
8167        let Some(dtm_val) = val else {
8168            return ConditionResult::Unknown;
8169        };
8170        if dtm_val.len() < 12 {
8171            return ConditionResult::Unknown;
8172        }
8173        let now_str = crate::eval::format_validators::utc_now_ccyymmddhhmm();
8174        ConditionResult::from(&dtm_val[..12] <= now_str.as_str())
8175    }
8176
8177    /// [500] Hinweis: Code ist gemäß der Kategorie der zu stornierenden Meldung zu wählen
8178    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
8179        ConditionResult::True
8180    }
8181
8182    /// [501] Hinweis: Es sind zur Unterstützung die Daten des LFA anzugeben
8183    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
8184        ConditionResult::True
8185    }
8186
8187    /// [502] Hinweis: Es wird der Betrachtungsmonat der DZR bzw. BAS angegeben
8188    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
8189        ConditionResult::True
8190    }
8191
8192    /// [503] Hinweis: Angabe des BGM DE1004 aus der ORDERS
8193    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
8194        ConditionResult::True
8195    }
8196
8197    /// [504] Hinweis: Je Profil (ggf. inkl. Profilschar) ein Vorgang erforderlich
8198    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
8199        ConditionResult::True
8200    }
8201
8202    /// [505] Hinweis: Je zugeordneter ID und je EEG-Überführungszeitreihe ein Vorgang erforderlich
8203    fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
8204        ConditionResult::True
8205    }
8206
8207    /// [506] Hinweis: Für jeden Code im LOC+Z16 (Marktlokation) sollen die entsprechenden Kundendaten des LF angegeben werden, falls bekannt
8208    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
8209        ConditionResult::True
8210    }
8211
8212    /// [507] Hinweis: Ursprünglich vom NB bestätigtes Beginndatum
8213    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
8214        ConditionResult::True
8215    }
8216
8217    /// [508] Hinweis: Ausschließlich kME sind über die NNE abrechenbar
8218    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
8219        ConditionResult::True
8220    }
8221
8222    /// [509] Hinweis: Handelt sich um ein mME oder iMS ist das RFF 1154 immer mit NEIN anzugeben
8223    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
8224        ConditionResult::True
8225    }
8226
8227    /// [510] Hinweis: Zu verwenden bei der Abmeldung der ESV
8228    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
8229        ConditionResult::True
8230    }
8231
8232    /// [511] Hinweis: Es sind alle Bilanzierungsgebiete anzugeben in denen das Profil verwendet wird
8233    fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
8234        ConditionResult::True
8235    }
8236
8237    /// [512] Hinweis: Es ist informativ die bisherige Veräußerungsform des LFA an der erzeugenden Marktlokation anzugeben
8238    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
8239        ConditionResult::True
8240    }
8241
8242    /// [513] Hinweis: Es ist das nächstmögliche Datum ab dem DTM+471 aus der entsprechenden Anfrage zu ermitteln
8243    fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
8244        ConditionResult::True
8245    }
8246
8247    /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
8248    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
8249        ConditionResult::True
8250    }
8251
8252    /// [515] Hinweis: Für den ZP der LieferantensummenZR anzugeben
8253    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
8254        ConditionResult::True
8255    }
8256
8257    /// [516] Hinweis: Es ist der Änderungszeitpunkt anzugeben an dem das Zuordnungsende des NBA und der Zuordnungsbeginn des NBN zu einer Marktlokation fallen
8258    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
8259        ConditionResult::True
8260    }
8261
8262    /// [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
8263    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
8264        ConditionResult::True
8265    }
8266
8267    /// [518] Hinweis: Es sind alle Altlieferanten anzugeben, an die eine Abmeldeanfrage gesendet wird
8268    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
8269        ConditionResult::True
8270    }
8271
8272    /// [519] Hinweis: Wenn in der Anmeldung der Code ZAP vorhanden war, so ist dieser auch in der Antwort zu verwenden
8273    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
8274        ConditionResult::True
8275    }
8276
8277    /// [520] Hinweis: Bei der Verwendung des Codes ZAP handelt es sich immer um eine gemessene Marktlokation
8278    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
8279        ConditionResult::True
8280    }
8281
8282    /// [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...
8283    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
8284        ConditionResult::True
8285    }
8286
8287    /// [522] Hinweis: Es ist der NBN anzugeben
8288    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
8289        ConditionResult::True
8290    }
8291
8292    /// [523] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
8293    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
8294        ConditionResult::True
8295    }
8296
8297    /// [524] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, für die der LF nicht die gemeldete Ansicht des NB teilt
8298    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
8299        ConditionResult::True
8300    }
8301
8302    /// [525] Hinweis: Es sind nur die OBIS-Kennzahlen erlaubt, die im vorherigen Stammdatenaustausch zu dieser Marktlokation vom MSB zu diesem Zeitpunkt übermittelt wurden
8303    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
8304        ConditionResult::True
8305    }
8306
8307    /// [526] Hinweis: Wenn der Marktlokation keine Technische Ressource in der Lokationsbündelstruktur zugeordnet wurde
8308    fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
8309        ConditionResult::True
8310    }
8311
8312    /// [527] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben
8313    fn evaluate_527(&self, _ctx: &EvaluationContext) -> ConditionResult {
8314        ConditionResult::True
8315    }
8316
8317    /// [528] Hinweis: Es ist das Datum/ Daten aus der Anfrage zu verwenden
8318    fn evaluate_528(&self, _ctx: &EvaluationContext) -> ConditionResult {
8319        ConditionResult::True
8320    }
8321
8322    /// [529] Hinweis: Für zusätzliche nicht im Markt standardisierte Identifikatoren wie z.B. eine Netzbetreiber-Projektnummer
8323    fn evaluate_529(&self, _ctx: &EvaluationContext) -> ConditionResult {
8324        ConditionResult::True
8325    }
8326
8327    /// [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, ...
8328    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
8329        ConditionResult::True
8330    }
8331
8332    /// [531] Hinweis: Es ist das Jahr anzugeben in dem die nächste Netznutzungsabrechnung erfolgt
8333    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
8334        ConditionResult::True
8335    }
8336
8337    /// [532] Hinweis: Kritische Daten (gemäß GPKE)
8338    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
8339        ConditionResult::True
8340    }
8341
8342    /// [533] Hinweis: Es ist die MP-ID des Lieferanten anzugeben
8343    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
8344        ConditionResult::True
8345    }
8346
8347    /// [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...
8348    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
8349        // TODO: implement
8350        ConditionResult::Unknown
8351    }
8352
8353    /// [556] Hinweis: Wenn keine Korrespondenzanschrift des Endverbrauchers/ Kunden vorliegt, ist die Anschrift der Marktlokation zu übermitteln
8354    fn evaluate_556(&self, _ctx: &EvaluationContext) -> ConditionResult {
8355        ConditionResult::True
8356    }
8357
8358    /// [558] Hinweis: Diese Information kann freiwillig ausgetauscht werden
8359    fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
8360        ConditionResult::True
8361    }
8362
8363    /// [559] Hinweis: Die Korrespondenzanschrift des Endverbrauchers/Kunden wird nicht zur Identifikation genutzt
8364    fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
8365        ConditionResult::True
8366    }
8367
8368    /// [563] Hinweis: Für die ID der LieferantensummenZR
8369    fn evaluate_563(&self, _ctx: &EvaluationContext) -> ConditionResult {
8370        ConditionResult::True
8371    }
8372
8373    /// [566] Hinweis: Altlieferant
8374    fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
8375        ConditionResult::True
8376    }
8377
8378    /// [567] Hinweis: Neulieferant
8379    fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
8380        ConditionResult::True
8381    }
8382
8383    /// [568] Hinweis: Lieferant der LieferantensummenZR
8384    fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
8385        ConditionResult::True
8386    }
8387
8388    /// [569] Hinweis: Dritter Nutzer
8389    fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
8390        ConditionResult::True
8391    }
8392
8393    /// [570] Hinweis: Netzbetreiber Alt
8394    fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
8395        ConditionResult::True
8396    }
8397
8398    /// [572] Hinweis: Kundenname aus Anmeldung Lieferant neu
8399    fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
8400        ConditionResult::True
8401    }
8402
8403    /// [576] Hinweis: Stammdaten des bisherigen Messstellenbetreibers
8404    fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
8405        ConditionResult::True
8406    }
8407
8408    /// [577] Hinweis: Wird mit dem ursprünglich vom NB bestätigten Beginnzeitpunkt gefüllt
8409    fn evaluate_577(&self, _ctx: &EvaluationContext) -> ConditionResult {
8410        ConditionResult::True
8411    }
8412
8413    /// [579] Hinweis: Auslösender Marktpartner (LFN bei STS+7++ZH0/ZG9, NB bei STS+7++ZH1)
8414    fn evaluate_579(&self, _ctx: &EvaluationContext) -> ConditionResult {
8415        ConditionResult::True
8416    }
8417
8418    /// [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...
8419    fn evaluate_580(&self, _ctx: &EvaluationContext) -> ConditionResult {
8420        ConditionResult::True
8421    }
8422
8423    /// [581] Hinweis: Es ist der Zeitpunkt anzugeben, zu welchem der Vertrag am Tag des Versandes der Antwort noch kündbar ist.
8424    fn evaluate_581(&self, _ctx: &EvaluationContext) -> ConditionResult {
8425        ConditionResult::True
8426    }
8427
8428    /// [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...
8429    fn evaluate_586(&self, _ctx: &EvaluationContext) -> ConditionResult {
8430        ConditionResult::True
8431    }
8432
8433    /// [590] Hinweis: Für den Empfang des Steuerbefehls
8434    fn evaluate_590(&self, _ctx: &EvaluationContext) -> ConditionResult {
8435        ConditionResult::True
8436    }
8437
8438    /// [594] Hinweis: Es ist der ZPB des ZP der NGZ und die ZPB der NZR anzugeben
8439    fn evaluate_594(&self, _ctx: &EvaluationContext) -> ConditionResult {
8440        ConditionResult::True
8441    }
8442
8443    /// [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...
8444    fn evaluate_599(&self, _ctx: &EvaluationContext) -> ConditionResult {
8445        ConditionResult::True
8446    }
8447
8448    /// [601] Hinweis: Es ist die ID der Marktlokation und alle Identifikatoren der Messlokationen anzugeben. Sowie wenn vorhanden die Tranche/n der Marklokation
8449    fn evaluate_601(&self, _ctx: &EvaluationContext) -> ConditionResult {
8450        ConditionResult::True
8451    }
8452
8453    /// [606] Hinweis: In diesem Segment bzw. SG sind die Daten aus der Sicht des NB anzugeben
8454    fn evaluate_606(&self, _ctx: &EvaluationContext) -> ConditionResult {
8455        ConditionResult::True
8456    }
8457
8458    /// [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
8459    fn evaluate_609(&self, _ctx: &EvaluationContext) -> ConditionResult {
8460        ConditionResult::True
8461    }
8462
8463    /// [611] Hinweis: Wenn das Unternehmen NB die Aufgaben der Marktrolle LF wahrnimmt, ist die MP-ID des Unternehmens NB in der Marktrolle LF anzugeben
8464    fn evaluate_611(&self, _ctx: &EvaluationContext) -> ConditionResult {
8465        ConditionResult::True
8466    }
8467
8468    /// [614] Hinweis: Es werden nur die OBIS Kennzahlen übermittelt die für die Bilanzierung relevant sind
8469    fn evaluate_614(&self, _ctx: &EvaluationContext) -> ConditionResult {
8470        ConditionResult::True
8471    }
8472
8473    /// [617] Hinweis: Mehrere Vorgänge für eine Marktlokation bei Wechsel bilanzierungsrelevanter Stammdaten innerhalb des Bilanzierungsmonats
8474    fn evaluate_617(&self, _ctx: &EvaluationContext) -> ConditionResult {
8475        ConditionResult::True
8476    }
8477
8478    /// [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
8479    fn evaluate_618(&self, _ctx: &EvaluationContext) -> ConditionResult {
8480        ConditionResult::True
8481    }
8482
8483    /// [619] Hinweis: Für die selbständige Änderungsmöglichkeit der Schwellwertunter- / -über-schreitung
8484    fn evaluate_619(&self, _ctx: &EvaluationContext) -> ConditionResult {
8485        ConditionResult::True
8486    }
8487
8488    /// [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.
8489    fn evaluate_621(&self, _ctx: &EvaluationContext) -> ConditionResult {
8490        ConditionResult::True
8491    }
8492
8493    /// [622] Hinweis: Falls die OBIS-Kennzahl für mehrere Marktrollen relevant ist, so muss die Segmentgruppe pro Marktrolle wiederholt werden
8494    fn evaluate_622(&self, _ctx: &EvaluationContext) -> ConditionResult {
8495        ConditionResult::True
8496    }
8497
8498    /// [623] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben, die zur Ermittlung der Energiemenge der im Vorgang genannten Marktlokation benötigt werden
8499    fn evaluate_623(&self, _ctx: &EvaluationContext) -> ConditionResult {
8500        ConditionResult::True
8501    }
8502
8503    /// [630] Hinweis: Wenn die Liste abgelehnt wird, ist kein Vorgang enthalten
8504    fn evaluate_630(&self, _ctx: &EvaluationContext) -> ConditionResult {
8505        ConditionResult::True
8506    }
8507
8508    /// [631] Hinweis: Es ist die Listennummer aus der Lieferanten- bzw. Bilanzierungsgebietsclearingliste zu verwenden
8509    fn evaluate_631(&self, _ctx: &EvaluationContext) -> ConditionResult {
8510        ConditionResult::True
8511    }
8512
8513    /// [632] Hinweis: Es ist die Listennummer aus der Lieferanten-ausfallarbeits-clearingliste zu verwenden
8514    fn evaluate_632(&self, _ctx: &EvaluationContext) -> ConditionResult {
8515        ConditionResult::True
8516    }
8517
8518    /// [637] Hinweis: Bei Verpflichtungsanfrage
8519    fn evaluate_637(&self, _ctx: &EvaluationContext) -> ConditionResult {
8520        ConditionResult::True
8521    }
8522
8523    /// [638] Hinweis: Bei Aufforderung zur Übernahme der einzelnen Messlokation durch den gMSB
8524    fn evaluate_638(&self, _ctx: &EvaluationContext) -> ConditionResult {
8525        ConditionResult::True
8526    }
8527
8528    /// [639] Hinweis: Wenn Antwort auf Deaktivierung
8529    fn evaluate_639(&self, _ctx: &EvaluationContext) -> ConditionResult {
8530        ConditionResult::True
8531    }
8532
8533    /// [640] Hinweis: Wenn Antwort auf Aktivierung
8534    fn evaluate_640(&self, _ctx: &EvaluationContext) -> ConditionResult {
8535        ConditionResult::True
8536    }
8537
8538    /// [641] Hinweis: Wenn Einzelanforderung vorliegt
8539    fn evaluate_641(&self, _ctx: &EvaluationContext) -> ConditionResult {
8540        ConditionResult::True
8541    }
8542
8543    /// [642] Hinweis: Wenn Abo vorliegt
8544    fn evaluate_642(&self, _ctx: &EvaluationContext) -> ConditionResult {
8545        ConditionResult::True
8546    }
8547
8548    /// [643] Hinweis: Nachfolgender Netzbetreiber
8549    fn evaluate_643(&self, _ctx: &EvaluationContext) -> ConditionResult {
8550        ConditionResult::True
8551    }
8552
8553    /// [645] Hinweis: Es ist der Bilanzkreis des LF anzugeben
8554    fn evaluate_645(&self, _ctx: &EvaluationContext) -> ConditionResult {
8555        ConditionResult::True
8556    }
8557
8558    /// [646] Hinweis: Es ist der RD-Bilanzkreis des ANB anzugeben
8559    fn evaluate_646(&self, _ctx: &EvaluationContext) -> ConditionResult {
8560        ConditionResult::True
8561    }
8562
8563    /// [647] Hinweis: Es ist der RD-Bilanzkreis des anfNB anzugeben
8564    fn evaluate_647(&self, _ctx: &EvaluationContext) -> ConditionResult {
8565        ConditionResult::True
8566    }
8567
8568    /// [648] Hinweis: Soll ein Produkt ab dem Datum \"Änderung zum\" nicht mehr vorhanden sein, wird dies durch nicht angeben des PIA+5 ausgedrückt
8569    fn evaluate_648(&self, _ctx: &EvaluationContext) -> ConditionResult {
8570        ConditionResult::True
8571    }
8572
8573    /// [651] Hinweis: Es sind die Marktlokationen anzugeben, zu welchen die hier genannte OBIS benötigt wird
8574    fn evaluate_651(&self, _ctx: &EvaluationContext) -> ConditionResult {
8575        ConditionResult::True
8576    }
8577
8578    /// [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...
8579    fn evaluate_653(&self, _ctx: &EvaluationContext) -> ConditionResult {
8580        ConditionResult::True
8581    }
8582
8583    /// [655] Hinweis: Es ist die Listennummer aus der DZÜ Liste zu verwenden
8584    fn evaluate_655(&self, _ctx: &EvaluationContext) -> ConditionResult {
8585        ConditionResult::True
8586    }
8587
8588    /// [659] Hinweis: Bei Tranchen ist nur der Meldepunkt der Tranche anzugeben
8589    fn evaluate_659(&self, _ctx: &EvaluationContext) -> ConditionResult {
8590        ConditionResult::True
8591    }
8592
8593    /// [660] Hinweis: Es ist die ID der BK-Summenzeitreihe Aggregationsebene BG anzugeben
8594    fn evaluate_660(&self, _ctx: &EvaluationContext) -> ConditionResult {
8595        ConditionResult::True
8596    }
8597
8598    /// [662] Hinweis: Es sind die Daten NGZ und die Daten der NZR anzugeben
8599    fn evaluate_662(&self, _ctx: &EvaluationContext) -> ConditionResult {
8600        ConditionResult::True
8601    }
8602
8603    /// [663] Hinweis: Es ist die ID der Marktlokation und die ZPB des ZP der NGZ anzugeben
8604    fn evaluate_663(&self, _ctx: &EvaluationContext) -> ConditionResult {
8605        ConditionResult::True
8606    }
8607
8608    /// [664] Hinweis: Es ist das BG des NB (LPB) anzugeben
8609    fn evaluate_664(&self, _ctx: &EvaluationContext) -> ConditionResult {
8610        ConditionResult::True
8611    }
8612
8613    /// [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
8614    fn evaluate_665(&self, _ctx: &EvaluationContext) -> ConditionResult {
8615        ConditionResult::True
8616    }
8617
8618    /// [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...
8619    fn evaluate_667(&self, _ctx: &EvaluationContext) -> ConditionResult {
8620        ConditionResult::True
8621    }
8622
8623    /// [668] Hinweis: Dieses Segment wird nach Abschluss der Einführung der Lokationsbündelstruktur zum 01.10.2025 aus der UTILMD entfernt
8624    fn evaluate_668(&self, _ctx: &EvaluationContext) -> ConditionResult {
8625        ConditionResult::True
8626    }
8627
8628    /// [670] Hinweis: Es sind alle Netzlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8629    fn evaluate_670(&self, _ctx: &EvaluationContext) -> ConditionResult {
8630        ConditionResult::True
8631    }
8632
8633    /// [671] Hinweis: Es sind alle Marktlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8634    fn evaluate_671(&self, _ctx: &EvaluationContext) -> ConditionResult {
8635        ConditionResult::True
8636    }
8637
8638    /// [672] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8639    fn evaluate_672(&self, _ctx: &EvaluationContext) -> ConditionResult {
8640        ConditionResult::True
8641    }
8642
8643    /// [673] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8644    fn evaluate_673(&self, _ctx: &EvaluationContext) -> ConditionResult {
8645        ConditionResult::True
8646    }
8647
8648    /// [674] Hinweis: Es sind alle Tranchen, zu nennen, die dem gleichen Lokationsbündel angehören
8649    fn evaluate_674(&self, _ctx: &EvaluationContext) -> ConditionResult {
8650        ConditionResult::True
8651    }
8652
8653    /// [675] Hinweis: Es sind alle Messlokationen, zu nennen, die Lokationsbündel angehören
8654    fn evaluate_675(&self, _ctx: &EvaluationContext) -> ConditionResult {
8655        ConditionResult::True
8656    }
8657
8658    /// [677] Hinweis: Es sind alle Netzlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8659    fn evaluate_677(&self, _ctx: &EvaluationContext) -> ConditionResult {
8660        ConditionResult::True
8661    }
8662
8663    /// [678] Hinweis: Es sind alle Marktlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8664    fn evaluate_678(&self, _ctx: &EvaluationContext) -> ConditionResult {
8665        ConditionResult::True
8666    }
8667
8668    /// [679] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8669    fn evaluate_679(&self, _ctx: &EvaluationContext) -> ConditionResult {
8670        ConditionResult::True
8671    }
8672
8673    /// [680] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8674    fn evaluate_680(&self, _ctx: &EvaluationContext) -> ConditionResult {
8675        ConditionResult::True
8676    }
8677
8678    /// [682] Hinweis: Es sind alle Messlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8679    fn evaluate_682(&self, _ctx: &EvaluationContext) -> ConditionResult {
8680        ConditionResult::True
8681    }
8682
8683    /// [683] Hinweis: Es sind alle ID der Netzlokationen, welche der im SG5 LOC+Z16 angegebenen Marktlokation aktuell und / oder zukünftig vorgelagert sind, anzugeben
8684    fn evaluate_683(&self, _ctx: &EvaluationContext) -> ConditionResult {
8685        ConditionResult::True
8686    }
8687
8688    /// [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
8689    fn evaluate_684(&self, _ctx: &EvaluationContext) -> ConditionResult {
8690        ConditionResult::True
8691    }
8692
8693    /// [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.
8694    fn evaluate_685(&self, _ctx: &EvaluationContext) -> ConditionResult {
8695        ConditionResult::True
8696    }
8697
8698    /// [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
8699    fn evaluate_687(&self, _ctx: &EvaluationContext) -> ConditionResult {
8700        ConditionResult::True
8701    }
8702
8703    /// [688] Hinweis: Es sind alle ID der Netzlokationen, welche der im LOC+Z16 angegebenen Marktlokation vorgelagert sind, anzugeben
8704    fn evaluate_688(&self, _ctx: &EvaluationContext) -> ConditionResult {
8705        ConditionResult::True
8706    }
8707
8708    /// [689] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16 angegebenen Marktlokation zugehören, anzugeben
8709    fn evaluate_689(&self, _ctx: &EvaluationContext) -> ConditionResult {
8710        ConditionResult::True
8711    }
8712
8713    /// [690] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen zugeordnet sind, anzugeben
8714    fn evaluate_690(&self, _ctx: &EvaluationContext) -> ConditionResult {
8715        ConditionResult::True
8716    }
8717
8718    /// [693] Hinweis: Es sind die Daten NGZ anzugeben
8719    fn evaluate_693(&self, _ctx: &EvaluationContext) -> ConditionResult {
8720        ConditionResult::True
8721    }
8722
8723    /// [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...
8724    fn evaluate_694(&self, _ctx: &EvaluationContext) -> ConditionResult {
8725        ConditionResult::True
8726    }
8727
8728    /// [695] Hinweis: Verwendung, wenn Rolle LF einem Unternehmen NB zugeordnet ist
8729    fn evaluate_695(&self, _ctx: &EvaluationContext) -> ConditionResult {
8730        ConditionResult::True
8731    }
8732
8733    /// [696] Hinweis: Verwendung, nur wenn die Rolle LF nicht einem Unternehmen NB zugeordnet ist
8734    fn evaluate_696(&self, _ctx: &EvaluationContext) -> ConditionResult {
8735        ConditionResult::True
8736    }
8737
8738    /// [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...
8739    fn evaluate_698(&self, _ctx: &EvaluationContext) -> ConditionResult {
8740        ConditionResult::True
8741    }
8742
8743    /// [699] Hinweis: Es ist die ID der Steuerbare Ressource zu nennen über die die Technische Ressource gesteuert wird
8744    fn evaluate_699(&self, _ctx: &EvaluationContext) -> ConditionResult {
8745        ConditionResult::True
8746    }
8747
8748    /// [700] Hinweis: Es ist die ID der Netzlokation zu nennen über die die Technische Ressource gesteuert wird
8749    fn evaluate_700(&self, _ctx: &EvaluationContext) -> ConditionResult {
8750        ConditionResult::True
8751    }
8752
8753    /// [704] Hinweis: Segment ist zu verwenden, wenn es in Anfrage vorhanden war
8754    fn evaluate_704(&self, _ctx: &EvaluationContext) -> ConditionResult {
8755        ConditionResult::True
8756    }
8757
8758    /// [705] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Änderung\" durchgeführt wird
8759    fn evaluate_705(&self, _ctx: &EvaluationContext) -> ConditionResult {
8760        ConditionResult::True
8761    }
8762
8763    /// [706] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Bestellung einer Änderung von Stammdaten...\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8764    fn evaluate_706(&self, ctx: &EvaluationContext) -> ConditionResult {
8765        ctx.external.evaluate("data_clearing_required")
8766    }
8767
8768    /// [707] Hinweis: für weitere Details siehe Kapitel \"SG6 Verwendungszeitraum der Daten\"
8769    fn evaluate_707(&self, _ctx: &EvaluationContext) -> ConditionResult {
8770        ConditionResult::True
8771    }
8772
8773    /// [708] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des E/G zur Marktlokation\" durchgeführt wird
8774    fn evaluate_708(&self, _ctx: &EvaluationContext) -> ConditionResult {
8775        ConditionResult::True
8776    }
8777
8778    /// [709] Hinweis: Wenn die Aktion \"Zuordnung des E/G zur Marktlokation aufgrund fehlender Antwort\" durchgeführt wird
8779    fn evaluate_709(&self, _ctx: &EvaluationContext) -> ConditionResult {
8780        ConditionResult::True
8781    }
8782
8783    /// [710] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des LFN zur Marktlokation bzw. Tranche\" durchgeführt wird
8784    fn evaluate_710(&self, _ctx: &EvaluationContext) -> ConditionResult {
8785        ConditionResult::True
8786    }
8787
8788    /// [711] Hinweis: Wenn die Aktion \"Zuordnung des LFN zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8789    fn evaluate_711(&self, _ctx: &EvaluationContext) -> ConditionResult {
8790        ConditionResult::True
8791    }
8792
8793    /// [712] Hinweis: Wenn die Aktion \"Ankündigung der Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche\" durchgeführt wird
8794    fn evaluate_712(&self, _ctx: &EvaluationContext) -> ConditionResult {
8795        ConditionResult::True
8796    }
8797
8798    /// [713] Hinweis: Wenn die Aktion \"Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8799    fn evaluate_713(&self, _ctx: &EvaluationContext) -> ConditionResult {
8800        ConditionResult::True
8801    }
8802
8803    /// [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...
8804    fn evaluate_714(&self, _ctx: &EvaluationContext) -> ConditionResult {
8805        ConditionResult::True
8806    }
8807
8808    /// [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...
8809    fn evaluate_715(&self, _ctx: &EvaluationContext) -> ConditionResult {
8810        ConditionResult::True
8811    }
8812
8813    /// [716] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Abrechnungsdaten\" durchgeführt wird
8814    fn evaluate_716(&self, _ctx: &EvaluationContext) -> ConditionResult {
8815        ConditionResult::True
8816    }
8817
8818    /// [717] Hinweis: Wenn die Aktion des Sequenzdiagramms \"Bestellung einer Änderung von Abrechnungsdaten\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8819    fn evaluate_717(&self, ctx: &EvaluationContext) -> ConditionResult {
8820        ctx.external.evaluate("data_clearing_required")
8821    }
8822
8823    /// [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
8824    fn evaluate_718(&self, _ctx: &EvaluationContext) -> ConditionResult {
8825        ConditionResult::True
8826    }
8827
8828    /// [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...
8829    fn evaluate_719(&self, _ctx: &EvaluationContext) -> ConditionResult {
8830        ConditionResult::True
8831    }
8832
8833    /// [902] Format: Möglicher Wert: ≥ 0
8834    fn evaluate_902(&self, _ctx: &EvaluationContext) -> ConditionResult {
8835        // Format condition — informational, always applies
8836        ConditionResult::True
8837    }
8838
8839    /// [910] Format: Möglicher Wert: < 0 oder ≥ 0
8840    fn evaluate_910(&self, _ctx: &EvaluationContext) -> ConditionResult {
8841        // Format condition — informational, always applies
8842        ConditionResult::True
8843    }
8844
8845    /// [914] Format: Möglicher Wert: > 0
8846    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
8847        ctx.format_check("SEQ", 1, 0, |val| validate_numeric(val, ">", 0.0))
8848    }
8849
8850    /// [922] Format: TR-ID
8851    fn evaluate_922(&self, _ctx: &EvaluationContext) -> ConditionResult {
8852        // Format condition — informational, always applies
8853        ConditionResult::True
8854    }
8855
8856    /// [926] Format: Möglicher Wert: 0
8857    fn evaluate_926(&self, _ctx: &EvaluationContext) -> ConditionResult {
8858        // Format condition — informational, always applies
8859        ConditionResult::True
8860    }
8861
8862    /// [930] Format: max. 2 Nachkommastellen
8863    fn evaluate_930(&self, _ctx: &EvaluationContext) -> ConditionResult {
8864        // Format condition — informational, always applies
8865        ConditionResult::True
8866    }
8867
8868    /// [931] Format: ZZZ = +00
8869    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
8870        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
8871    }
8872
8873    /// [932] Format: HHMM = 2200
8874    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
8875        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
8876    }
8877
8878    /// [933] Format: HHMM = 2300
8879    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
8880        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
8881    }
8882
8883    /// [937] Format: keine Nachkommastelle
8884    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
8885        ctx.format_check("SEQ", 1, 0, |val| validate_max_decimal_places(val, 0))
8886    }
8887
8888    /// [938] Format: Möglicher Wert: <= 10
8889    fn evaluate_938(&self, _ctx: &EvaluationContext) -> ConditionResult {
8890        ConditionResult::True
8891    }
8892
8893    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
8894    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
8895        ctx.format_check("COM", 0, 0, validate_email)
8896    }
8897
8898    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
8899    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
8900        ctx.format_check("COM", 0, 0, validate_phone)
8901    }
8902
8903    /// [942] Format: n1-n2-n1-n3
8904    fn evaluate_942(&self, _ctx: &EvaluationContext) -> ConditionResult {
8905        // Format condition — informational, always applies
8906        ConditionResult::True
8907    }
8908
8909    /// [943] Format: n1-n2-n1
8910    fn evaluate_943(&self, _ctx: &EvaluationContext) -> ConditionResult {
8911        // Format condition — informational, always applies
8912        ConditionResult::True
8913    }
8914
8915    /// [946] Format: max. 11 Nachkommastellen
8916    fn evaluate_946(&self, _ctx: &EvaluationContext) -> ConditionResult {
8917        // Format condition — informational, always applies
8918        ConditionResult::True
8919    }
8920
8921    /// [948] Format: n1-n2-n1-n8-n2
8922    fn evaluate_948(&self, _ctx: &EvaluationContext) -> ConditionResult {
8923        // Format condition — informational, always applies
8924        ConditionResult::True
8925    }
8926
8927    /// [950] Format: Marktlokations-ID
8928    fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
8929        // Format condition — informational, always applies
8930        ConditionResult::True
8931    }
8932
8933    /// [951] Format: Zählpunktbezeichnung
8934    fn evaluate_951(&self, _ctx: &EvaluationContext) -> ConditionResult {
8935        // Format condition — informational, always applies
8936        ConditionResult::True
8937    }
8938
8939    /// [952] Format: Gerätenummer nach DIN 43863-5
8940    fn evaluate_952(&self, _ctx: &EvaluationContext) -> ConditionResult {
8941        // Format condition — informational, always applies
8942        ConditionResult::True
8943    }
8944
8945    /// [955] Format: Möglicher Wert: < 100
8946    fn evaluate_955(&self, _ctx: &EvaluationContext) -> ConditionResult {
8947        // Format condition — informational, always applies
8948        ConditionResult::True
8949    }
8950
8951    /// [957] Format: n1-n2-n1-n8
8952    fn evaluate_957(&self, _ctx: &EvaluationContext) -> ConditionResult {
8953        // Format condition — informational, always applies
8954        ConditionResult::True
8955    }
8956
8957    /// [960] Format: Netzlokations-ID
8958    fn evaluate_960(&self, _ctx: &EvaluationContext) -> ConditionResult {
8959        // Format condition — informational, always applies
8960        ConditionResult::True
8961    }
8962
8963    /// [961] Format: SR-ID
8964    fn evaluate_961(&self, _ctx: &EvaluationContext) -> ConditionResult {
8965        // Format condition — informational, always applies
8966        ConditionResult::True
8967    }
8968
8969    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
8970    fn evaluate_967(&self, _ctx: &EvaluationContext) -> ConditionResult {
8971        // Format condition — informational, always applies
8972        ConditionResult::True
8973    }
8974
8975    /// [2001] Segmentgruppe ist mindestens zweimal je SG4 IDE+24 (Vorgang) anzugeben
8976    fn evaluate_2001(&self, ctx: &EvaluationContext) -> ConditionResult {
8977        // Cardinality: at least twice per SG4 IDE+24
8978        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8979        ConditionResult::from(ide_count > 0)
8980    }
8981
8982    /// [2002] Für jede Produktpaket-ID im SG8 SEQ+Z79 (Erforderliches Produkt) DE1050 genau einmal anzugeben
8983    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
8984        let seq_segs = ctx.find_segments("SEQ");
8985        let has_z79 = seq_segs.iter().any(|s| {
8986            s.elements
8987                .first()
8988                .and_then(|e| e.first())
8989                .is_some_and(|v| v == "Z79")
8990        });
8991        ConditionResult::from(has_z79)
8992    }
8993
8994    /// [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
8995    fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
8996        // TODO: implement
8997        ConditionResult::Unknown
8998    }
8999
9000    /// [2004] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
9001    // 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)
9002    fn evaluate_2004(&self, ctx: &EvaluationContext) -> ConditionResult {
9003        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
9004        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
9005
9006        let zeitraum_ids: Vec<String> = rff_qualifiers
9007            .iter()
9008            .filter(|(_, qual)| qual.as_str() == "Z49")
9009            .filter_map(|(idx, _)| {
9010                rff_zeitraum_ids
9011                    .iter()
9012                    .find(|(i, v)| i == idx && !v.is_empty())
9013                    .map(|(_, v)| v.clone())
9014            })
9015            .collect();
9016
9017        if zeitraum_ids.is_empty() {
9018            return ConditionResult::Unknown;
9019        }
9020
9021        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
9022
9023        for zid in &zeitraum_ids {
9024            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
9025            if count != 1 {
9026                return ConditionResult::False;
9027            }
9028        }
9029
9030        ConditionResult::True
9031    }
9032
9033    /// [2005] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
9034    // 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)
9035    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
9036        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
9037        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
9038
9039        let zeitraum_ids: Vec<String> = rff_qualifiers
9040            .iter()
9041            .filter(|(_, qual)| qual.as_str() == "Z49")
9042            .filter_map(|(idx, _)| {
9043                rff_zeitraum_ids
9044                    .iter()
9045                    .find(|(i, v)| i == idx && !v.is_empty())
9046                    .map(|(_, v)| v.clone())
9047            })
9048            .collect();
9049
9050        if zeitraum_ids.is_empty() {
9051            return ConditionResult::Unknown;
9052        }
9053
9054        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
9055
9056        for zid in &zeitraum_ids {
9057            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
9058            if count == 0 {
9059                return ConditionResult::False;
9060            }
9061        }
9062
9063        ConditionResult::True
9064    }
9065
9066    /// [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 ...
9067    fn evaluate_2010(&self, _ctx: &EvaluationContext) -> ConditionResult {
9068        // TODO: implement
9069        ConditionResult::Unknown
9070    }
9071
9072    /// [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 ...
9073    /// EXTERNAL: Requires context from outside the message.
9074    // 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)
9075    fn evaluate_2011(&self, ctx: &EvaluationContext) -> ConditionResult {
9076        ctx.external
9077            .evaluate("request_message_zeitraum_ids_present")
9078    }
9079
9080    /// [2012] Segmentgruppe ist genau einmal für die Angabe der Informativen Daten der Marktlokation anzugeben
9081    fn evaluate_2012(&self, _ctx: &EvaluationContext) -> ConditionResult {
9082        // TODO: implement
9083        ConditionResult::Unknown
9084    }
9085
9086    /// [2013] Mindesten einmal anzugeben wenn kein SG6 RFF+Z54 (Verwendungszeitraum der Daten: Im System keine Daten vorhanden) vorhanden
9087    fn evaluate_2013(&self, ctx: &EvaluationContext) -> ConditionResult {
9088        // Check: condition applies when no RFF+Z54 exists
9089        ctx.lacks_qualifier("RFF", 0, "Z54")
9090    }
9091
9092    /// [2014] Mindesten einmal anzugeben wenn kein SG6 RFF+Z47 (Verwendungszeitraum der Daten: Im System vorhandene Daten) vorhanden
9093    fn evaluate_2014(&self, ctx: &EvaluationContext) -> ConditionResult {
9094        // Check: condition applies when no RFF+Z47 exists
9095        ctx.lacks_qualifier("RFF", 0, "Z47")
9096    }
9097
9098    /// [2015] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) untergeordnet ist
9099    fn evaluate_2015(&self, _ctx: &EvaluationContext) -> ConditionResult {
9100        // TODO: implement
9101        ConditionResult::Unknown
9102    }
9103
9104    /// [2016] Je SG5 LOC+Z16 (Marktlokation) ist genau einmal die Segmentgruppe anzugeben
9105    fn evaluate_2016(&self, ctx: &EvaluationContext) -> ConditionResult {
9106        // Cardinality: exactly once per SG5 LOC+Z16 (Marktlokation)
9107        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9108        ConditionResult::from(loc_count > 0)
9109    }
9110
9111    /// [2017] Je SG5 LOC+Z17 (Messlokation) ist genau einmal die Segmentgruppe anzugeben
9112    fn evaluate_2017(&self, ctx: &EvaluationContext) -> ConditionResult {
9113        // Cardinality: exactly once per SG5 LOC+Z17 (Messlokation)
9114        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9115        ConditionResult::from(loc_count > 0)
9116    }
9117
9118    /// [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...
9119    fn evaluate_2018(&self, _ctx: &EvaluationContext) -> ConditionResult {
9120        // TODO: implement
9121        ConditionResult::Unknown
9122    }
9123
9124    /// [2060] Wenn SG10 CAV+NZR ist die Segmentgruppe genau zwei Mal je IDE+24 anzugeben
9125    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
9126        // Check: CAV+NZR exists (then segment group exactly twice per IDE+24)
9127        let found = ctx.find_segments("CAV").iter().any(|s| {
9128            s.elements
9129                .first()
9130                .and_then(|e| e.first())
9131                .is_some_and(|v| v == "NZR")
9132        });
9133        ConditionResult::from(found)
9134    }
9135
9136    /// [2061] Segment bzw. Segmentgruppe ist genau einmal je SG4 IDE (Vorgang) anzugeben
9137    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
9138        // Cardinality: exactly once per SG4 IDE (always true for single-transaction messages)
9139        let ide_count = ctx.find_segments("IDE").len();
9140        ConditionResult::from(ide_count > 0)
9141    }
9142
9143    /// [2071] Für die ID der LieferantensummenZR einmal je SG4 IDE+24 (Vorgang)
9144    fn evaluate_2071(&self, ctx: &EvaluationContext) -> ConditionResult {
9145        // Cardinality: LieferantensummenZR once per SG4 IDE+24
9146        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9147        ConditionResult::from(ide_count > 0)
9148    }
9149
9150    /// [2073] Für die ID der BilanzkreissummenZR einmal je SG4 IDE+24 (Vorgang)
9151    fn evaluate_2073(&self, ctx: &EvaluationContext) -> ConditionResult {
9152        // Cardinality: BilanzkreissummenZR once per SG4 IDE+24
9153        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9154        ConditionResult::from(ide_count > 0)
9155    }
9156
9157    /// [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)
9158    fn evaluate_2075(&self, ctx: &EvaluationContext) -> ConditionResult {
9159        // Cardinality: Zeitreihen (not EEG-EUZ/AAÜZ) once per SG4 IDE+24
9160        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9161        ConditionResult::from(ide_count > 0)
9162    }
9163
9164    /// [2080] Segmentgruppe ist max. zweimal je SG4 IDE+24 (Vorgang) anzugeben
9165    fn evaluate_2080(&self, ctx: &EvaluationContext) -> ConditionResult {
9166        // Cardinality: max twice per SG4 IDE+24
9167        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9168        ConditionResult::from(ide_count > 0)
9169    }
9170
9171    /// [2095] Je SG5 LOC+Z15 (MaBiS-Zählpunkt) ist genau einmal die Segmentgruppe anzugeben
9172    fn evaluate_2095(&self, ctx: &EvaluationContext) -> ConditionResult {
9173        // Cardinality: exactly once per SG5 LOC+Z15 (MaBiS-Zählpunkt)
9174        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z15").len();
9175        ConditionResult::from(loc_count > 0)
9176    }
9177
9178    /// [2096] Segmentgruppe ist genau zweimal je SG4 IDE anzugeben
9179    fn evaluate_2096(&self, ctx: &EvaluationContext) -> ConditionResult {
9180        // Cardinality: exactly twice per SG4 IDE
9181        let ide_count = ctx.find_segments("IDE").len();
9182        ConditionResult::from(ide_count > 0)
9183    }
9184
9185    /// [2119] Je SG8 SEQ+Z13/ ZG0 (Smartmeter-Gateway) ist genau einmal die Segmentgruppe anzugeben
9186    fn evaluate_2119(&self, ctx: &EvaluationContext) -> ConditionResult {
9187        // Cardinality: exactly once per SG8 SEQ+Z13/ZG0 (Smartmeter-Gateway)
9188        let found = ctx.find_segments("SEQ").iter().any(|s| {
9189            s.elements
9190                .first()
9191                .and_then(|e| e.first())
9192                .is_some_and(|v| v == "Z13" || v == "ZG0")
9193        });
9194        ConditionResult::from(found)
9195    }
9196
9197    /// [2140] Für die ID der LieferantensummenZR einmal je SG4 IDE+Z01 (Liste)
9198    fn evaluate_2140(&self, ctx: &EvaluationContext) -> ConditionResult {
9199        // Cardinality: LieferantensummenZR once per SG4 IDE+Z01
9200        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9201        ConditionResult::from(ide_count > 0)
9202    }
9203
9204    /// [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
9205    fn evaluate_2182(&self, ctx: &EvaluationContext) -> ConditionResult {
9206        // Cardinality: exactly once per SG8 SEQ+Z01/Z80/Z81/Z98
9207        let found = ctx.find_segments("SEQ").iter().any(|s| {
9208            s.elements
9209                .first()
9210                .and_then(|e| e.first())
9211                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9212        });
9213        ConditionResult::from(found)
9214    }
9215
9216    /// [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
9217    fn evaluate_2183(&self, ctx: &EvaluationContext) -> ConditionResult {
9218        // Cardinality: exactly twice per SG8 SEQ+Z01/Z80/Z81/Z98
9219        let found = ctx.find_segments("SEQ").iter().any(|s| {
9220            s.elements
9221                .first()
9222                .and_then(|e| e.first())
9223                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9224        });
9225        ConditionResult::from(found)
9226    }
9227
9228    /// [2207] Für die ID der Lieferanten-ausfall-arbeits-summen-zeitreihe einmal je SG4 IDE+Z01 (Liste)
9229    fn evaluate_2207(&self, ctx: &EvaluationContext) -> ConditionResult {
9230        // Cardinality: Lieferantenausfallarbeitssummenzeitreihe once per SG4 IDE+Z01
9231        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9232        ConditionResult::from(ide_count > 0)
9233    }
9234
9235    /// [2225] Einmal für jede Marktlokation bzw. Tranche für die der LF nicht die gemeldete Ansicht des NB teilt
9236    fn evaluate_2225(&self, _ctx: &EvaluationContext) -> ConditionResult {
9237        // TODO: implement
9238        ConditionResult::Unknown
9239    }
9240
9241    /// [2236] Code einmal je SG4 IDE+24 (Vorgang)
9242    fn evaluate_2236(&self, ctx: &EvaluationContext) -> ConditionResult {
9243        // Cardinality: code once per SG4 IDE+24
9244        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9245        ConditionResult::from(ide_count > 0)
9246    }
9247
9248    /// [2252] Einmal für jede Marktlokation bzw. Tranche, die in der Lieferantenausfallarbeits-summenzeitreihe berücksichtigt wurde
9249    fn evaluate_2252(&self, _ctx: &EvaluationContext) -> ConditionResult {
9250        // TODO: implement
9251        ConditionResult::Unknown
9252    }
9253
9254    /// [2261] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9255    fn evaluate_2261(&self, ctx: &EvaluationContext) -> ConditionResult {
9256        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9257        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9258        ConditionResult::from(loc_count > 0)
9259    }
9260
9261    /// [2284] Für jede Messlokations-ID im SG5 LOC+Z17 (Messlokation) DE3225 genau einmal anzugeben
9262    fn evaluate_2284(&self, ctx: &EvaluationContext) -> ConditionResult {
9263        // Cardinality: exactly once per LOC+Z17 (Messlokation) ID
9264        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9265        ConditionResult::from(loc_count > 0)
9266    }
9267
9268    /// [2286] Für jede SEQ+ZF3 (Daten der Messlokation) mindestens einmal anzugeben
9269    fn evaluate_2286(&self, ctx: &EvaluationContext) -> ConditionResult {
9270        // Cardinality: at least once per SEQ+ZF3 (Daten der Messlokation)
9271        let found = !ctx.find_segments_with_qualifier("SEQ", 0, "ZF3").is_empty();
9272        ConditionResult::from(found)
9273    }
9274
9275    /// [2287] Für jede SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) mindestens einmal anzugeben
9276    fn evaluate_2287(&self, ctx: &EvaluationContext) -> ConditionResult {
9277        // Cardinality: at least once per SEQ+Z03/ZF5 (Zähleinrichtungsdaten)
9278        let found = ctx.find_segments("SEQ").iter().any(|s| {
9279            s.elements
9280                .first()
9281                .and_then(|e| e.first())
9282                .is_some_and(|v| v == "Z03" || v == "ZF5")
9283        });
9284        ConditionResult::from(found)
9285    }
9286
9287    /// [2288] Einmal für jede Zeitreihe, die in der DZR berücksichtigt wurde
9288    fn evaluate_2288(&self, _ctx: &EvaluationContext) -> ConditionResult {
9289        // TODO: implement
9290        ConditionResult::Unknown
9291    }
9292
9293    /// [2307] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225 genau einmal anzugeben
9294    fn evaluate_2307(&self, ctx: &EvaluationContext) -> ConditionResult {
9295        // Cardinality: exactly once per LOC+Z21 (Tranche) ID
9296        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9297        ConditionResult::from(loc_count > 0)
9298    }
9299
9300    /// [2308] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225, mindestens einmal anzugeben
9301    fn evaluate_2308(&self, ctx: &EvaluationContext) -> ConditionResult {
9302        // Cardinality: at least once per LOC+Z16 (Marktlokation) ID
9303        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9304        ConditionResult::from(loc_count > 0)
9305    }
9306
9307    /// [2309] Für jede ID im SG5 LOC+Z17 (Messlokation) DE3225 mindestens einmal anzugeben
9308    fn evaluate_2309(&self, ctx: &EvaluationContext) -> ConditionResult {
9309        // Cardinality: at least once per LOC+Z17 (Messlokation) ID
9310        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9311        ConditionResult::from(loc_count > 0)
9312    }
9313
9314    /// [2310] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225 genau einmal anzugeben
9315    fn evaluate_2310(&self, ctx: &EvaluationContext) -> ConditionResult {
9316        // Cardinality: exactly once per LOC+Z16 (Marktlokation) ID
9317        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9318        ConditionResult::from(loc_count > 0)
9319    }
9320
9321    /// [2311] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9322    fn evaluate_2311(&self, ctx: &EvaluationContext) -> ConditionResult {
9323        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9324        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9325        ConditionResult::from(loc_count > 0)
9326    }
9327
9328    /// [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...
9329    fn evaluate_2312(&self, ctx: &EvaluationContext) -> ConditionResult {
9330        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9331        // Check: RFF+Z33 DE1154 does NOT contain Objektcode 9992000001256 (Netzlokation)
9332        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
9333        let has_netzlokation = rff_z33.iter().any(|s| {
9334            s.elements
9335                .first()
9336                .and_then(|e| e.get(1))
9337                .is_some_and(|v| v == "9992000001256")
9338        });
9339        ConditionResult::from(!has_netzlokation)
9340    }
9341
9342    /// [2313] Je SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels) genau einmal anzugeben
9343    fn evaluate_2313(&self, ctx: &EvaluationContext) -> ConditionResult {
9344        // Check: SG8 with SEQ+Z58/ZC9/ZD0/ZD6 exists (cardinality constraint)
9345        let found = ctx.find_segments("SEQ").iter().any(|s| {
9346            s.elements
9347                .first()
9348                .and_then(|e| e.first())
9349                .is_some_and(|v| ["Z58", "ZC9", "ZD0", "ZD6"].contains(&v.as_str()))
9350        });
9351        ConditionResult::from(found)
9352    }
9353
9354    /// [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
9355    fn evaluate_2317(&self, ctx: &EvaluationContext) -> ConditionResult {
9356        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9357        // Check: SG10 CCI+++Z25 has CAV+MIW/MPW/MUW (Wandler types)
9358        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9359            s.elements
9360                .first()
9361                .and_then(|e| e.first())
9362                .is_some_and(|v| ["MIW", "MPW", "MUW"].contains(&v.as_str()))
9363        });
9364        ConditionResult::from(has_cav)
9365    }
9366
9367    /// [2318] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MBW (Blockstromwandler) vorhanden, ist das Segment genau einmal anzugeben
9368    fn evaluate_2318(&self, ctx: &EvaluationContext) -> ConditionResult {
9369        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9370        // Check: SG10 CCI+++Z25 has CAV+MBW (Blockstromwandler)
9371        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9372            s.elements
9373                .first()
9374                .and_then(|e| e.first())
9375                .is_some_and(|v| v == "MBW")
9376        });
9377        ConditionResult::from(has_cav)
9378    }
9379
9380    /// [2344] Einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
9381    fn evaluate_2344(&self, _ctx: &EvaluationContext) -> ConditionResult {
9382        // TODO: implement
9383        ConditionResult::Unknown
9384    }
9385
9386    /// [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...
9387    fn evaluate_2350(&self, _ctx: &EvaluationContext) -> ConditionResult {
9388        // TODO: implement
9389        ConditionResult::Unknown
9390    }
9391
9392    /// [2351] Einmal je ZP einer BK-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufgenommen wurde
9393    fn evaluate_2351(&self, _ctx: &EvaluationContext) -> ConditionResult {
9394        // TODO: implement
9395        ConditionResult::Unknown
9396    }
9397
9398    /// [2352] Einmal je ZP einer BG-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufge-nommen wurde
9399    fn evaluate_2352(&self, _ctx: &EvaluationContext) -> ConditionResult {
9400        // TODO: implement
9401        ConditionResult::Unknown
9402    }
9403
9404    /// [2356] Je SG5 LOC+Z18 (Netzlokation) ist genau einmal die Segmentgruppe anzugeben
9405    fn evaluate_2356(&self, ctx: &EvaluationContext) -> ConditionResult {
9406        // Cardinality: exactly once per SG5 LOC+Z18 (Netzlokation)
9407        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9408        ConditionResult::from(loc_count > 0)
9409    }
9410
9411    /// [2357] Je SG5 LOC+Z20 (Technische Ressource) ist genau einmal die Segmentgruppe anzugeben
9412    fn evaluate_2357(&self, ctx: &EvaluationContext) -> ConditionResult {
9413        // Cardinality: exactly once per SG5 LOC+Z20 (Technische Ressource)
9414        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z20").len();
9415        ConditionResult::from(loc_count > 0)
9416    }
9417
9418    /// [2358] Je SG5 LOC+Z19 (Steuerbare Ressource) ist genau einmal die Segmentgruppe anzugeben
9419    fn evaluate_2358(&self, ctx: &EvaluationContext) -> ConditionResult {
9420        // Cardinality: exactly once per SG5 LOC+Z19 (Steuerbare Ressource)
9421        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z19").len();
9422        ConditionResult::from(loc_count > 0)
9423    }
9424
9425    /// [2359] Für jede ID im SG5 LOC+Z16 / Z17 / Z20 (Marktlokation / Messlokation / Technische Ressource) DE3225 mindestens einmal anzugeben
9426    fn evaluate_2359(&self, ctx: &EvaluationContext) -> ConditionResult {
9427        // Cardinality: at least once per LOC+Z16/Z17/Z20 ID
9428        let found = ctx.find_segments("LOC").iter().any(|s| {
9429            s.elements
9430                .first()
9431                .and_then(|e| e.first())
9432                .is_some_and(|v| ["Z16", "Z17", "Z20"].contains(&v.as_str()))
9433        });
9434        ConditionResult::from(found)
9435    }
9436
9437    /// [2360] Für jede ID im SG5 LOC+Z18 (Netzlokation) DE3225 mindestens einmal anzugeben
9438    fn evaluate_2360(&self, ctx: &EvaluationContext) -> ConditionResult {
9439        // Cardinality: at least once per LOC+Z18 (Netzlokation) ID
9440        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9441        ConditionResult::from(loc_count > 0)
9442    }
9443
9444    /// [2361] Für jede ID im SG5 LOC+Z17 / Z20 / Z22 (Messlokation / Technische Ressource / ruhende Marktlokation) DE3225 mindestens einmal anzugeben
9445    fn evaluate_2361(&self, ctx: &EvaluationContext) -> ConditionResult {
9446        // Cardinality: at least once per LOC+Z17/Z20/Z22 ID
9447        let found = ctx.find_segments("LOC").iter().any(|s| {
9448            s.elements
9449                .first()
9450                .and_then(|e| e.first())
9451                .is_some_and(|v| ["Z17", "Z20", "Z22"].contains(&v.as_str()))
9452        });
9453        ConditionResult::from(found)
9454    }
9455}
9456
9457// HAND-EDITED: helpers for [223] / [265].
9458
9459use mig_types::segment::OwnedSegment;
9460
9461/// One SG8 repetition: its SEQ qualifier, the Marktlokation IDs of its
9462/// RFF+Z18, and its SG10s as `(CCIs, CAVs)`.
9463struct Sg8Block {
9464    seq: String,
9465    malo_refs: Vec<String>,
9466    sg10: Vec<(Vec<OwnedSegment>, Vec<OwnedSegment>)>,
9467}
9468
9469fn component(seg: &OwnedSegment, element: usize, comp: usize) -> Option<&str> {
9470    seg.elements
9471        .get(element)
9472        .and_then(|e| e.get(comp))
9473        .map(String::as_str)
9474}
9475
9476/// The message's SG8 repetitions, from the group navigator when there is one,
9477/// else from the segments in message order: SEQ opens an SG8, CCI opens an
9478/// SG10 within it that takes the CAVs after it. (`validate_pid_with_conditions`
9479/// evaluates BO4E input without a navigator.) `None` without either.
9480fn sg8_blocks(ctx: &EvaluationContext) -> Option<Vec<Sg8Block>> {
9481    let malo_refs = |rffs: &[OwnedSegment]| -> Vec<String> {
9482        rffs.iter()
9483            .filter(|s| component(s, 0, 0) == Some("Z18"))
9484            .filter_map(|s| component(s, 0, 1).map(str::to_string))
9485            .collect()
9486    };
9487    if let Some(nav) = ctx.navigator() {
9488        let sg8 = ["SG4", "SG8"];
9489        return Some(
9490            (0..nav.group_instance_count(&sg8))
9491                .map(|i| Sg8Block {
9492                    seq: nav
9493                        .find_segments_in_group("SEQ", &sg8, i)
9494                        .first()
9495                        .and_then(|s| component(s, 0, 0).map(str::to_string))
9496                        .unwrap_or_default(),
9497                    malo_refs: malo_refs(&nav.find_segments_in_group("RFF", &sg8, i)),
9498                    sg10: (0..nav.child_group_instance_count(&sg8, i, "SG10"))
9499                        .map(|j| {
9500                            (
9501                                nav.find_segments_in_child_group("CCI", &sg8, i, "SG10", j),
9502                                nav.find_segments_in_child_group("CAV", &sg8, i, "SG10", j),
9503                            )
9504                        })
9505                        .collect(),
9506                })
9507                .collect(),
9508        );
9509    }
9510    if ctx.segments.is_empty() {
9511        return None;
9512    }
9513    let mut blocks: Vec<Sg8Block> = Vec::new();
9514    let mut rffs: Vec<OwnedSegment> = Vec::new();
9515    for seg in ctx.segments {
9516        match seg.id.as_str() {
9517            "SEQ" => {
9518                if let Some(b) = blocks.last_mut() {
9519                    b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9520                }
9521                rffs.clear();
9522                blocks.push(Sg8Block {
9523                    seq: component(seg, 0, 0).unwrap_or_default().to_string(),
9524                    malo_refs: Vec::new(),
9525                    sg10: Vec::new(),
9526                });
9527            }
9528            // Segments after the SG8s end the last one.
9529            "NAD" | "UNT" | "IDE" => {
9530                if let Some(b) = blocks.last_mut() {
9531                    if b.malo_refs.is_empty() {
9532                        b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9533                    }
9534                }
9535                rffs.clear();
9536                if seg.id == "IDE" {
9537                    continue;
9538                }
9539                break;
9540            }
9541            "RFF" if !blocks.is_empty() => rffs.push(seg.clone()),
9542            "CCI" => {
9543                if let Some(b) = blocks.last_mut() {
9544                    b.sg10.push((vec![seg.clone()], Vec::new()));
9545                }
9546            }
9547            "CAV" => {
9548                if let Some((_, cavs)) = blocks.last_mut().and_then(|b| b.sg10.last_mut()) {
9549                    cavs.push(seg.clone());
9550                }
9551            }
9552            _ => {}
9553        }
9554    }
9555    if let Some(b) = blocks.last_mut() {
9556        if b.malo_refs.is_empty() {
9557            b.malo_refs = malo_refs(&rffs);
9558        }
9559    }
9560    Some(blocks)
9561}