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        // HAND-EDITED: "im SG8 SEQ+Z01/ Z98 … SG10 CCI+++ZB3 CAV+Z91" — the MSB in
7141        // the Marktlokation data of this SG4, of the MaLo this SG8 refers to
7142        // (RFF+Z18) where it refers to one, when the scope reaches an SG8. Any
7143        // SG8 of the message (the MeLo's SEQ+Z18 names the recipient by design)
7144        // otherwise, as before.
7145        if let Some(scope) = ctx.enclosing_scope("SG8") {
7146            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
7147            let names_recipient = |s: &[OwnedSegment]| {
7148                sg4_instances::has_cci(s, "", "ZB3")
7149                    && s.iter().any(|c| {
7150                        c.id == "CAV"
7151                            && c.get_element(0) == "Z91"
7152                            && c.get_component(0, 1) == mr_mp_id
7153                    })
7154            };
7155            let same = sg4_instances::sg8s(ctx).iter().any(|p| {
7156                sg4_instances::seq_at(ctx, p)
7157                    .is_some_and(|(code, _)| code == "Z01" || code == "Z98")
7158                    && (refs.is_empty()
7159                        || sg4_instances::rff_values_at(ctx, p, "Z18")
7160                            .iter()
7161                            .any(|v| refs.contains(v)))
7162                    && sg4_instances::sg10_at(ctx, p, names_recipient)
7163            });
7164            return ConditionResult::from(!same);
7165        }
7166
7167        let nav = match ctx.navigator {
7168            Some(n) => n,
7169            None => {
7170                let cavs = ctx.find_segments("CAV");
7171                let same_id_exists = cavs.iter().any(|s| {
7172                    s.elements
7173                        .first()
7174                        .and_then(|e: &Vec<String>| e.first())
7175                        .is_some_and(|v: &String| v == "Z91")
7176                        && s.elements
7177                            .first()
7178                            .and_then(|e| e.get(1))
7179                            .is_some_and(|v: &String| v == &mr_mp_id)
7180                });
7181                return ConditionResult::from(!same_id_exists);
7182            }
7183        };
7184
7185        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7186        for i in 0..sg8_count {
7187            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7188            for j in 0..sg10_count {
7189                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7190                let has_cci_zb3 = ccis.iter().any(|s| {
7191                    s.elements
7192                        .get(2)
7193                        .and_then(|e: &Vec<String>| e.first())
7194                        .is_some_and(|v: &String| v == "ZB3")
7195                });
7196                if !has_cci_zb3 {
7197                    continue;
7198                }
7199                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
7200                for cav in &cavs {
7201                    if cav
7202                        .elements
7203                        .first()
7204                        .and_then(|e: &Vec<String>| e.first())
7205                        .is_some_and(|v: &String| v == "Z91")
7206                    {
7207                        let cav_mp_id = cav
7208                            .elements
7209                            .first()
7210                            .and_then(|e| e.get(1))
7211                            .map(|s| s.as_str())
7212                            .unwrap_or("");
7213                        if cav_mp_id == mr_mp_id.as_str() {
7214                            return ConditionResult::False;
7215                        }
7216                    }
7217                }
7218            }
7219        }
7220
7221        ConditionResult::True
7222    }
7223
7224    /// [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
7225    fn evaluate_405(&self, ctx: &EvaluationContext) -> ConditionResult {
7226        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7227        // Check: OBIS Wirkarbeit kumuliert code in PIA+5
7228        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7229        let found = pia_segs.iter().any(|s| {
7230            s.elements
7231                .first()
7232                .and_then(|e| e.first())
7233                .is_some_and(|v| v == "5")
7234                && s.elements
7235                    .get(1)
7236                    .and_then(|e| e.first())
7237                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7238        });
7239        ConditionResult::from(found)
7240    }
7241
7242    /// [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
7243    fn evaluate_406(&self, ctx: &EvaluationContext) -> ConditionResult {
7244        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7245        // Check: OBIS Wirkarbeit höchste 1/4 Stunde code in PIA+5
7246        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7247        let found = pia_segs.iter().any(|s| {
7248            s.elements
7249                .first()
7250                .and_then(|e| e.first())
7251                .is_some_and(|v| v == "5")
7252                && s.elements
7253                    .get(1)
7254                    .and_then(|e| e.first())
7255                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7256        });
7257        ConditionResult::from(found)
7258    }
7259
7260    /// [407] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Blindarbeit im PIA+5 vorhanden
7261    fn evaluate_407(&self, ctx: &EvaluationContext) -> ConditionResult {
7262        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7263        // Check: OBIS Blindarbeit code in PIA+5
7264        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7265        let found = pia_segs.iter().any(|s| {
7266            s.elements
7267                .first()
7268                .and_then(|e| e.first())
7269                .is_some_and(|v| v == "5")
7270                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7271                    v.starts_with("1-") && (v.contains(":3.8.") || v.contains(":4.8."))
7272                })
7273        });
7274        ConditionResult::from(found)
7275    }
7276
7277    /// [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...
7278    fn evaluate_408(&self, ctx: &EvaluationContext) -> ConditionResult {
7279        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7280        // reaches an SG8; the message as before otherwise.
7281        if let Some(scope) = ctx.enclosing_scope("SG8") {
7282            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7283                ctx,
7284                scope,
7285                is_obis_wirkarbeit_kumuliert,
7286            ));
7287        }
7288        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7289        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7290        ConditionResult::from(has_seq_z02)
7291    }
7292
7293    /// [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 ...
7294    fn evaluate_409(&self, ctx: &EvaluationContext) -> ConditionResult {
7295        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7296        // reaches an SG8; the message as before otherwise.
7297        if let Some(scope) = ctx.enclosing_scope("SG8") {
7298            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7299                ctx,
7300                scope,
7301                is_obis_wirkarbeit_kumuliert,
7302            ));
7303        }
7304        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7305        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7306        ConditionResult::from(!has_seq_z02)
7307    }
7308
7309    /// [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...
7310    fn evaluate_410(&self, ctx: &EvaluationContext) -> ConditionResult {
7311        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7312        // reaches an SG8; the message as before otherwise.
7313        if let Some(scope) = ctx.enclosing_scope("SG8") {
7314            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7315                ctx,
7316                scope,
7317                is_obis_wirkarbeit_quarter_hour,
7318            ));
7319        }
7320        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7321        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7322        ConditionResult::from(has_seq_z02)
7323    }
7324
7325    /// [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 ...
7326    fn evaluate_411(&self, ctx: &EvaluationContext) -> ConditionResult {
7327        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7328        // reaches an SG8; the message as before otherwise.
7329        if let Some(scope) = ctx.enclosing_scope("SG8") {
7330            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7331                ctx,
7332                scope,
7333                is_obis_wirkarbeit_quarter_hour,
7334            ));
7335        }
7336        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7337        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7338        ConditionResult::from(!has_seq_z02)
7339    }
7340
7341    /// [412] Es sind ausschließlich Konfigurationsprodukt-Codes der \"EDI@Energy Codeliste der Konfigurationen\" aus Kapitel 4.2 Konfigurationsprodukte Leistungskurvendefinition erlaubt
7342    fn evaluate_412(&self, ctx: &EvaluationContext) -> ConditionResult {
7343        ctx.external.evaluate("code_list_membership_check")
7344    }
7345
7346    /// [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
7347    fn evaluate_413(&self, ctx: &EvaluationContext) -> ConditionResult {
7348        // Check: SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7349        let found = ctx.find_segments("SEQ").iter().any(|s| {
7350            s.elements
7351                .first()
7352                .and_then(|e| e.first())
7353                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7354        });
7355        ConditionResult::from(found)
7356    }
7357
7358    /// [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
7359    fn evaluate_414(&self, ctx: &EvaluationContext) -> ConditionResult {
7360        // Check: no SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7361        let found = ctx.find_segments("SEQ").iter().any(|s| {
7362            s.elements
7363                .first()
7364                .and_then(|e| e.first())
7365                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7366        });
7367        ConditionResult::from(!found)
7368    }
7369
7370    /// [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
7371    fn evaluate_415(&self, ctx: &EvaluationContext) -> ConditionResult {
7372        // Check: another SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7373        let count = ctx
7374            .find_segments("SEQ")
7375            .iter()
7376            .filter(|s| {
7377                s.elements
7378                    .first()
7379                    .and_then(|e| e.first())
7380                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7381            })
7382            .count();
7383        ConditionResult::from(count > 1)
7384    }
7385
7386    /// [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
7387    fn evaluate_416(&self, ctx: &EvaluationContext) -> ConditionResult {
7388        // Check: no other SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7389        let count = ctx
7390            .find_segments("SEQ")
7391            .iter()
7392            .filter(|s| {
7393                s.elements
7394                    .first()
7395                    .and_then(|e| e.first())
7396                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7397            })
7398            .count();
7399        ConditionResult::from(count <= 1)
7400    }
7401
7402    /// [417] Wenn für den erforderlichen Wert keine Zählzeit benötigt wird
7403    fn evaluate_417(&self, _ctx: &EvaluationContext) -> ConditionResult {
7404        // TODO: implement
7405        ConditionResult::Unknown
7406    }
7407
7408    /// [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
7409    fn evaluate_419(&self, _ctx: &EvaluationContext) -> ConditionResult {
7410        // TODO: implement
7411        ConditionResult::Unknown
7412    }
7413
7414    /// [420] Wenn in dieser SG8 das RFF+Z14 (Smartmeter-Gateway) vorhanden ist
7415    fn evaluate_420(&self, ctx: &EvaluationContext) -> ConditionResult {
7416        // HAND-EDITED: "in dieser SG8" — scoped to the enclosing SG8.
7417        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7418        let found = rff_segs.iter().any(|s| {
7419            s.elements
7420                .first()
7421                .and_then(|e| e.first())
7422                .is_some_and(|v| v == "Z14")
7423        });
7424        ConditionResult::from(found)
7425    }
7426
7427    /// [421] Wenn in dieser SG8 das SG10 CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden ist
7428    fn evaluate_421(&self, ctx: &EvaluationContext) -> ConditionResult {
7429        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing group.
7430        // Check: CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden
7431        ctx.this_group_has_qualifier("CCI", 0, "Z39", &["SG4", "SG8"])
7432    }
7433
7434    /// [425] Messprodukt-Code aus den Kapitel 2.1 \"Standard-Messprodukte der Marktlokation\" der Codeliste der Konfigurationen
7435    fn evaluate_425(&self, ctx: &EvaluationContext) -> ConditionResult {
7436        ctx.external.evaluate("code_list_membership_check")
7437    }
7438
7439    /// [426] Messprodukt-Code aus den Kapitel 2.2 \"Standard-Messprodukte der Tranche\" der Codeliste der Konfigurationen
7440    fn evaluate_426(&self, ctx: &EvaluationContext) -> ConditionResult {
7441        ctx.external.evaluate("code_list_membership_check")
7442    }
7443
7444    /// [427] Messprodukt-Code aus den Kapitel 2.3 \"Standard-Messprodukte der Messlokation\" der Codeliste der Konfigurationen
7445    fn evaluate_427(&self, ctx: &EvaluationContext) -> ConditionResult {
7446        ctx.external.evaluate("code_list_membership_check")
7447    }
7448
7449    /// [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...
7450    fn evaluate_428(&self, ctx: &EvaluationContext) -> ConditionResult {
7451        ctx.external.evaluate("code_list_membership_check")
7452    }
7453
7454    /// [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...
7455    fn evaluate_429(&self, ctx: &EvaluationContext) -> ConditionResult {
7456        ctx.external.evaluate("code_list_membership_check")
7457    }
7458
7459    /// [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...
7460    fn evaluate_430(&self, ctx: &EvaluationContext) -> ConditionResult {
7461        // Check: another SG8 SEQ+Z27 exists with matching RFF+Z18
7462        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7463        ConditionResult::from(seq_z27_count > 1)
7464    }
7465
7466    /// [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+...
7467    fn evaluate_431(&self, ctx: &EvaluationContext) -> ConditionResult {
7468        // Check: no other SG8 SEQ+Z27 exists with matching RFF+Z18
7469        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7470        ConditionResult::from(seq_z27_count <= 1)
7471    }
7472
7473    /// [432] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
7474    fn evaluate_432(&self, ctx: &EvaluationContext) -> ConditionResult {
7475        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7476        // Check: PIA+5 with code 9991000000078 (Wirkarbeit Lastgang) exists
7477        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7478        let found = pia_segs.iter().any(|s| {
7479            s.elements
7480                .first()
7481                .and_then(|e| e.first())
7482                .is_some_and(|v| v == "5")
7483                && s.elements
7484                    .get(1)
7485                    .and_then(|e| e.first())
7486                    .is_some_and(|v| v == "9991000000078")
7487        });
7488        ConditionResult::from(found)
7489    }
7490
7491    /// [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 ...
7492    fn evaluate_433(&self, ctx: &EvaluationContext) -> ConditionResult {
7493        ctx.external.evaluate("code_list_membership_check")
7494    }
7495
7496    /// [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...
7497    fn evaluate_435(&self, ctx: &EvaluationContext) -> ConditionResult {
7498        ctx.external.evaluate("code_list_membership_check")
7499    }
7500
7501    /// [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
7502    fn evaluate_436(&self, ctx: &EvaluationContext) -> ConditionResult {
7503        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7504        // Check: PIA+5 with code 9991000000086 (Wirkarbeit höchste 1/4h Leistung) exists
7505        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7506        let found = pia_segs.iter().any(|s| {
7507            s.elements
7508                .first()
7509                .and_then(|e| e.first())
7510                .is_some_and(|v| v == "5")
7511                && s.elements
7512                    .get(1)
7513                    .and_then(|e| e.first())
7514                    .is_some_and(|v| v == "9991000000086")
7515        });
7516        ConditionResult::from(found)
7517    }
7518
7519    /// [437] Wenn in dieser SG4 das STS+E01++A04 / A23 (Status der Antwort) vorhanden
7520    fn evaluate_437(&self, ctx: &EvaluationContext) -> ConditionResult {
7521        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
7522        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
7523        let found = sts_segs.iter().any(|s| {
7524            s.elements
7525                .first()
7526                .and_then(|e| e.first())
7527                .is_some_and(|v| v == "E01")
7528                && s.elements
7529                    .get(2)
7530                    .and_then(|e| e.first())
7531                    .is_some_and(|v| ["A04", "A23"].contains(&v.as_str()))
7532        });
7533        ConditionResult::from(found)
7534    }
7535
7536    /// [438] Wenn im selben CCI im DE7059 der Code Z39 (Code der Zählzeit) vorhanden ist
7537    fn evaluate_438(&self, ctx: &EvaluationContext) -> ConditionResult {
7538        // HAND-EDITED: "im selben CCI" — the CCI the field is in (its own segment
7539        // in tree validation), else the enclosing SG10's CCI.
7540        let is_z39 = |elements: &[Vec<String>]| {
7541            elements
7542                .first()
7543                .and_then(|e| e.first())
7544                .is_some_and(|v| v == "Z39")
7545        };
7546        if let Some(own) = ctx.resolved_segment.filter(|_| {
7547            ctx.scoped_find_segments_in("SG10", "CCI")
7548                .iter()
7549                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
7550        }) {
7551            return ConditionResult::from(is_z39(own));
7552        }
7553        ConditionResult::from(
7554            ctx.scoped_find_segments_in("SG10", "CCI")
7555                .iter()
7556                .any(|s| is_z39(&s.elements)),
7557        )
7558    }
7559
7560    /// [440] Wenn in demselben RFF der Code Z35 (vorgelagerte Netzlokation) im DE1153 vorhanden
7561    fn evaluate_440(&self, ctx: &EvaluationContext) -> ConditionResult {
7562        // Check: RFF with qualifier Z35 (vorgelagerte Netzlokation) exists
7563        ctx.has_qualifier("RFF", 0, "Z35")
7564    }
7565
7566    /// [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
7567    fn evaluate_441(&self, ctx: &EvaluationContext) -> ConditionResult {
7568        // Check: SG8 SEQ+Z03/ZF5 device without RFF+Z14 (SMGW reference)
7569        ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
7570    }
7571
7572    /// [442] Wenn in demselben RFF der Code Z34 (vorgelagerte Messlokation) im DE1153 vorhanden
7573    fn evaluate_442(&self, ctx: &EvaluationContext) -> ConditionResult {
7574        // Check: RFF with qualifier Z34 (vorgelagerte Messlokation) exists
7575        ctx.has_qualifier("RFF", 0, "Z34")
7576    }
7577
7578    /// [444] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das PIA+5 (Produkt-Daten der Netzlokation) nicht vorhanden
7579    // 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)
7580    fn evaluate_444(&self, ctx: &EvaluationContext) -> ConditionResult {
7581        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7582        // scope reaches one; the message as before otherwise.
7583        if let Some(scope) = ctx.enclosing_scope("SG8") {
7584            return ConditionResult::from(
7585                ctx.find_segments_at(scope, "SEQ")
7586                    .iter()
7587                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7588                    && !ctx
7589                        .find_segments_at(scope, "PIA")
7590                        .iter()
7591                        .any(|s| s.get_element(0) == "5"),
7592            );
7593        }
7594        for code in ["Z60", "ZE0", "ZG8", "ZG9"] {
7595            if matches!(
7596                ctx.any_group_has_qualifier_without("SEQ", 0, code, "PIA", 0, "5", &["SG4", "SG8"]),
7597                ConditionResult::True
7598            ) {
7599                return ConditionResult::True;
7600            }
7601        }
7602        ConditionResult::False
7603    }
7604
7605    /// [445] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das SG10 CCI+11 (Details zum Produkt der Netzlokation) nicht vorhanden
7606    // 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)
7607    fn evaluate_445(&self, ctx: &EvaluationContext) -> ConditionResult {
7608        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7609        // scope reaches one; the message as before otherwise.
7610        if let Some(scope) = ctx.enclosing_scope("SG8") {
7611            return ConditionResult::from(
7612                ctx.find_segments_at(scope, "SEQ")
7613                    .iter()
7614                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7615                    && !ctx
7616                        .find_segments_at(scope, "CCI")
7617                        .iter()
7618                        .any(|s| s.get_element(0) == "11"),
7619            );
7620        }
7621        let nav = match ctx.navigator {
7622            Some(n) => n,
7623            None => {
7624                let has_cci11 = ["Z60", "ZE0", "ZG8", "ZG9"].iter().any(|code| {
7625                    matches!(
7626                        ctx.filtered_parent_child_has_qualifier(
7627                            &["SG4", "SG8"],
7628                            "SEQ",
7629                            0,
7630                            code,
7631                            "SG10",
7632                            "CCI",
7633                            0,
7634                            "11",
7635                        ),
7636                        ConditionResult::True
7637                    )
7638                });
7639                return ConditionResult::from(!has_cci11);
7640            }
7641        };
7642
7643        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7644        for i in 0..sg8_count {
7645            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7646            let has_cci11 = (0..sg10_count).any(|j| {
7647                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7648                ccis.iter().any(|s| {
7649                    s.elements
7650                        .first()
7651                        .and_then(|e: &Vec<String>| e.first())
7652                        .is_some_and(|v: &String| v == "11")
7653                })
7654            });
7655            if !has_cci11 {
7656                return ConditionResult::True;
7657            }
7658        }
7659        ConditionResult::False
7660    }
7661
7662    /// [446] Wenn in derselben SG8 das CCI+Z17 (Stromverbrauchsart) vorhanden
7663    fn evaluate_446(&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 == "Z17")
7671        });
7672        ConditionResult::from(found)
7673    }
7674
7675    /// [447] Wenn in derselben SG8 das CCI+Z50 (Stromerzeugungsart) vorhanden
7676    fn evaluate_447(&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 found = cci_segs.iter().any(|s| {
7680            s.elements
7681                .first()
7682                .and_then(|e| e.first())
7683                .is_some_and(|v| v == "Z50")
7684        });
7685        ConditionResult::from(found)
7686    }
7687
7688    /// [448] Wenn in derselben SG8 das CCI+Z56 (Speicher) vorhanden
7689    fn evaluate_448(&self, ctx: &EvaluationContext) -> ConditionResult {
7690        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7691        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7692        let found = cci_segs.iter().any(|s| {
7693            s.elements
7694                .first()
7695                .and_then(|e| e.first())
7696                .is_some_and(|v| v == "Z56")
7697        });
7698        ConditionResult::from(found)
7699    }
7700
7701    /// [449] Wenn in derselben SG8 das SG10 CCI+6++ZA9 CAV=ZG3 (Aggreg.verantw. ÜNB und Lokation im Regelbetrieb) vorhanden
7702    fn evaluate_449(&self, ctx: &EvaluationContext) -> ConditionResult {
7703        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7704        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7705        let has_cci = cci_segs.iter().any(|s| {
7706            s.elements
7707                .first()
7708                .and_then(|e| e.first())
7709                .is_some_and(|v| v == "6")
7710                && s.elements
7711                    .get(2)
7712                    .and_then(|e| e.first())
7713                    .is_some_and(|v| v == "ZA9")
7714        });
7715        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
7716        let has_cav = cav_segs.iter().any(|s| {
7717            s.elements
7718                .first()
7719                .and_then(|e| e.first())
7720                .is_some_and(|v| v == "ZG3")
7721        });
7722        ConditionResult::from(has_cci && has_cav)
7723    }
7724
7725    /// [450] Wenn in demselben RFF der Code Z32 (Netzlokation) im DE1153 vorhanden
7726    fn evaluate_450(&self, ctx: &EvaluationContext) -> ConditionResult {
7727        // Check: RFF with qualifier Z32 (Netzlokation) exists
7728        ctx.has_qualifier("RFF", 0, "Z32")
7729    }
7730
7731    /// [451] Wenn in demselben RFF der Code Z18 (Marktlokation) im DE1153 vorhanden
7732    fn evaluate_451(&self, ctx: &EvaluationContext) -> ConditionResult {
7733        // Check: RFF with qualifier Z18 (Marktlokation) exists
7734        ctx.has_qualifier("RFF", 0, "Z18")
7735    }
7736
7737    /// [452] Wenn in demselben RFF der Code Z19 (Messlokation) im DE1153 vorhanden
7738    fn evaluate_452(&self, ctx: &EvaluationContext) -> ConditionResult {
7739        // Check: RFF with qualifier Z19 (Messlokation) exists
7740        ctx.has_qualifier("RFF", 0, "Z19")
7741    }
7742
7743    /// [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...
7744    fn evaluate_453(&self, ctx: &EvaluationContext) -> ConditionResult {
7745        ctx.external.evaluate("code_list_membership_check")
7746    }
7747
7748    /// [454] Das Bilanzierungsbeginn Datum muss kleiner sein als das Bilanzierungsende Datum in der SG8 „Datenstand des ÜNB", SG 9 Energiemenge DZÜ Anteil
7749    fn evaluate_454(&self, _ctx: &EvaluationContext) -> ConditionResult {
7750        // Check: begin date < end date (structural — always true for valid messages)
7751        ConditionResult::True
7752    }
7753
7754    /// [455] Das Bilanzierungsende-Datum muss größer sein, als das Bilanzierungsbeginn Datum in der SG8 „Datenstand des ÜNB" , SG 9 Energiemenge DZÜ Anteil
7755    fn evaluate_455(&self, _ctx: &EvaluationContext) -> ConditionResult {
7756        // Check: end date > begin date (structural — always true for valid messages)
7757        ConditionResult::True
7758    }
7759
7760    /// [456] Wenn in demselben RFF der Code Z37 (Technische Ressource) im DE1153 vorhanden
7761    fn evaluate_456(&self, ctx: &EvaluationContext) -> ConditionResult {
7762        // Check: RFF with qualifier Z37 (Technische Ressource) exists
7763        ctx.has_qualifier("RFF", 0, "Z37")
7764    }
7765
7766    /// [457] Wenn bei der Bestellung das Messprodukten 9991000001498 bestellt wurde
7767    fn evaluate_457(&self, ctx: &EvaluationContext) -> ConditionResult {
7768        // Check: PIA+5 with Messprodukt 9991000001498 exists
7769        let pia_segs = ctx.find_segments("PIA");
7770        let found = pia_segs.iter().any(|s| {
7771            s.elements
7772                .first()
7773                .and_then(|e| e.first())
7774                .is_some_and(|v| v == "5")
7775                && s.elements
7776                    .get(1)
7777                    .and_then(|e| e.first())
7778                    .is_some_and(|v| v == "9991000001498")
7779        });
7780        ConditionResult::from(found)
7781    }
7782
7783    /// [458] Wenn bei der Bestellung das Konfigurationsprodukte 9991000000739 bestellt wurde
7784    fn evaluate_458(&self, ctx: &EvaluationContext) -> ConditionResult {
7785        // Check: PIA+5 with Konfigurationsprodukt 9991000000739 exists
7786        let pia_segs = ctx.find_segments("PIA");
7787        let found = pia_segs.iter().any(|s| {
7788            s.elements
7789                .first()
7790                .and_then(|e| e.first())
7791                .is_some_and(|v| v == "5")
7792                && s.elements
7793                    .get(1)
7794                    .and_then(|e| e.first())
7795                    .is_some_and(|v| v == "9991000000739")
7796        });
7797        ConditionResult::from(found)
7798    }
7799
7800    /// [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
7801    fn evaluate_459(&self, ctx: &EvaluationContext) -> ConditionResult {
7802        ctx.external.evaluate("code_list_membership_check")
7803    }
7804
7805    /// [460] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"aus dem SMGW\" bestellt wurde
7806    fn evaluate_460(&self, ctx: &EvaluationContext) -> ConditionResult {
7807        ctx.external.evaluate("code_list_membership_check")
7808    }
7809
7810    /// [461] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden
7811    fn evaluate_461(&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 == "Z17")
7819        });
7820        ConditionResult::from(found)
7821    }
7822
7823    /// [462] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden
7824    fn evaluate_462(&self, ctx: &EvaluationContext) -> ConditionResult {
7825        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7826        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7827        let found = cci_segs.iter().any(|s| {
7828            s.elements
7829                .first()
7830                .and_then(|e| e.first())
7831                .is_some_and(|v| v == "Z17")
7832        });
7833        ConditionResult::from(found)
7834    }
7835
7836    /// [463] Wenn in derselben SG10 das CCI+Z61++ZF9 (Kunde erfüllt die Voraussetzung nach EnFG) vorhanden
7837    fn evaluate_463(&self, ctx: &EvaluationContext) -> ConditionResult {
7838        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7839        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7840        let found = cci_segs.iter().any(|s| {
7841            s.elements
7842                .first()
7843                .and_then(|e| e.first())
7844                .is_some_and(|v| v == "Z61")
7845                && s.elements
7846                    .get(2)
7847                    .and_then(|e| e.first())
7848                    .is_some_and(|v| v == "ZF9")
7849        });
7850        ConditionResult::from(found)
7851    }
7852
7853    /// [465] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z38 (Referenz auf die der Technischen Ressource zugeordnete Steuerbare Ressource) vorhanden ist
7854    fn evaluate_465(&self, ctx: &EvaluationContext) -> ConditionResult {
7855        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7856        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7857        let found = rff_segs.iter().any(|s| {
7858            s.elements
7859                .first()
7860                .and_then(|e| e.first())
7861                .is_some_and(|v| v == "Z38")
7862        });
7863        ConditionResult::from(found)
7864    }
7865
7866    /// [466] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z32 (Referenz auf die der Technischen Ressource zugeordneten Netzlokation) vorhanden ist
7867    fn evaluate_466(&self, ctx: &EvaluationContext) -> ConditionResult {
7868        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7869        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7870        let found = rff_segs.iter().any(|s| {
7871            s.elements
7872                .first()
7873                .and_then(|e| e.first())
7874                .is_some_and(|v| v == "Z32")
7875        });
7876        ConditionResult::from(found)
7877    }
7878
7879    /// [467] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden ist
7880    fn evaluate_467(&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 == "ZG8")
7888        });
7889        ConditionResult::from(found)
7890    }
7891
7892    /// [468] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH0 (Inbetriebsetzung der TR nach 2023) vorhanden ist
7893    fn evaluate_468(&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 == "ZH0")
7901        });
7902        ConditionResult::from(found)
7903    }
7904
7905    /// [469] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden ist
7906    fn evaluate_469(&self, ctx: &EvaluationContext) -> ConditionResult {
7907        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7908        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7909        let found = segs.iter().any(|s| {
7910            s.elements
7911                .first()
7912                .and_then(|e| e.first())
7913                .is_some_and(|v| v == "ZH1")
7914        });
7915        ConditionResult::from(found)
7916    }
7917
7918    /// [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
7919    fn evaluate_470(&self, ctx: &EvaluationContext) -> ConditionResult {
7920        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7921        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7922        let found = segs.iter().any(|s| {
7923            s.elements
7924                .first()
7925                .and_then(|e| e.first())
7926                .is_some_and(|v| v == "ZH5")
7927        });
7928        ConditionResult::from(found)
7929    }
7930
7931    /// [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/ ...
7932    fn evaluate_471(&self, ctx: &EvaluationContext) -> ConditionResult {
7933        // Check: SG6 RFF+Z49/Z53 has Zeitraum-ID > 1 in DE1156
7934        let found = ctx.find_segments("RFF").iter().any(|s| {
7935            let qual = s
7936                .elements
7937                .first()
7938                .and_then(|e| e.first())
7939                .map(|v| v.as_str());
7940            matches!(qual, Some("Z49") | Some("Z53"))
7941                && s.elements
7942                    .first()
7943                    .and_then(|e| e.get(2))
7944                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7945        });
7946        ConditionResult::from(found)
7947    }
7948
7949    /// [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...
7950    fn evaluate_472(&self, ctx: &EvaluationContext) -> ConditionResult {
7951        // Check: SG6 RFF+Z48/Z55 has Zeitraum-ID > 1 in DE1156
7952        let found = ctx.find_segments("RFF").iter().any(|s| {
7953            let qual = s
7954                .elements
7955                .first()
7956                .and_then(|e| e.first())
7957                .map(|v| v.as_str());
7958            matches!(qual, Some("Z48") | Some("Z55"))
7959                && s.elements
7960                    .first()
7961                    .and_then(|e| e.get(2))
7962                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7963        });
7964        ConditionResult::from(found)
7965    }
7966
7967    /// [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...
7968    fn evaluate_473(&self, ctx: &EvaluationContext) -> ConditionResult {
7969        // Check: SG6 RFF+Z47/Z54 has Zeitraum-ID > 1 in DE1156
7970        let found = ctx.find_segments("RFF").iter().any(|s| {
7971            let qual = s
7972                .elements
7973                .first()
7974                .and_then(|e| e.first())
7975                .map(|v| v.as_str());
7976            matches!(qual, Some("Z47") | Some("Z54"))
7977                && s.elements
7978                    .first()
7979                    .and_then(|e| e.get(2))
7980                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7981        });
7982        ConditionResult::from(found)
7983    }
7984
7985    /// [474] Wenn SG4 STS+7++ZC8 (Transaktionsgrund \"Beendigung der Zuordnung\") vorhanden
7986    fn evaluate_474(&self, ctx: &EvaluationContext) -> ConditionResult {
7987        let sts_segs = ctx.find_segments("STS");
7988        let found = sts_segs.iter().any(|s| {
7989            s.elements
7990                .first()
7991                .and_then(|e| e.first())
7992                .is_some_and(|v| v == "7")
7993                && s.elements
7994                    .get(2)
7995                    .and_then(|e| e.first())
7996                    .is_some_and(|v| ["ZC8"].contains(&v.as_str()))
7997        });
7998        ConditionResult::from(found)
7999    }
8000
8001    /// [475] Wenn SG4 STS+7++ZH1 (Transaktionsgrund \"Aufhebung einer zukünftigen Zuordnung wegen Stilllegung\") vorhanden
8002    fn evaluate_475(&self, ctx: &EvaluationContext) -> ConditionResult {
8003        let sts_segs = ctx.find_segments("STS");
8004        let found = sts_segs.iter().any(|s| {
8005            s.elements
8006                .first()
8007                .and_then(|e| e.first())
8008                .is_some_and(|v| v == "7")
8009                && s.elements
8010                    .get(2)
8011                    .and_then(|e| e.first())
8012                    .is_some_and(|v| ["ZH1"].contains(&v.as_str()))
8013        });
8014        ConditionResult::from(found)
8015    }
8016
8017    /// [476] Wenn SG4 STS+7++xxx+ZW0 (Transaktionsgrundergänzung Geschäftsvorfall 1) vorhanden
8018    fn evaluate_476(&self, ctx: &EvaluationContext) -> ConditionResult {
8019        let sts_segs = ctx.find_segments("STS");
8020        let found = sts_segs.iter().any(|s| {
8021            s.elements
8022                .first()
8023                .and_then(|e| e.first())
8024                .is_some_and(|v| v == "7")
8025                && s.elements
8026                    .get(3)
8027                    .and_then(|e| e.first())
8028                    .is_some_and(|v| ["ZW0"].contains(&v.as_str()))
8029        });
8030        ConditionResult::from(found)
8031    }
8032
8033    /// [477] Wenn SG4 STS+7++xxx+ZW1 (Transaktionsgrundergänzung Geschäftsvorfall 2) vorhanden
8034    fn evaluate_477(&self, ctx: &EvaluationContext) -> ConditionResult {
8035        let sts_segs = ctx.find_segments("STS");
8036        let found = sts_segs.iter().any(|s| {
8037            s.elements
8038                .first()
8039                .and_then(|e| e.first())
8040                .is_some_and(|v| v == "7")
8041                && s.elements
8042                    .get(3)
8043                    .and_then(|e| e.first())
8044                    .is_some_and(|v| ["ZW1"].contains(&v.as_str()))
8045        });
8046        ConditionResult::from(found)
8047    }
8048
8049    /// [478] Wenn SG4 STS+7++xxx+ZW2 (Transaktionsgrundergänzung Geschäftsvorfall 3) vorhanden
8050    fn evaluate_478(&self, ctx: &EvaluationContext) -> ConditionResult {
8051        let sts_segs = ctx.find_segments("STS");
8052        let found = sts_segs.iter().any(|s| {
8053            s.elements
8054                .first()
8055                .and_then(|e| e.first())
8056                .is_some_and(|v| v == "7")
8057                && s.elements
8058                    .get(3)
8059                    .and_then(|e| e.first())
8060                    .is_some_and(|v| ["ZW2"].contains(&v.as_str()))
8061        });
8062        ConditionResult::from(found)
8063    }
8064
8065    /// [479] Wenn SG4 STS+7++xxx+ZW3 (Transaktionsgrundergänzung Erzeugende Marktlokation) vorhanden
8066    fn evaluate_479(&self, ctx: &EvaluationContext) -> ConditionResult {
8067        let sts_segs = ctx.find_segments("STS");
8068        let found = sts_segs.iter().any(|s| {
8069            s.elements
8070                .first()
8071                .and_then(|e| e.first())
8072                .is_some_and(|v| v == "7")
8073                && s.elements
8074                    .get(3)
8075                    .and_then(|e| e.first())
8076                    .is_some_and(|v| ["ZW3"].contains(&v.as_str()))
8077        });
8078        ConditionResult::from(found)
8079    }
8080
8081    /// [480] Wenn SG4 STS+7++xxx+ZW4 (Transaktionsgrundergänzung Verbrauchende Marktlokation) vorhanden
8082    fn evaluate_480(&self, ctx: &EvaluationContext) -> ConditionResult {
8083        let sts_segs = ctx.find_segments("STS");
8084        let found = sts_segs.iter().any(|s| {
8085            s.elements
8086                .first()
8087                .and_then(|e| e.first())
8088                .is_some_and(|v| v == "7")
8089                && s.elements
8090                    .get(3)
8091                    .and_then(|e| e.first())
8092                    .is_some_and(|v| ["ZW4"].contains(&v.as_str()))
8093        });
8094        ConditionResult::from(found)
8095    }
8096
8097    /// [481] Wenn SG4 STS+7++xxx+ZW5 (Transaktionsgrundergänzung Tranche) vorhanden
8098    fn evaluate_481(&self, ctx: &EvaluationContext) -> ConditionResult {
8099        let sts_segs = ctx.find_segments("STS");
8100        let found = sts_segs.iter().any(|s| {
8101            s.elements
8102                .first()
8103                .and_then(|e| e.first())
8104                .is_some_and(|v| v == "7")
8105                && s.elements
8106                    .get(3)
8107                    .and_then(|e| e.first())
8108                    .is_some_and(|v| ["ZW5"].contains(&v.as_str()))
8109        });
8110        ConditionResult::from(found)
8111    }
8112
8113    /// [483] Wenn SG4 STS+7++xxx+ZW7 (Transaktionsgrundergänzung Gemessene Marktlokation) vorhanden
8114    fn evaluate_483(&self, ctx: &EvaluationContext) -> ConditionResult {
8115        let sts_segs = ctx.find_segments("STS");
8116        let found = sts_segs.iter().any(|s| {
8117            s.elements
8118                .first()
8119                .and_then(|e| e.first())
8120                .is_some_and(|v| v == "7")
8121                && s.elements
8122                    .get(3)
8123                    .and_then(|e| e.first())
8124                    .is_some_and(|v| ["ZW7"].contains(&v.as_str()))
8125        });
8126        ConditionResult::from(found)
8127    }
8128
8129    /// [484] Wenn SG4 STS+7++xxx+ZW8 (Transaktionsgrundergänzung Fall1) vorhanden
8130    fn evaluate_484(&self, ctx: &EvaluationContext) -> ConditionResult {
8131        let sts_segs = ctx.find_segments("STS");
8132        let found = sts_segs.iter().any(|s| {
8133            s.elements
8134                .first()
8135                .and_then(|e| e.first())
8136                .is_some_and(|v| v == "7")
8137                && s.elements
8138                    .get(3)
8139                    .and_then(|e| e.first())
8140                    .is_some_and(|v| ["ZW8"].contains(&v.as_str()))
8141        });
8142        ConditionResult::from(found)
8143    }
8144
8145    /// [487] Wenn SG4 STS+7++xxx+ZX1 (Transaktionsgrundergänzung Fall4) vorhanden
8146    fn evaluate_487(&self, ctx: &EvaluationContext) -> ConditionResult {
8147        let sts_segs = ctx.find_segments("STS");
8148        let found = sts_segs.iter().any(|s| {
8149            s.elements
8150                .first()
8151                .and_then(|e| e.first())
8152                .is_some_and(|v| v == "7")
8153                && s.elements
8154                    .get(3)
8155                    .and_then(|e| e.first())
8156                    .is_some_and(|v| ["ZX1"].contains(&v.as_str()))
8157        });
8158        ConditionResult::from(found)
8159    }
8160
8161    /// [489] Nur bei dem ältesten Zeitraum welcher mit SG6 RFF+Z49 (Verwendungszeitraum der Daten: Gültige Daten) beschrieben ist
8162    fn evaluate_489(&self, ctx: &EvaluationContext) -> ConditionResult {
8163        // Check: this is the oldest Zeitraum (lowest Zeitraum-ID) with RFF+Z49
8164        let rff_z49 = ctx.find_segments_with_qualifier("RFF", 0, "Z49");
8165        if rff_z49.is_empty() {
8166            return ConditionResult::Unknown;
8167        }
8168        // Approximate: condition applies when RFF+Z49 exists
8169        ConditionResult::True
8170    }
8171
8172    /// [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
8173    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
8174        ctx.format_check("DTM", 0, 1, is_mesz_utc)
8175    }
8176
8177    /// [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
8178    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
8179        ctx.format_check("DTM", 0, 1, is_mez_utc)
8180    }
8181
8182    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
8183    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
8184        // Document date must be <= now. Format 303 is CCYYMMDDHHMM — fixed-width,
8185        // so lexicographic comparison works directly.
8186        let val = ctx.resolved_value.or_else(|| {
8187            ctx.find_segments_with_qualifier("DTM", 0, "137")
8188                .first()
8189                .and_then(|s| s.elements.first())
8190                .and_then(|e| e.get(1))
8191                .map(|v| v.as_str())
8192        });
8193        let Some(dtm_val) = val else {
8194            return ConditionResult::Unknown;
8195        };
8196        if dtm_val.len() < 12 {
8197            return ConditionResult::Unknown;
8198        }
8199        let now_str = crate::eval::format_validators::utc_now_ccyymmddhhmm();
8200        ConditionResult::from(&dtm_val[..12] <= now_str.as_str())
8201    }
8202
8203    /// [500] Hinweis: Code ist gemäß der Kategorie der zu stornierenden Meldung zu wählen
8204    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
8205        ConditionResult::True
8206    }
8207
8208    /// [501] Hinweis: Es sind zur Unterstützung die Daten des LFA anzugeben
8209    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
8210        ConditionResult::True
8211    }
8212
8213    /// [502] Hinweis: Es wird der Betrachtungsmonat der DZR bzw. BAS angegeben
8214    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
8215        ConditionResult::True
8216    }
8217
8218    /// [503] Hinweis: Angabe des BGM DE1004 aus der ORDERS
8219    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
8220        ConditionResult::True
8221    }
8222
8223    /// [504] Hinweis: Je Profil (ggf. inkl. Profilschar) ein Vorgang erforderlich
8224    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
8225        ConditionResult::True
8226    }
8227
8228    /// [505] Hinweis: Je zugeordneter ID und je EEG-Überführungszeitreihe ein Vorgang erforderlich
8229    fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
8230        ConditionResult::True
8231    }
8232
8233    /// [506] Hinweis: Für jeden Code im LOC+Z16 (Marktlokation) sollen die entsprechenden Kundendaten des LF angegeben werden, falls bekannt
8234    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
8235        ConditionResult::True
8236    }
8237
8238    /// [507] Hinweis: Ursprünglich vom NB bestätigtes Beginndatum
8239    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
8240        ConditionResult::True
8241    }
8242
8243    /// [508] Hinweis: Ausschließlich kME sind über die NNE abrechenbar
8244    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
8245        ConditionResult::True
8246    }
8247
8248    /// [509] Hinweis: Handelt sich um ein mME oder iMS ist das RFF 1154 immer mit NEIN anzugeben
8249    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
8250        ConditionResult::True
8251    }
8252
8253    /// [510] Hinweis: Zu verwenden bei der Abmeldung der ESV
8254    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
8255        ConditionResult::True
8256    }
8257
8258    /// [511] Hinweis: Es sind alle Bilanzierungsgebiete anzugeben in denen das Profil verwendet wird
8259    fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
8260        ConditionResult::True
8261    }
8262
8263    /// [512] Hinweis: Es ist informativ die bisherige Veräußerungsform des LFA an der erzeugenden Marktlokation anzugeben
8264    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
8265        ConditionResult::True
8266    }
8267
8268    /// [513] Hinweis: Es ist das nächstmögliche Datum ab dem DTM+471 aus der entsprechenden Anfrage zu ermitteln
8269    fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
8270        ConditionResult::True
8271    }
8272
8273    /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
8274    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
8275        ConditionResult::True
8276    }
8277
8278    /// [515] Hinweis: Für den ZP der LieferantensummenZR anzugeben
8279    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
8280        ConditionResult::True
8281    }
8282
8283    /// [516] Hinweis: Es ist der Änderungszeitpunkt anzugeben an dem das Zuordnungsende des NBA und der Zuordnungsbeginn des NBN zu einer Marktlokation fallen
8284    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
8285        ConditionResult::True
8286    }
8287
8288    /// [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
8289    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
8290        ConditionResult::True
8291    }
8292
8293    /// [518] Hinweis: Es sind alle Altlieferanten anzugeben, an die eine Abmeldeanfrage gesendet wird
8294    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
8295        ConditionResult::True
8296    }
8297
8298    /// [519] Hinweis: Wenn in der Anmeldung der Code ZAP vorhanden war, so ist dieser auch in der Antwort zu verwenden
8299    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
8300        ConditionResult::True
8301    }
8302
8303    /// [520] Hinweis: Bei der Verwendung des Codes ZAP handelt es sich immer um eine gemessene Marktlokation
8304    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
8305        ConditionResult::True
8306    }
8307
8308    /// [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...
8309    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
8310        ConditionResult::True
8311    }
8312
8313    /// [522] Hinweis: Es ist der NBN anzugeben
8314    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
8315        ConditionResult::True
8316    }
8317
8318    /// [523] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
8319    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
8320        ConditionResult::True
8321    }
8322
8323    /// [524] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, für die der LF nicht die gemeldete Ansicht des NB teilt
8324    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
8325        ConditionResult::True
8326    }
8327
8328    /// [525] Hinweis: Es sind nur die OBIS-Kennzahlen erlaubt, die im vorherigen Stammdatenaustausch zu dieser Marktlokation vom MSB zu diesem Zeitpunkt übermittelt wurden
8329    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
8330        ConditionResult::True
8331    }
8332
8333    /// [526] Hinweis: Wenn der Marktlokation keine Technische Ressource in der Lokationsbündelstruktur zugeordnet wurde
8334    fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
8335        ConditionResult::True
8336    }
8337
8338    /// [527] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben
8339    fn evaluate_527(&self, _ctx: &EvaluationContext) -> ConditionResult {
8340        ConditionResult::True
8341    }
8342
8343    /// [528] Hinweis: Es ist das Datum/ Daten aus der Anfrage zu verwenden
8344    fn evaluate_528(&self, _ctx: &EvaluationContext) -> ConditionResult {
8345        ConditionResult::True
8346    }
8347
8348    /// [529] Hinweis: Für zusätzliche nicht im Markt standardisierte Identifikatoren wie z.B. eine Netzbetreiber-Projektnummer
8349    fn evaluate_529(&self, _ctx: &EvaluationContext) -> ConditionResult {
8350        ConditionResult::True
8351    }
8352
8353    /// [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, ...
8354    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
8355        ConditionResult::True
8356    }
8357
8358    /// [531] Hinweis: Es ist das Jahr anzugeben in dem die nächste Netznutzungsabrechnung erfolgt
8359    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
8360        ConditionResult::True
8361    }
8362
8363    /// [532] Hinweis: Kritische Daten (gemäß GPKE)
8364    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
8365        ConditionResult::True
8366    }
8367
8368    /// [533] Hinweis: Es ist die MP-ID des Lieferanten anzugeben
8369    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
8370        ConditionResult::True
8371    }
8372
8373    /// [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...
8374    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
8375        // TODO: implement
8376        ConditionResult::Unknown
8377    }
8378
8379    /// [556] Hinweis: Wenn keine Korrespondenzanschrift des Endverbrauchers/ Kunden vorliegt, ist die Anschrift der Marktlokation zu übermitteln
8380    fn evaluate_556(&self, _ctx: &EvaluationContext) -> ConditionResult {
8381        ConditionResult::True
8382    }
8383
8384    /// [558] Hinweis: Diese Information kann freiwillig ausgetauscht werden
8385    fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
8386        ConditionResult::True
8387    }
8388
8389    /// [559] Hinweis: Die Korrespondenzanschrift des Endverbrauchers/Kunden wird nicht zur Identifikation genutzt
8390    fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
8391        ConditionResult::True
8392    }
8393
8394    /// [563] Hinweis: Für die ID der LieferantensummenZR
8395    fn evaluate_563(&self, _ctx: &EvaluationContext) -> ConditionResult {
8396        ConditionResult::True
8397    }
8398
8399    /// [566] Hinweis: Altlieferant
8400    fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
8401        ConditionResult::True
8402    }
8403
8404    /// [567] Hinweis: Neulieferant
8405    fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
8406        ConditionResult::True
8407    }
8408
8409    /// [568] Hinweis: Lieferant der LieferantensummenZR
8410    fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
8411        ConditionResult::True
8412    }
8413
8414    /// [569] Hinweis: Dritter Nutzer
8415    fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
8416        ConditionResult::True
8417    }
8418
8419    /// [570] Hinweis: Netzbetreiber Alt
8420    fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
8421        ConditionResult::True
8422    }
8423
8424    /// [572] Hinweis: Kundenname aus Anmeldung Lieferant neu
8425    fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
8426        ConditionResult::True
8427    }
8428
8429    /// [576] Hinweis: Stammdaten des bisherigen Messstellenbetreibers
8430    fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
8431        ConditionResult::True
8432    }
8433
8434    /// [577] Hinweis: Wird mit dem ursprünglich vom NB bestätigten Beginnzeitpunkt gefüllt
8435    fn evaluate_577(&self, _ctx: &EvaluationContext) -> ConditionResult {
8436        ConditionResult::True
8437    }
8438
8439    /// [579] Hinweis: Auslösender Marktpartner (LFN bei STS+7++ZH0/ZG9, NB bei STS+7++ZH1)
8440    fn evaluate_579(&self, _ctx: &EvaluationContext) -> ConditionResult {
8441        ConditionResult::True
8442    }
8443
8444    /// [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...
8445    fn evaluate_580(&self, _ctx: &EvaluationContext) -> ConditionResult {
8446        ConditionResult::True
8447    }
8448
8449    /// [581] Hinweis: Es ist der Zeitpunkt anzugeben, zu welchem der Vertrag am Tag des Versandes der Antwort noch kündbar ist.
8450    fn evaluate_581(&self, _ctx: &EvaluationContext) -> ConditionResult {
8451        ConditionResult::True
8452    }
8453
8454    /// [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...
8455    fn evaluate_586(&self, _ctx: &EvaluationContext) -> ConditionResult {
8456        ConditionResult::True
8457    }
8458
8459    /// [590] Hinweis: Für den Empfang des Steuerbefehls
8460    fn evaluate_590(&self, _ctx: &EvaluationContext) -> ConditionResult {
8461        ConditionResult::True
8462    }
8463
8464    /// [594] Hinweis: Es ist der ZPB des ZP der NGZ und die ZPB der NZR anzugeben
8465    fn evaluate_594(&self, _ctx: &EvaluationContext) -> ConditionResult {
8466        ConditionResult::True
8467    }
8468
8469    /// [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...
8470    fn evaluate_599(&self, _ctx: &EvaluationContext) -> ConditionResult {
8471        ConditionResult::True
8472    }
8473
8474    /// [601] Hinweis: Es ist die ID der Marktlokation und alle Identifikatoren der Messlokationen anzugeben. Sowie wenn vorhanden die Tranche/n der Marklokation
8475    fn evaluate_601(&self, _ctx: &EvaluationContext) -> ConditionResult {
8476        ConditionResult::True
8477    }
8478
8479    /// [606] Hinweis: In diesem Segment bzw. SG sind die Daten aus der Sicht des NB anzugeben
8480    fn evaluate_606(&self, _ctx: &EvaluationContext) -> ConditionResult {
8481        ConditionResult::True
8482    }
8483
8484    /// [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
8485    fn evaluate_609(&self, _ctx: &EvaluationContext) -> ConditionResult {
8486        ConditionResult::True
8487    }
8488
8489    /// [611] Hinweis: Wenn das Unternehmen NB die Aufgaben der Marktrolle LF wahrnimmt, ist die MP-ID des Unternehmens NB in der Marktrolle LF anzugeben
8490    fn evaluate_611(&self, _ctx: &EvaluationContext) -> ConditionResult {
8491        ConditionResult::True
8492    }
8493
8494    /// [614] Hinweis: Es werden nur die OBIS Kennzahlen übermittelt die für die Bilanzierung relevant sind
8495    fn evaluate_614(&self, _ctx: &EvaluationContext) -> ConditionResult {
8496        ConditionResult::True
8497    }
8498
8499    /// [617] Hinweis: Mehrere Vorgänge für eine Marktlokation bei Wechsel bilanzierungsrelevanter Stammdaten innerhalb des Bilanzierungsmonats
8500    fn evaluate_617(&self, _ctx: &EvaluationContext) -> ConditionResult {
8501        ConditionResult::True
8502    }
8503
8504    /// [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
8505    fn evaluate_618(&self, _ctx: &EvaluationContext) -> ConditionResult {
8506        ConditionResult::True
8507    }
8508
8509    /// [619] Hinweis: Für die selbständige Änderungsmöglichkeit der Schwellwertunter- / -über-schreitung
8510    fn evaluate_619(&self, _ctx: &EvaluationContext) -> ConditionResult {
8511        ConditionResult::True
8512    }
8513
8514    /// [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.
8515    fn evaluate_621(&self, _ctx: &EvaluationContext) -> ConditionResult {
8516        ConditionResult::True
8517    }
8518
8519    /// [622] Hinweis: Falls die OBIS-Kennzahl für mehrere Marktrollen relevant ist, so muss die Segmentgruppe pro Marktrolle wiederholt werden
8520    fn evaluate_622(&self, _ctx: &EvaluationContext) -> ConditionResult {
8521        ConditionResult::True
8522    }
8523
8524    /// [623] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben, die zur Ermittlung der Energiemenge der im Vorgang genannten Marktlokation benötigt werden
8525    fn evaluate_623(&self, _ctx: &EvaluationContext) -> ConditionResult {
8526        ConditionResult::True
8527    }
8528
8529    /// [630] Hinweis: Wenn die Liste abgelehnt wird, ist kein Vorgang enthalten
8530    fn evaluate_630(&self, _ctx: &EvaluationContext) -> ConditionResult {
8531        ConditionResult::True
8532    }
8533
8534    /// [631] Hinweis: Es ist die Listennummer aus der Lieferanten- bzw. Bilanzierungsgebietsclearingliste zu verwenden
8535    fn evaluate_631(&self, _ctx: &EvaluationContext) -> ConditionResult {
8536        ConditionResult::True
8537    }
8538
8539    /// [632] Hinweis: Es ist die Listennummer aus der Lieferanten-ausfallarbeits-clearingliste zu verwenden
8540    fn evaluate_632(&self, _ctx: &EvaluationContext) -> ConditionResult {
8541        ConditionResult::True
8542    }
8543
8544    /// [637] Hinweis: Bei Verpflichtungsanfrage
8545    fn evaluate_637(&self, _ctx: &EvaluationContext) -> ConditionResult {
8546        ConditionResult::True
8547    }
8548
8549    /// [638] Hinweis: Bei Aufforderung zur Übernahme der einzelnen Messlokation durch den gMSB
8550    fn evaluate_638(&self, _ctx: &EvaluationContext) -> ConditionResult {
8551        ConditionResult::True
8552    }
8553
8554    /// [639] Hinweis: Wenn Antwort auf Deaktivierung
8555    fn evaluate_639(&self, _ctx: &EvaluationContext) -> ConditionResult {
8556        ConditionResult::True
8557    }
8558
8559    /// [640] Hinweis: Wenn Antwort auf Aktivierung
8560    fn evaluate_640(&self, _ctx: &EvaluationContext) -> ConditionResult {
8561        ConditionResult::True
8562    }
8563
8564    /// [641] Hinweis: Wenn Einzelanforderung vorliegt
8565    fn evaluate_641(&self, _ctx: &EvaluationContext) -> ConditionResult {
8566        ConditionResult::True
8567    }
8568
8569    /// [642] Hinweis: Wenn Abo vorliegt
8570    fn evaluate_642(&self, _ctx: &EvaluationContext) -> ConditionResult {
8571        ConditionResult::True
8572    }
8573
8574    /// [643] Hinweis: Nachfolgender Netzbetreiber
8575    fn evaluate_643(&self, _ctx: &EvaluationContext) -> ConditionResult {
8576        ConditionResult::True
8577    }
8578
8579    /// [645] Hinweis: Es ist der Bilanzkreis des LF anzugeben
8580    fn evaluate_645(&self, _ctx: &EvaluationContext) -> ConditionResult {
8581        ConditionResult::True
8582    }
8583
8584    /// [646] Hinweis: Es ist der RD-Bilanzkreis des ANB anzugeben
8585    fn evaluate_646(&self, _ctx: &EvaluationContext) -> ConditionResult {
8586        ConditionResult::True
8587    }
8588
8589    /// [647] Hinweis: Es ist der RD-Bilanzkreis des anfNB anzugeben
8590    fn evaluate_647(&self, _ctx: &EvaluationContext) -> ConditionResult {
8591        ConditionResult::True
8592    }
8593
8594    /// [648] Hinweis: Soll ein Produkt ab dem Datum \"Änderung zum\" nicht mehr vorhanden sein, wird dies durch nicht angeben des PIA+5 ausgedrückt
8595    fn evaluate_648(&self, _ctx: &EvaluationContext) -> ConditionResult {
8596        ConditionResult::True
8597    }
8598
8599    /// [651] Hinweis: Es sind die Marktlokationen anzugeben, zu welchen die hier genannte OBIS benötigt wird
8600    fn evaluate_651(&self, _ctx: &EvaluationContext) -> ConditionResult {
8601        ConditionResult::True
8602    }
8603
8604    /// [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...
8605    fn evaluate_653(&self, _ctx: &EvaluationContext) -> ConditionResult {
8606        ConditionResult::True
8607    }
8608
8609    /// [655] Hinweis: Es ist die Listennummer aus der DZÜ Liste zu verwenden
8610    fn evaluate_655(&self, _ctx: &EvaluationContext) -> ConditionResult {
8611        ConditionResult::True
8612    }
8613
8614    /// [659] Hinweis: Bei Tranchen ist nur der Meldepunkt der Tranche anzugeben
8615    fn evaluate_659(&self, _ctx: &EvaluationContext) -> ConditionResult {
8616        ConditionResult::True
8617    }
8618
8619    /// [660] Hinweis: Es ist die ID der BK-Summenzeitreihe Aggregationsebene BG anzugeben
8620    fn evaluate_660(&self, _ctx: &EvaluationContext) -> ConditionResult {
8621        ConditionResult::True
8622    }
8623
8624    /// [662] Hinweis: Es sind die Daten NGZ und die Daten der NZR anzugeben
8625    fn evaluate_662(&self, _ctx: &EvaluationContext) -> ConditionResult {
8626        ConditionResult::True
8627    }
8628
8629    /// [663] Hinweis: Es ist die ID der Marktlokation und die ZPB des ZP der NGZ anzugeben
8630    fn evaluate_663(&self, _ctx: &EvaluationContext) -> ConditionResult {
8631        ConditionResult::True
8632    }
8633
8634    /// [664] Hinweis: Es ist das BG des NB (LPB) anzugeben
8635    fn evaluate_664(&self, _ctx: &EvaluationContext) -> ConditionResult {
8636        ConditionResult::True
8637    }
8638
8639    /// [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
8640    fn evaluate_665(&self, _ctx: &EvaluationContext) -> ConditionResult {
8641        ConditionResult::True
8642    }
8643
8644    /// [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...
8645    fn evaluate_667(&self, _ctx: &EvaluationContext) -> ConditionResult {
8646        ConditionResult::True
8647    }
8648
8649    /// [668] Hinweis: Dieses Segment wird nach Abschluss der Einführung der Lokationsbündelstruktur zum 01.10.2025 aus der UTILMD entfernt
8650    fn evaluate_668(&self, _ctx: &EvaluationContext) -> ConditionResult {
8651        ConditionResult::True
8652    }
8653
8654    /// [670] Hinweis: Es sind alle Netzlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8655    fn evaluate_670(&self, _ctx: &EvaluationContext) -> ConditionResult {
8656        ConditionResult::True
8657    }
8658
8659    /// [671] Hinweis: Es sind alle Marktlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8660    fn evaluate_671(&self, _ctx: &EvaluationContext) -> ConditionResult {
8661        ConditionResult::True
8662    }
8663
8664    /// [672] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8665    fn evaluate_672(&self, _ctx: &EvaluationContext) -> ConditionResult {
8666        ConditionResult::True
8667    }
8668
8669    /// [673] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8670    fn evaluate_673(&self, _ctx: &EvaluationContext) -> ConditionResult {
8671        ConditionResult::True
8672    }
8673
8674    /// [674] Hinweis: Es sind alle Tranchen, zu nennen, die dem gleichen Lokationsbündel angehören
8675    fn evaluate_674(&self, _ctx: &EvaluationContext) -> ConditionResult {
8676        ConditionResult::True
8677    }
8678
8679    /// [675] Hinweis: Es sind alle Messlokationen, zu nennen, die Lokationsbündel angehören
8680    fn evaluate_675(&self, _ctx: &EvaluationContext) -> ConditionResult {
8681        ConditionResult::True
8682    }
8683
8684    /// [677] Hinweis: Es sind alle Netzlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8685    fn evaluate_677(&self, _ctx: &EvaluationContext) -> ConditionResult {
8686        ConditionResult::True
8687    }
8688
8689    /// [678] Hinweis: Es sind alle Marktlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8690    fn evaluate_678(&self, _ctx: &EvaluationContext) -> ConditionResult {
8691        ConditionResult::True
8692    }
8693
8694    /// [679] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8695    fn evaluate_679(&self, _ctx: &EvaluationContext) -> ConditionResult {
8696        ConditionResult::True
8697    }
8698
8699    /// [680] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8700    fn evaluate_680(&self, _ctx: &EvaluationContext) -> ConditionResult {
8701        ConditionResult::True
8702    }
8703
8704    /// [682] Hinweis: Es sind alle Messlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8705    fn evaluate_682(&self, _ctx: &EvaluationContext) -> ConditionResult {
8706        ConditionResult::True
8707    }
8708
8709    /// [683] Hinweis: Es sind alle ID der Netzlokationen, welche der im SG5 LOC+Z16 angegebenen Marktlokation aktuell und / oder zukünftig vorgelagert sind, anzugeben
8710    fn evaluate_683(&self, _ctx: &EvaluationContext) -> ConditionResult {
8711        ConditionResult::True
8712    }
8713
8714    /// [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
8715    fn evaluate_684(&self, _ctx: &EvaluationContext) -> ConditionResult {
8716        ConditionResult::True
8717    }
8718
8719    /// [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.
8720    fn evaluate_685(&self, _ctx: &EvaluationContext) -> ConditionResult {
8721        ConditionResult::True
8722    }
8723
8724    /// [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
8725    fn evaluate_687(&self, _ctx: &EvaluationContext) -> ConditionResult {
8726        ConditionResult::True
8727    }
8728
8729    /// [688] Hinweis: Es sind alle ID der Netzlokationen, welche der im LOC+Z16 angegebenen Marktlokation vorgelagert sind, anzugeben
8730    fn evaluate_688(&self, _ctx: &EvaluationContext) -> ConditionResult {
8731        ConditionResult::True
8732    }
8733
8734    /// [689] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16 angegebenen Marktlokation zugehören, anzugeben
8735    fn evaluate_689(&self, _ctx: &EvaluationContext) -> ConditionResult {
8736        ConditionResult::True
8737    }
8738
8739    /// [690] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen zugeordnet sind, anzugeben
8740    fn evaluate_690(&self, _ctx: &EvaluationContext) -> ConditionResult {
8741        ConditionResult::True
8742    }
8743
8744    /// [693] Hinweis: Es sind die Daten NGZ anzugeben
8745    fn evaluate_693(&self, _ctx: &EvaluationContext) -> ConditionResult {
8746        ConditionResult::True
8747    }
8748
8749    /// [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...
8750    fn evaluate_694(&self, _ctx: &EvaluationContext) -> ConditionResult {
8751        ConditionResult::True
8752    }
8753
8754    /// [695] Hinweis: Verwendung, wenn Rolle LF einem Unternehmen NB zugeordnet ist
8755    fn evaluate_695(&self, _ctx: &EvaluationContext) -> ConditionResult {
8756        ConditionResult::True
8757    }
8758
8759    /// [696] Hinweis: Verwendung, nur wenn die Rolle LF nicht einem Unternehmen NB zugeordnet ist
8760    fn evaluate_696(&self, _ctx: &EvaluationContext) -> ConditionResult {
8761        ConditionResult::True
8762    }
8763
8764    /// [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...
8765    fn evaluate_698(&self, _ctx: &EvaluationContext) -> ConditionResult {
8766        ConditionResult::True
8767    }
8768
8769    /// [699] Hinweis: Es ist die ID der Steuerbare Ressource zu nennen über die die Technische Ressource gesteuert wird
8770    fn evaluate_699(&self, _ctx: &EvaluationContext) -> ConditionResult {
8771        ConditionResult::True
8772    }
8773
8774    /// [700] Hinweis: Es ist die ID der Netzlokation zu nennen über die die Technische Ressource gesteuert wird
8775    fn evaluate_700(&self, _ctx: &EvaluationContext) -> ConditionResult {
8776        ConditionResult::True
8777    }
8778
8779    /// [704] Hinweis: Segment ist zu verwenden, wenn es in Anfrage vorhanden war
8780    fn evaluate_704(&self, _ctx: &EvaluationContext) -> ConditionResult {
8781        ConditionResult::True
8782    }
8783
8784    /// [705] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Änderung\" durchgeführt wird
8785    fn evaluate_705(&self, _ctx: &EvaluationContext) -> ConditionResult {
8786        ConditionResult::True
8787    }
8788
8789    /// [706] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Bestellung einer Änderung von Stammdaten...\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8790    fn evaluate_706(&self, ctx: &EvaluationContext) -> ConditionResult {
8791        ctx.external.evaluate("data_clearing_required")
8792    }
8793
8794    /// [707] Hinweis: für weitere Details siehe Kapitel \"SG6 Verwendungszeitraum der Daten\"
8795    fn evaluate_707(&self, _ctx: &EvaluationContext) -> ConditionResult {
8796        ConditionResult::True
8797    }
8798
8799    /// [708] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des E/G zur Marktlokation\" durchgeführt wird
8800    fn evaluate_708(&self, _ctx: &EvaluationContext) -> ConditionResult {
8801        ConditionResult::True
8802    }
8803
8804    /// [709] Hinweis: Wenn die Aktion \"Zuordnung des E/G zur Marktlokation aufgrund fehlender Antwort\" durchgeführt wird
8805    fn evaluate_709(&self, _ctx: &EvaluationContext) -> ConditionResult {
8806        ConditionResult::True
8807    }
8808
8809    /// [710] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des LFN zur Marktlokation bzw. Tranche\" durchgeführt wird
8810    fn evaluate_710(&self, _ctx: &EvaluationContext) -> ConditionResult {
8811        ConditionResult::True
8812    }
8813
8814    /// [711] Hinweis: Wenn die Aktion \"Zuordnung des LFN zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8815    fn evaluate_711(&self, _ctx: &EvaluationContext) -> ConditionResult {
8816        ConditionResult::True
8817    }
8818
8819    /// [712] Hinweis: Wenn die Aktion \"Ankündigung der Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche\" durchgeführt wird
8820    fn evaluate_712(&self, _ctx: &EvaluationContext) -> ConditionResult {
8821        ConditionResult::True
8822    }
8823
8824    /// [713] Hinweis: Wenn die Aktion \"Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8825    fn evaluate_713(&self, _ctx: &EvaluationContext) -> ConditionResult {
8826        ConditionResult::True
8827    }
8828
8829    /// [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...
8830    fn evaluate_714(&self, _ctx: &EvaluationContext) -> ConditionResult {
8831        ConditionResult::True
8832    }
8833
8834    /// [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...
8835    fn evaluate_715(&self, _ctx: &EvaluationContext) -> ConditionResult {
8836        ConditionResult::True
8837    }
8838
8839    /// [716] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Abrechnungsdaten\" durchgeführt wird
8840    fn evaluate_716(&self, _ctx: &EvaluationContext) -> ConditionResult {
8841        ConditionResult::True
8842    }
8843
8844    /// [717] Hinweis: Wenn die Aktion des Sequenzdiagramms \"Bestellung einer Änderung von Abrechnungsdaten\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8845    fn evaluate_717(&self, ctx: &EvaluationContext) -> ConditionResult {
8846        ctx.external.evaluate("data_clearing_required")
8847    }
8848
8849    /// [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
8850    fn evaluate_718(&self, _ctx: &EvaluationContext) -> ConditionResult {
8851        ConditionResult::True
8852    }
8853
8854    /// [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...
8855    fn evaluate_719(&self, _ctx: &EvaluationContext) -> ConditionResult {
8856        ConditionResult::True
8857    }
8858
8859    /// [902] Format: Möglicher Wert: ≥ 0
8860    fn evaluate_902(&self, _ctx: &EvaluationContext) -> ConditionResult {
8861        // Format condition — informational, always applies
8862        ConditionResult::True
8863    }
8864
8865    /// [910] Format: Möglicher Wert: < 0 oder ≥ 0
8866    fn evaluate_910(&self, _ctx: &EvaluationContext) -> ConditionResult {
8867        // Format condition — informational, always applies
8868        ConditionResult::True
8869    }
8870
8871    /// [914] Format: Möglicher Wert: > 0
8872    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
8873        ctx.format_check("SEQ", 1, 0, |val| validate_numeric(val, ">", 0.0))
8874    }
8875
8876    /// [922] Format: TR-ID
8877    fn evaluate_922(&self, _ctx: &EvaluationContext) -> ConditionResult {
8878        // Format condition — informational, always applies
8879        ConditionResult::True
8880    }
8881
8882    /// [926] Format: Möglicher Wert: 0
8883    fn evaluate_926(&self, _ctx: &EvaluationContext) -> ConditionResult {
8884        // Format condition — informational, always applies
8885        ConditionResult::True
8886    }
8887
8888    /// [930] Format: max. 2 Nachkommastellen
8889    fn evaluate_930(&self, _ctx: &EvaluationContext) -> ConditionResult {
8890        // Format condition — informational, always applies
8891        ConditionResult::True
8892    }
8893
8894    /// [931] Format: ZZZ = +00
8895    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
8896        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
8897    }
8898
8899    /// [932] Format: HHMM = 2200
8900    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
8901        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
8902    }
8903
8904    /// [933] Format: HHMM = 2300
8905    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
8906        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
8907    }
8908
8909    /// [937] Format: keine Nachkommastelle
8910    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
8911        ctx.format_check("SEQ", 1, 0, |val| validate_max_decimal_places(val, 0))
8912    }
8913
8914    /// [938] Format: Möglicher Wert: <= 10
8915    fn evaluate_938(&self, _ctx: &EvaluationContext) -> ConditionResult {
8916        ConditionResult::True
8917    }
8918
8919    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
8920    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
8921        ctx.format_check("COM", 0, 0, validate_email)
8922    }
8923
8924    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
8925    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
8926        ctx.format_check("COM", 0, 0, validate_phone)
8927    }
8928
8929    /// [942] Format: n1-n2-n1-n3
8930    fn evaluate_942(&self, _ctx: &EvaluationContext) -> ConditionResult {
8931        // Format condition — informational, always applies
8932        ConditionResult::True
8933    }
8934
8935    /// [943] Format: n1-n2-n1
8936    fn evaluate_943(&self, _ctx: &EvaluationContext) -> ConditionResult {
8937        // Format condition — informational, always applies
8938        ConditionResult::True
8939    }
8940
8941    /// [946] Format: max. 11 Nachkommastellen
8942    fn evaluate_946(&self, _ctx: &EvaluationContext) -> ConditionResult {
8943        // Format condition — informational, always applies
8944        ConditionResult::True
8945    }
8946
8947    /// [948] Format: n1-n2-n1-n8-n2
8948    fn evaluate_948(&self, _ctx: &EvaluationContext) -> ConditionResult {
8949        // Format condition — informational, always applies
8950        ConditionResult::True
8951    }
8952
8953    /// [950] Format: Marktlokations-ID
8954    fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
8955        // Format condition — informational, always applies
8956        ConditionResult::True
8957    }
8958
8959    /// [951] Format: Zählpunktbezeichnung
8960    fn evaluate_951(&self, _ctx: &EvaluationContext) -> ConditionResult {
8961        // Format condition — informational, always applies
8962        ConditionResult::True
8963    }
8964
8965    /// [952] Format: Gerätenummer nach DIN 43863-5
8966    fn evaluate_952(&self, _ctx: &EvaluationContext) -> ConditionResult {
8967        // Format condition — informational, always applies
8968        ConditionResult::True
8969    }
8970
8971    /// [955] Format: Möglicher Wert: < 100
8972    fn evaluate_955(&self, _ctx: &EvaluationContext) -> ConditionResult {
8973        // Format condition — informational, always applies
8974        ConditionResult::True
8975    }
8976
8977    /// [957] Format: n1-n2-n1-n8
8978    fn evaluate_957(&self, _ctx: &EvaluationContext) -> ConditionResult {
8979        // Format condition — informational, always applies
8980        ConditionResult::True
8981    }
8982
8983    /// [960] Format: Netzlokations-ID
8984    fn evaluate_960(&self, _ctx: &EvaluationContext) -> ConditionResult {
8985        // Format condition — informational, always applies
8986        ConditionResult::True
8987    }
8988
8989    /// [961] Format: SR-ID
8990    fn evaluate_961(&self, _ctx: &EvaluationContext) -> ConditionResult {
8991        // Format condition — informational, always applies
8992        ConditionResult::True
8993    }
8994
8995    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
8996    fn evaluate_967(&self, _ctx: &EvaluationContext) -> ConditionResult {
8997        // Format condition — informational, always applies
8998        ConditionResult::True
8999    }
9000
9001    /// [2001] Segmentgruppe ist mindestens zweimal je SG4 IDE+24 (Vorgang) anzugeben
9002    fn evaluate_2001(&self, ctx: &EvaluationContext) -> ConditionResult {
9003        // Cardinality: at least twice per SG4 IDE+24
9004        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9005        ConditionResult::from(ide_count > 0)
9006    }
9007
9008    /// [2002] Für jede Produktpaket-ID im SG8 SEQ+Z79 (Erforderliches Produkt) DE1050 genau einmal anzugeben
9009    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
9010        let seq_segs = ctx.find_segments("SEQ");
9011        let has_z79 = seq_segs.iter().any(|s| {
9012            s.elements
9013                .first()
9014                .and_then(|e| e.first())
9015                .is_some_and(|v| v == "Z79")
9016        });
9017        ConditionResult::from(has_z79)
9018    }
9019
9020    /// [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
9021    fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
9022        // TODO: implement
9023        ConditionResult::Unknown
9024    }
9025
9026    /// [2004] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
9027    // 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)
9028    fn evaluate_2004(&self, ctx: &EvaluationContext) -> ConditionResult {
9029        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
9030        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
9031
9032        let zeitraum_ids: Vec<String> = rff_qualifiers
9033            .iter()
9034            .filter(|(_, qual)| qual.as_str() == "Z49")
9035            .filter_map(|(idx, _)| {
9036                rff_zeitraum_ids
9037                    .iter()
9038                    .find(|(i, v)| i == idx && !v.is_empty())
9039                    .map(|(_, v)| v.clone())
9040            })
9041            .collect();
9042
9043        if zeitraum_ids.is_empty() {
9044            return ConditionResult::Unknown;
9045        }
9046
9047        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
9048
9049        for zid in &zeitraum_ids {
9050            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
9051            if count != 1 {
9052                return ConditionResult::False;
9053            }
9054        }
9055
9056        ConditionResult::True
9057    }
9058
9059    /// [2005] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
9060    // 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)
9061    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
9062        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
9063        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
9064
9065        let zeitraum_ids: Vec<String> = rff_qualifiers
9066            .iter()
9067            .filter(|(_, qual)| qual.as_str() == "Z49")
9068            .filter_map(|(idx, _)| {
9069                rff_zeitraum_ids
9070                    .iter()
9071                    .find(|(i, v)| i == idx && !v.is_empty())
9072                    .map(|(_, v)| v.clone())
9073            })
9074            .collect();
9075
9076        if zeitraum_ids.is_empty() {
9077            return ConditionResult::Unknown;
9078        }
9079
9080        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
9081
9082        for zid in &zeitraum_ids {
9083            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
9084            if count == 0 {
9085                return ConditionResult::False;
9086            }
9087        }
9088
9089        ConditionResult::True
9090    }
9091
9092    /// [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 ...
9093    fn evaluate_2010(&self, _ctx: &EvaluationContext) -> ConditionResult {
9094        // TODO: implement
9095        ConditionResult::Unknown
9096    }
9097
9098    /// [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 ...
9099    /// EXTERNAL: Requires context from outside the message.
9100    // 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)
9101    fn evaluate_2011(&self, ctx: &EvaluationContext) -> ConditionResult {
9102        ctx.external
9103            .evaluate("request_message_zeitraum_ids_present")
9104    }
9105
9106    /// [2012] Segmentgruppe ist genau einmal für die Angabe der Informativen Daten der Marktlokation anzugeben
9107    fn evaluate_2012(&self, _ctx: &EvaluationContext) -> ConditionResult {
9108        // TODO: implement
9109        ConditionResult::Unknown
9110    }
9111
9112    /// [2013] Mindesten einmal anzugeben wenn kein SG6 RFF+Z54 (Verwendungszeitraum der Daten: Im System keine Daten vorhanden) vorhanden
9113    fn evaluate_2013(&self, ctx: &EvaluationContext) -> ConditionResult {
9114        // Check: condition applies when no RFF+Z54 exists
9115        ctx.lacks_qualifier("RFF", 0, "Z54")
9116    }
9117
9118    /// [2014] Mindesten einmal anzugeben wenn kein SG6 RFF+Z47 (Verwendungszeitraum der Daten: Im System vorhandene Daten) vorhanden
9119    fn evaluate_2014(&self, ctx: &EvaluationContext) -> ConditionResult {
9120        // Check: condition applies when no RFF+Z47 exists
9121        ctx.lacks_qualifier("RFF", 0, "Z47")
9122    }
9123
9124    /// [2015] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) untergeordnet ist
9125    fn evaluate_2015(&self, _ctx: &EvaluationContext) -> ConditionResult {
9126        // TODO: implement
9127        ConditionResult::Unknown
9128    }
9129
9130    /// [2016] Je SG5 LOC+Z16 (Marktlokation) ist genau einmal die Segmentgruppe anzugeben
9131    fn evaluate_2016(&self, ctx: &EvaluationContext) -> ConditionResult {
9132        // Cardinality: exactly once per SG5 LOC+Z16 (Marktlokation)
9133        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9134        ConditionResult::from(loc_count > 0)
9135    }
9136
9137    /// [2017] Je SG5 LOC+Z17 (Messlokation) ist genau einmal die Segmentgruppe anzugeben
9138    fn evaluate_2017(&self, ctx: &EvaluationContext) -> ConditionResult {
9139        // Cardinality: exactly once per SG5 LOC+Z17 (Messlokation)
9140        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9141        ConditionResult::from(loc_count > 0)
9142    }
9143
9144    /// [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...
9145    fn evaluate_2018(&self, _ctx: &EvaluationContext) -> ConditionResult {
9146        // TODO: implement
9147        ConditionResult::Unknown
9148    }
9149
9150    /// [2060] Wenn SG10 CAV+NZR ist die Segmentgruppe genau zwei Mal je IDE+24 anzugeben
9151    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
9152        // Check: CAV+NZR exists (then segment group exactly twice per IDE+24)
9153        let found = ctx.find_segments("CAV").iter().any(|s| {
9154            s.elements
9155                .first()
9156                .and_then(|e| e.first())
9157                .is_some_and(|v| v == "NZR")
9158        });
9159        ConditionResult::from(found)
9160    }
9161
9162    /// [2061] Segment bzw. Segmentgruppe ist genau einmal je SG4 IDE (Vorgang) anzugeben
9163    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
9164        // Cardinality: exactly once per SG4 IDE (always true for single-transaction messages)
9165        let ide_count = ctx.find_segments("IDE").len();
9166        ConditionResult::from(ide_count > 0)
9167    }
9168
9169    /// [2071] Für die ID der LieferantensummenZR einmal je SG4 IDE+24 (Vorgang)
9170    fn evaluate_2071(&self, ctx: &EvaluationContext) -> ConditionResult {
9171        // Cardinality: LieferantensummenZR once per SG4 IDE+24
9172        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9173        ConditionResult::from(ide_count > 0)
9174    }
9175
9176    /// [2073] Für die ID der BilanzkreissummenZR einmal je SG4 IDE+24 (Vorgang)
9177    fn evaluate_2073(&self, ctx: &EvaluationContext) -> ConditionResult {
9178        // Cardinality: BilanzkreissummenZR once per SG4 IDE+24
9179        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9180        ConditionResult::from(ide_count > 0)
9181    }
9182
9183    /// [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)
9184    fn evaluate_2075(&self, ctx: &EvaluationContext) -> ConditionResult {
9185        // Cardinality: Zeitreihen (not EEG-EUZ/AAÜZ) once per SG4 IDE+24
9186        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9187        ConditionResult::from(ide_count > 0)
9188    }
9189
9190    /// [2080] Segmentgruppe ist max. zweimal je SG4 IDE+24 (Vorgang) anzugeben
9191    fn evaluate_2080(&self, ctx: &EvaluationContext) -> ConditionResult {
9192        // Cardinality: max twice per SG4 IDE+24
9193        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9194        ConditionResult::from(ide_count > 0)
9195    }
9196
9197    /// [2095] Je SG5 LOC+Z15 (MaBiS-Zählpunkt) ist genau einmal die Segmentgruppe anzugeben
9198    fn evaluate_2095(&self, ctx: &EvaluationContext) -> ConditionResult {
9199        // Cardinality: exactly once per SG5 LOC+Z15 (MaBiS-Zählpunkt)
9200        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z15").len();
9201        ConditionResult::from(loc_count > 0)
9202    }
9203
9204    /// [2096] Segmentgruppe ist genau zweimal je SG4 IDE anzugeben
9205    fn evaluate_2096(&self, ctx: &EvaluationContext) -> ConditionResult {
9206        // Cardinality: exactly twice per SG4 IDE
9207        let ide_count = ctx.find_segments("IDE").len();
9208        ConditionResult::from(ide_count > 0)
9209    }
9210
9211    /// [2119] Je SG8 SEQ+Z13/ ZG0 (Smartmeter-Gateway) ist genau einmal die Segmentgruppe anzugeben
9212    fn evaluate_2119(&self, ctx: &EvaluationContext) -> ConditionResult {
9213        // Cardinality: exactly once per SG8 SEQ+Z13/ZG0 (Smartmeter-Gateway)
9214        let found = ctx.find_segments("SEQ").iter().any(|s| {
9215            s.elements
9216                .first()
9217                .and_then(|e| e.first())
9218                .is_some_and(|v| v == "Z13" || v == "ZG0")
9219        });
9220        ConditionResult::from(found)
9221    }
9222
9223    /// [2140] Für die ID der LieferantensummenZR einmal je SG4 IDE+Z01 (Liste)
9224    fn evaluate_2140(&self, ctx: &EvaluationContext) -> ConditionResult {
9225        // Cardinality: LieferantensummenZR once per SG4 IDE+Z01
9226        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9227        ConditionResult::from(ide_count > 0)
9228    }
9229
9230    /// [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
9231    fn evaluate_2182(&self, ctx: &EvaluationContext) -> ConditionResult {
9232        // Cardinality: exactly once per SG8 SEQ+Z01/Z80/Z81/Z98
9233        let found = ctx.find_segments("SEQ").iter().any(|s| {
9234            s.elements
9235                .first()
9236                .and_then(|e| e.first())
9237                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9238        });
9239        ConditionResult::from(found)
9240    }
9241
9242    /// [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
9243    fn evaluate_2183(&self, ctx: &EvaluationContext) -> ConditionResult {
9244        // Cardinality: exactly twice per SG8 SEQ+Z01/Z80/Z81/Z98
9245        let found = ctx.find_segments("SEQ").iter().any(|s| {
9246            s.elements
9247                .first()
9248                .and_then(|e| e.first())
9249                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9250        });
9251        ConditionResult::from(found)
9252    }
9253
9254    /// [2207] Für die ID der Lieferanten-ausfall-arbeits-summen-zeitreihe einmal je SG4 IDE+Z01 (Liste)
9255    fn evaluate_2207(&self, ctx: &EvaluationContext) -> ConditionResult {
9256        // Cardinality: Lieferantenausfallarbeitssummenzeitreihe once per SG4 IDE+Z01
9257        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9258        ConditionResult::from(ide_count > 0)
9259    }
9260
9261    /// [2225] Einmal für jede Marktlokation bzw. Tranche für die der LF nicht die gemeldete Ansicht des NB teilt
9262    fn evaluate_2225(&self, _ctx: &EvaluationContext) -> ConditionResult {
9263        // TODO: implement
9264        ConditionResult::Unknown
9265    }
9266
9267    /// [2236] Code einmal je SG4 IDE+24 (Vorgang)
9268    fn evaluate_2236(&self, ctx: &EvaluationContext) -> ConditionResult {
9269        // Cardinality: code once per SG4 IDE+24
9270        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9271        ConditionResult::from(ide_count > 0)
9272    }
9273
9274    /// [2252] Einmal für jede Marktlokation bzw. Tranche, die in der Lieferantenausfallarbeits-summenzeitreihe berücksichtigt wurde
9275    fn evaluate_2252(&self, _ctx: &EvaluationContext) -> ConditionResult {
9276        // TODO: implement
9277        ConditionResult::Unknown
9278    }
9279
9280    /// [2261] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9281    fn evaluate_2261(&self, ctx: &EvaluationContext) -> ConditionResult {
9282        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9283        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9284        ConditionResult::from(loc_count > 0)
9285    }
9286
9287    /// [2284] Für jede Messlokations-ID im SG5 LOC+Z17 (Messlokation) DE3225 genau einmal anzugeben
9288    fn evaluate_2284(&self, ctx: &EvaluationContext) -> ConditionResult {
9289        // Cardinality: exactly once per LOC+Z17 (Messlokation) ID
9290        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9291        ConditionResult::from(loc_count > 0)
9292    }
9293
9294    /// [2286] Für jede SEQ+ZF3 (Daten der Messlokation) mindestens einmal anzugeben
9295    fn evaluate_2286(&self, ctx: &EvaluationContext) -> ConditionResult {
9296        // Cardinality: at least once per SEQ+ZF3 (Daten der Messlokation)
9297        let found = !ctx.find_segments_with_qualifier("SEQ", 0, "ZF3").is_empty();
9298        ConditionResult::from(found)
9299    }
9300
9301    /// [2287] Für jede SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) mindestens einmal anzugeben
9302    fn evaluate_2287(&self, ctx: &EvaluationContext) -> ConditionResult {
9303        // Cardinality: at least once per SEQ+Z03/ZF5 (Zähleinrichtungsdaten)
9304        let found = ctx.find_segments("SEQ").iter().any(|s| {
9305            s.elements
9306                .first()
9307                .and_then(|e| e.first())
9308                .is_some_and(|v| v == "Z03" || v == "ZF5")
9309        });
9310        ConditionResult::from(found)
9311    }
9312
9313    /// [2288] Einmal für jede Zeitreihe, die in der DZR berücksichtigt wurde
9314    fn evaluate_2288(&self, _ctx: &EvaluationContext) -> ConditionResult {
9315        // TODO: implement
9316        ConditionResult::Unknown
9317    }
9318
9319    /// [2307] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225 genau einmal anzugeben
9320    fn evaluate_2307(&self, ctx: &EvaluationContext) -> ConditionResult {
9321        // Cardinality: exactly once per LOC+Z21 (Tranche) ID
9322        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9323        ConditionResult::from(loc_count > 0)
9324    }
9325
9326    /// [2308] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225, mindestens einmal anzugeben
9327    fn evaluate_2308(&self, ctx: &EvaluationContext) -> ConditionResult {
9328        // Cardinality: at least once per LOC+Z16 (Marktlokation) ID
9329        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9330        ConditionResult::from(loc_count > 0)
9331    }
9332
9333    /// [2309] Für jede ID im SG5 LOC+Z17 (Messlokation) DE3225 mindestens einmal anzugeben
9334    fn evaluate_2309(&self, ctx: &EvaluationContext) -> ConditionResult {
9335        // Cardinality: at least once per LOC+Z17 (Messlokation) ID
9336        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9337        ConditionResult::from(loc_count > 0)
9338    }
9339
9340    /// [2310] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225 genau einmal anzugeben
9341    fn evaluate_2310(&self, ctx: &EvaluationContext) -> ConditionResult {
9342        // Cardinality: exactly once per LOC+Z16 (Marktlokation) ID
9343        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9344        ConditionResult::from(loc_count > 0)
9345    }
9346
9347    /// [2311] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9348    fn evaluate_2311(&self, ctx: &EvaluationContext) -> ConditionResult {
9349        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9350        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9351        ConditionResult::from(loc_count > 0)
9352    }
9353
9354    /// [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...
9355    fn evaluate_2312(&self, ctx: &EvaluationContext) -> ConditionResult {
9356        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9357        // Check: RFF+Z33 DE1154 does NOT contain Objektcode 9992000001256 (Netzlokation)
9358        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
9359        let has_netzlokation = rff_z33.iter().any(|s| {
9360            s.elements
9361                .first()
9362                .and_then(|e| e.get(1))
9363                .is_some_and(|v| v == "9992000001256")
9364        });
9365        ConditionResult::from(!has_netzlokation)
9366    }
9367
9368    /// [2313] Je SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels) genau einmal anzugeben
9369    fn evaluate_2313(&self, ctx: &EvaluationContext) -> ConditionResult {
9370        // Check: SG8 with SEQ+Z58/ZC9/ZD0/ZD6 exists (cardinality constraint)
9371        let found = ctx.find_segments("SEQ").iter().any(|s| {
9372            s.elements
9373                .first()
9374                .and_then(|e| e.first())
9375                .is_some_and(|v| ["Z58", "ZC9", "ZD0", "ZD6"].contains(&v.as_str()))
9376        });
9377        ConditionResult::from(found)
9378    }
9379
9380    /// [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
9381    fn evaluate_2317(&self, ctx: &EvaluationContext) -> ConditionResult {
9382        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9383        // Check: SG10 CCI+++Z25 has CAV+MIW/MPW/MUW (Wandler types)
9384        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9385            s.elements
9386                .first()
9387                .and_then(|e| e.first())
9388                .is_some_and(|v| ["MIW", "MPW", "MUW"].contains(&v.as_str()))
9389        });
9390        ConditionResult::from(has_cav)
9391    }
9392
9393    /// [2318] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MBW (Blockstromwandler) vorhanden, ist das Segment genau einmal anzugeben
9394    fn evaluate_2318(&self, ctx: &EvaluationContext) -> ConditionResult {
9395        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9396        // Check: SG10 CCI+++Z25 has CAV+MBW (Blockstromwandler)
9397        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9398            s.elements
9399                .first()
9400                .and_then(|e| e.first())
9401                .is_some_and(|v| v == "MBW")
9402        });
9403        ConditionResult::from(has_cav)
9404    }
9405
9406    /// [2344] Einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
9407    fn evaluate_2344(&self, _ctx: &EvaluationContext) -> ConditionResult {
9408        // TODO: implement
9409        ConditionResult::Unknown
9410    }
9411
9412    /// [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...
9413    fn evaluate_2350(&self, _ctx: &EvaluationContext) -> ConditionResult {
9414        // TODO: implement
9415        ConditionResult::Unknown
9416    }
9417
9418    /// [2351] Einmal je ZP einer BK-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufgenommen wurde
9419    fn evaluate_2351(&self, _ctx: &EvaluationContext) -> ConditionResult {
9420        // TODO: implement
9421        ConditionResult::Unknown
9422    }
9423
9424    /// [2352] Einmal je ZP einer BG-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufge-nommen wurde
9425    fn evaluate_2352(&self, _ctx: &EvaluationContext) -> ConditionResult {
9426        // TODO: implement
9427        ConditionResult::Unknown
9428    }
9429
9430    /// [2356] Je SG5 LOC+Z18 (Netzlokation) ist genau einmal die Segmentgruppe anzugeben
9431    fn evaluate_2356(&self, ctx: &EvaluationContext) -> ConditionResult {
9432        // Cardinality: exactly once per SG5 LOC+Z18 (Netzlokation)
9433        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9434        ConditionResult::from(loc_count > 0)
9435    }
9436
9437    /// [2357] Je SG5 LOC+Z20 (Technische Ressource) ist genau einmal die Segmentgruppe anzugeben
9438    fn evaluate_2357(&self, ctx: &EvaluationContext) -> ConditionResult {
9439        // Cardinality: exactly once per SG5 LOC+Z20 (Technische Ressource)
9440        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z20").len();
9441        ConditionResult::from(loc_count > 0)
9442    }
9443
9444    /// [2358] Je SG5 LOC+Z19 (Steuerbare Ressource) ist genau einmal die Segmentgruppe anzugeben
9445    fn evaluate_2358(&self, ctx: &EvaluationContext) -> ConditionResult {
9446        // Cardinality: exactly once per SG5 LOC+Z19 (Steuerbare Ressource)
9447        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z19").len();
9448        ConditionResult::from(loc_count > 0)
9449    }
9450
9451    /// [2359] Für jede ID im SG5 LOC+Z16 / Z17 / Z20 (Marktlokation / Messlokation / Technische Ressource) DE3225 mindestens einmal anzugeben
9452    fn evaluate_2359(&self, ctx: &EvaluationContext) -> ConditionResult {
9453        // Cardinality: at least once per LOC+Z16/Z17/Z20 ID
9454        let found = ctx.find_segments("LOC").iter().any(|s| {
9455            s.elements
9456                .first()
9457                .and_then(|e| e.first())
9458                .is_some_and(|v| ["Z16", "Z17", "Z20"].contains(&v.as_str()))
9459        });
9460        ConditionResult::from(found)
9461    }
9462
9463    /// [2360] Für jede ID im SG5 LOC+Z18 (Netzlokation) DE3225 mindestens einmal anzugeben
9464    fn evaluate_2360(&self, ctx: &EvaluationContext) -> ConditionResult {
9465        // Cardinality: at least once per LOC+Z18 (Netzlokation) ID
9466        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9467        ConditionResult::from(loc_count > 0)
9468    }
9469
9470    /// [2361] Für jede ID im SG5 LOC+Z17 / Z20 / Z22 (Messlokation / Technische Ressource / ruhende Marktlokation) DE3225 mindestens einmal anzugeben
9471    fn evaluate_2361(&self, ctx: &EvaluationContext) -> ConditionResult {
9472        // Cardinality: at least once per LOC+Z17/Z20/Z22 ID
9473        let found = ctx.find_segments("LOC").iter().any(|s| {
9474            s.elements
9475                .first()
9476                .and_then(|e| e.first())
9477                .is_some_and(|v| ["Z17", "Z20", "Z22"].contains(&v.as_str()))
9478        });
9479        ConditionResult::from(found)
9480    }
9481}
9482
9483// HAND-EDITED: helpers for [223] / [265].
9484
9485use mig_types::segment::OwnedSegment;
9486
9487/// One SG8 repetition: its SEQ qualifier, the Marktlokation IDs of its
9488/// RFF+Z18, and its SG10s as `(CCIs, CAVs)`.
9489struct Sg8Block {
9490    seq: String,
9491    malo_refs: Vec<String>,
9492    sg10: Vec<(Vec<OwnedSegment>, Vec<OwnedSegment>)>,
9493}
9494
9495fn component(seg: &OwnedSegment, element: usize, comp: usize) -> Option<&str> {
9496    seg.elements
9497        .get(element)
9498        .and_then(|e| e.get(comp))
9499        .map(String::as_str)
9500}
9501
9502/// The message's SG8 repetitions, from the group navigator when there is one,
9503/// else from the segments in message order: SEQ opens an SG8, CCI opens an
9504/// SG10 within it that takes the CAVs after it. (`validate_pid_with_conditions`
9505/// evaluates BO4E input without a navigator.) `None` without either.
9506fn sg8_blocks(ctx: &EvaluationContext) -> Option<Vec<Sg8Block>> {
9507    let malo_refs = |rffs: &[OwnedSegment]| -> Vec<String> {
9508        rffs.iter()
9509            .filter(|s| component(s, 0, 0) == Some("Z18"))
9510            .filter_map(|s| component(s, 0, 1).map(str::to_string))
9511            .collect()
9512    };
9513    if let Some(nav) = ctx.navigator() {
9514        let sg8 = ["SG4", "SG8"];
9515        return Some(
9516            (0..nav.group_instance_count(&sg8))
9517                .map(|i| Sg8Block {
9518                    seq: nav
9519                        .find_segments_in_group("SEQ", &sg8, i)
9520                        .first()
9521                        .and_then(|s| component(s, 0, 0).map(str::to_string))
9522                        .unwrap_or_default(),
9523                    malo_refs: malo_refs(&nav.find_segments_in_group("RFF", &sg8, i)),
9524                    sg10: (0..nav.child_group_instance_count(&sg8, i, "SG10"))
9525                        .map(|j| {
9526                            (
9527                                nav.find_segments_in_child_group("CCI", &sg8, i, "SG10", j),
9528                                nav.find_segments_in_child_group("CAV", &sg8, i, "SG10", j),
9529                            )
9530                        })
9531                        .collect(),
9532                })
9533                .collect(),
9534        );
9535    }
9536    if ctx.segments.is_empty() {
9537        return None;
9538    }
9539    let mut blocks: Vec<Sg8Block> = Vec::new();
9540    let mut rffs: Vec<OwnedSegment> = Vec::new();
9541    for seg in ctx.segments {
9542        match seg.id.as_str() {
9543            "SEQ" => {
9544                if let Some(b) = blocks.last_mut() {
9545                    b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9546                }
9547                rffs.clear();
9548                blocks.push(Sg8Block {
9549                    seq: component(seg, 0, 0).unwrap_or_default().to_string(),
9550                    malo_refs: Vec::new(),
9551                    sg10: Vec::new(),
9552                });
9553            }
9554            // Segments after the SG8s end the last one.
9555            "NAD" | "UNT" | "IDE" => {
9556                if let Some(b) = blocks.last_mut() {
9557                    if b.malo_refs.is_empty() {
9558                        b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9559                    }
9560                }
9561                rffs.clear();
9562                if seg.id == "IDE" {
9563                    continue;
9564                }
9565                break;
9566            }
9567            "RFF" if !blocks.is_empty() => rffs.push(seg.clone()),
9568            "CCI" => {
9569                if let Some(b) = blocks.last_mut() {
9570                    b.sg10.push((vec![seg.clone()], Vec::new()));
9571                }
9572            }
9573            "CAV" => {
9574                if let Some((_, cavs)) = blocks.last_mut().and_then(|b| b.sg10.last_mut()) {
9575                    cavs.push(seg.clone());
9576                }
9577            }
9578            _ => {}
9579        }
9580    }
9581    if let Some(b) = blocks.last_mut() {
9582        if b.malo_refs.is_empty() {
9583            b.malo_refs = malo_refs(&rffs);
9584        }
9585    }
9586    Some(blocks)
9587}