Skip to main content

automapper_validation/generated/fv2504/
utilmd_strom_conditions_fv2504.rs

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