Skip to main content

automapper_validation/generated/fv2504/
utilmd_strom_conditions_fv2504.rs

1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2504/UTILMD_AHB_Strom_2_1_Fehlerkorrektur_20250623.xml
4// Generated: 2026-03-12T10:15:42Z
5// </auto-generated>
6
7// LLM-produced bodies aren't idiomatic Rust — silence stylistic lints at
8// file scope so `cargo clippy -D warnings` stays green. Correctness,
9// suspicious, and perf categories stay on so real bugs still surface.
10#![allow(clippy::style, clippy::complexity)]
11
12#[allow(unused_imports)]
13use crate::eval::format_validators::*;
14// HAND-EDITED: instances of one SG4 compared with each other.
15use crate::eval::sg4_instances;
16use crate::eval::{Cluster, ConditionEvaluator, ConditionResult, EvaluationContext};
17// HAND-EDITED: shared cluster helpers for conditions [359]/[360]/[366]/[368].
18// Preserve across regeneration — see CLAUDE.md "Codelist Providers".
19use crate::eval::ebd_cluster::{all_sts_e01_in_cluster, ebd_ablehnung_except};
20
21/// Generated condition evaluator for UTILMD_Strom FV2504.
22pub struct UtilmdStromConditionEvaluatorFV2504 {
23    // External condition IDs that require runtime context.
24    external_conditions: std::collections::HashSet<u32>,
25}
26
27impl Default for UtilmdStromConditionEvaluatorFV2504 {
28    fn default() -> Self {
29        let mut external_conditions = std::collections::HashSet::new();
30        external_conditions.insert(1);
31        external_conditions.insert(4);
32        external_conditions.insert(5);
33        external_conditions.insert(6);
34        external_conditions.insert(8);
35        external_conditions.insert(9);
36        external_conditions.insert(14);
37        external_conditions.insert(31);
38        external_conditions.insert(33);
39        external_conditions.insert(36);
40        external_conditions.insert(38);
41        external_conditions.insert(39);
42        external_conditions.insert(40);
43        external_conditions.insert(75);
44        external_conditions.insert(98);
45        external_conditions.insert(99);
46        external_conditions.insert(151);
47        external_conditions.insert(233);
48        external_conditions.insert(241);
49        external_conditions.insert(251);
50        external_conditions.insert(292);
51        external_conditions.insert(314);
52        external_conditions.insert(315);
53        external_conditions.insert(323);
54        external_conditions.insert(412);
55        external_conditions.insert(425);
56        external_conditions.insert(426);
57        external_conditions.insert(427);
58        external_conditions.insert(428);
59        external_conditions.insert(429);
60        external_conditions.insert(433);
61        external_conditions.insert(435);
62        external_conditions.insert(453);
63        external_conditions.insert(459);
64        external_conditions.insert(460);
65        external_conditions.insert(706);
66        external_conditions.insert(717);
67        external_conditions.insert(2011);
68        Self {
69            external_conditions,
70        }
71    }
72}
73
74impl ConditionEvaluator for UtilmdStromConditionEvaluatorFV2504 {
75    fn message_type(&self) -> &str {
76        "UTILMD_Strom"
77    }
78
79    fn format_version(&self) -> &str {
80        "FV2504"
81    }
82
83    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
84        match condition {
85            1 => self.evaluate_1(ctx),
86            4 => self.evaluate_4(ctx),
87            5 => self.evaluate_5(ctx),
88            6 => self.evaluate_6(ctx),
89            7 => self.evaluate_7(ctx),
90            8 => self.evaluate_8(ctx),
91            9 => self.evaluate_9(ctx),
92            10 => self.evaluate_10(ctx),
93            11 => self.evaluate_11(ctx),
94            12 => self.evaluate_12(ctx),
95            13 => self.evaluate_13(ctx),
96            14 => self.evaluate_14(ctx),
97            15 => self.evaluate_15(ctx),
98            16 => self.evaluate_16(ctx),
99            17 => self.evaluate_17(ctx),
100            18 => self.evaluate_18(ctx),
101            19 => self.evaluate_19(ctx),
102            20 => self.evaluate_20(ctx),
103            21 => self.evaluate_21(ctx),
104            22 => self.evaluate_22(ctx),
105            23 => self.evaluate_23(ctx),
106            24 => self.evaluate_24(ctx),
107            25 => self.evaluate_25(ctx),
108            27 => self.evaluate_27(ctx),
109            30 => self.evaluate_30(ctx),
110            31 => self.evaluate_31(ctx),
111            32 => self.evaluate_32(ctx),
112            33 => self.evaluate_33(ctx),
113            34 => self.evaluate_34(ctx),
114            35 => self.evaluate_35(ctx),
115            36 => self.evaluate_36(ctx),
116            37 => self.evaluate_37(ctx),
117            38 => self.evaluate_38(ctx),
118            39 => self.evaluate_39(ctx),
119            40 => self.evaluate_40(ctx),
120            41 => self.evaluate_41(ctx),
121            42 => self.evaluate_42(ctx),
122            43 => self.evaluate_43(ctx),
123            44 => self.evaluate_44(ctx),
124            45 => self.evaluate_45(ctx),
125            46 => self.evaluate_46(ctx),
126            47 => self.evaluate_47(ctx),
127            48 => self.evaluate_48(ctx),
128            49 => self.evaluate_49(ctx),
129            50 => self.evaluate_50(ctx),
130            51 => self.evaluate_51(ctx),
131            52 => self.evaluate_52(ctx),
132            53 => self.evaluate_53(ctx),
133            54 => self.evaluate_54(ctx),
134            55 => self.evaluate_55(ctx),
135            56 => self.evaluate_56(ctx),
136            57 => self.evaluate_57(ctx),
137            58 => self.evaluate_58(ctx),
138            60 => self.evaluate_60(ctx),
139            61 => self.evaluate_61(ctx),
140            62 => self.evaluate_62(ctx),
141            63 => self.evaluate_63(ctx),
142            64 => self.evaluate_64(ctx),
143            65 => self.evaluate_65(ctx),
144            66 => self.evaluate_66(ctx),
145            67 => self.evaluate_67(ctx),
146            68 => self.evaluate_68(ctx),
147            69 => self.evaluate_69(ctx),
148            70 => self.evaluate_70(ctx),
149            71 => self.evaluate_71(ctx),
150            72 => self.evaluate_72(ctx),
151            73 => self.evaluate_73(ctx),
152            74 => self.evaluate_74(ctx),
153            75 => self.evaluate_75(ctx),
154            76 => self.evaluate_76(ctx),
155            77 => self.evaluate_77(ctx),
156            78 => self.evaluate_78(ctx),
157            83 => self.evaluate_83(ctx),
158            84 => self.evaluate_84(ctx),
159            85 => self.evaluate_85(ctx),
160            86 => self.evaluate_86(ctx),
161            87 => self.evaluate_87(ctx),
162            88 => self.evaluate_88(ctx),
163            89 => self.evaluate_89(ctx),
164            90 => self.evaluate_90(ctx),
165            91 => self.evaluate_91(ctx),
166            92 => self.evaluate_92(ctx),
167            93 => self.evaluate_93(ctx),
168            94 => self.evaluate_94(ctx),
169            95 => self.evaluate_95(ctx),
170            96 => self.evaluate_96(ctx),
171            97 => self.evaluate_97(ctx),
172            98 => self.evaluate_98(ctx),
173            99 => self.evaluate_99(ctx),
174            100 => self.evaluate_100(ctx),
175            101 => self.evaluate_101(ctx),
176            102 => self.evaluate_102(ctx),
177            103 => self.evaluate_103(ctx),
178            104 => self.evaluate_104(ctx),
179            105 => self.evaluate_105(ctx),
180            106 => self.evaluate_106(ctx),
181            107 => self.evaluate_107(ctx),
182            108 => self.evaluate_108(ctx),
183            110 => self.evaluate_110(ctx),
184            111 => self.evaluate_111(ctx),
185            112 => self.evaluate_112(ctx),
186            113 => self.evaluate_113(ctx),
187            114 => self.evaluate_114(ctx),
188            115 => self.evaluate_115(ctx),
189            116 => self.evaluate_116(ctx),
190            117 => self.evaluate_117(ctx),
191            118 => self.evaluate_118(ctx),
192            119 => self.evaluate_119(ctx),
193            120 => self.evaluate_120(ctx),
194            121 => self.evaluate_121(ctx),
195            122 => self.evaluate_122(ctx),
196            123 => self.evaluate_123(ctx),
197            124 => self.evaluate_124(ctx),
198            125 => self.evaluate_125(ctx),
199            126 => self.evaluate_126(ctx),
200            127 => self.evaluate_127(ctx),
201            128 => self.evaluate_128(ctx),
202            129 => self.evaluate_129(ctx),
203            130 => self.evaluate_130(ctx),
204            131 => self.evaluate_131(ctx),
205            132 => self.evaluate_132(ctx),
206            133 => self.evaluate_133(ctx),
207            134 => self.evaluate_134(ctx),
208            135 => self.evaluate_135(ctx),
209            136 => self.evaluate_136(ctx),
210            137 => self.evaluate_137(ctx),
211            138 => self.evaluate_138(ctx),
212            139 => self.evaluate_139(ctx),
213            140 => self.evaluate_140(ctx),
214            141 => self.evaluate_141(ctx),
215            142 => self.evaluate_142(ctx),
216            143 => self.evaluate_143(ctx),
217            144 => self.evaluate_144(ctx),
218            145 => self.evaluate_145(ctx),
219            146 => self.evaluate_146(ctx),
220            147 => self.evaluate_147(ctx),
221            148 => self.evaluate_148(ctx),
222            149 => self.evaluate_149(ctx),
223            150 => self.evaluate_150(ctx),
224            151 => self.evaluate_151(ctx),
225            152 => self.evaluate_152(ctx),
226            153 => self.evaluate_153(ctx),
227            156 => self.evaluate_156(ctx),
228            161 => self.evaluate_161(ctx),
229            162 => self.evaluate_162(ctx),
230            163 => self.evaluate_163(ctx),
231            164 => self.evaluate_164(ctx),
232            165 => self.evaluate_165(ctx),
233            166 => self.evaluate_166(ctx),
234            167 => self.evaluate_167(ctx),
235            170 => self.evaluate_170(ctx),
236            172 => self.evaluate_172(ctx),
237            173 => self.evaluate_173(ctx),
238            174 => self.evaluate_174(ctx),
239            175 => self.evaluate_175(ctx),
240            176 => self.evaluate_176(ctx),
241            177 => self.evaluate_177(ctx),
242            178 => self.evaluate_178(ctx),
243            179 => self.evaluate_179(ctx),
244            180 => self.evaluate_180(ctx),
245            184 => self.evaluate_184(ctx),
246            190 => self.evaluate_190(ctx),
247            191 => self.evaluate_191(ctx),
248            192 => self.evaluate_192(ctx),
249            193 => self.evaluate_193(ctx),
250            194 => self.evaluate_194(ctx),
251            195 => self.evaluate_195(ctx),
252            196 => self.evaluate_196(ctx),
253            197 => self.evaluate_197(ctx),
254            198 => self.evaluate_198(ctx),
255            199 => self.evaluate_199(ctx),
256            201 => self.evaluate_201(ctx),
257            202 => self.evaluate_202(ctx),
258            203 => self.evaluate_203(ctx),
259            204 => self.evaluate_204(ctx),
260            205 => self.evaluate_205(ctx),
261            206 => self.evaluate_206(ctx),
262            209 => self.evaluate_209(ctx),
263            210 => self.evaluate_210(ctx),
264            212 => self.evaluate_212(ctx),
265            213 => self.evaluate_213(ctx),
266            215 => self.evaluate_215(ctx),
267            216 => self.evaluate_216(ctx),
268            217 => self.evaluate_217(ctx),
269            219 => self.evaluate_219(ctx),
270            220 => self.evaluate_220(ctx),
271            221 => self.evaluate_221(ctx),
272            223 => self.evaluate_223(ctx),
273            224 => self.evaluate_224(ctx),
274            227 => self.evaluate_227(ctx),
275            229 => self.evaluate_229(ctx),
276            232 => self.evaluate_232(ctx),
277            233 => self.evaluate_233(ctx),
278            234 => self.evaluate_234(ctx),
279            237 => self.evaluate_237(ctx),
280            238 => self.evaluate_238(ctx),
281            239 => self.evaluate_239(ctx),
282            240 => self.evaluate_240(ctx),
283            241 => self.evaluate_241(ctx),
284            243 => self.evaluate_243(ctx),
285            244 => self.evaluate_244(ctx),
286            248 => self.evaluate_248(ctx),
287            249 => self.evaluate_249(ctx),
288            251 => self.evaluate_251(ctx),
289            252 => self.evaluate_252(ctx),
290            254 => self.evaluate_254(ctx),
291            255 => self.evaluate_255(ctx),
292            256 => self.evaluate_256(ctx),
293            257 => self.evaluate_257(ctx),
294            259 => self.evaluate_259(ctx),
295            261 => self.evaluate_261(ctx),
296            262 => self.evaluate_262(ctx),
297            265 => self.evaluate_265(ctx),
298            266 => self.evaluate_266(ctx),
299            267 => self.evaluate_267(ctx),
300            268 => self.evaluate_268(ctx),
301            269 => self.evaluate_269(ctx),
302            270 => self.evaluate_270(ctx),
303            273 => self.evaluate_273(ctx),
304            279 => self.evaluate_279(ctx),
305            280 => self.evaluate_280(ctx),
306            282 => self.evaluate_282(ctx),
307            284 => self.evaluate_284(ctx),
308            285 => self.evaluate_285(ctx),
309            286 => self.evaluate_286(ctx),
310            287 => self.evaluate_287(ctx),
311            288 => self.evaluate_288(ctx),
312            291 => self.evaluate_291(ctx),
313            292 => self.evaluate_292(ctx),
314            293 => self.evaluate_293(ctx),
315            294 => self.evaluate_294(ctx),
316            295 => self.evaluate_295(ctx),
317            296 => self.evaluate_296(ctx),
318            297 => self.evaluate_297(ctx),
319            298 => self.evaluate_298(ctx),
320            299 => self.evaluate_299(ctx),
321            300 => self.evaluate_300(ctx),
322            301 => self.evaluate_301(ctx),
323            302 => self.evaluate_302(ctx),
324            303 => self.evaluate_303(ctx),
325            304 => self.evaluate_304(ctx),
326            305 => self.evaluate_305(ctx),
327            306 => self.evaluate_306(ctx),
328            307 => self.evaluate_307(ctx),
329            309 => self.evaluate_309(ctx),
330            314 => self.evaluate_314(ctx),
331            315 => self.evaluate_315(ctx),
332            316 => self.evaluate_316(ctx),
333            317 => self.evaluate_317(ctx),
334            318 => self.evaluate_318(ctx),
335            321 => self.evaluate_321(ctx),
336            322 => self.evaluate_322(ctx),
337            323 => self.evaluate_323(ctx),
338            327 => self.evaluate_327(ctx),
339            329 => self.evaluate_329(ctx),
340            331 => self.evaluate_331(ctx),
341            332 => self.evaluate_332(ctx),
342            333 => self.evaluate_333(ctx),
343            334 => self.evaluate_334(ctx),
344            335 => self.evaluate_335(ctx),
345            336 => self.evaluate_336(ctx),
346            337 => self.evaluate_337(ctx),
347            338 => self.evaluate_338(ctx),
348            339 => self.evaluate_339(ctx),
349            340 => self.evaluate_340(ctx),
350            341 => self.evaluate_341(ctx),
351            342 => self.evaluate_342(ctx),
352            344 => self.evaluate_344(ctx),
353            345 => self.evaluate_345(ctx),
354            346 => self.evaluate_346(ctx),
355            347 => self.evaluate_347(ctx),
356            348 => self.evaluate_348(ctx),
357            349 => self.evaluate_349(ctx),
358            350 => self.evaluate_350(ctx),
359            351 => self.evaluate_351(ctx),
360            352 => self.evaluate_352(ctx),
361            355 => self.evaluate_355(ctx),
362            356 => self.evaluate_356(ctx),
363            357 => self.evaluate_357(ctx),
364            358 => self.evaluate_358(ctx),
365            359 => self.evaluate_359(ctx),
366            360 => self.evaluate_360(ctx),
367            363 => self.evaluate_363(ctx),
368            365 => self.evaluate_365(ctx),
369            366 => self.evaluate_366(ctx),
370            367 => self.evaluate_367(ctx),
371            368 => self.evaluate_368(ctx),
372            370 => self.evaluate_370(ctx),
373            371 => self.evaluate_371(ctx),
374            372 => self.evaluate_372(ctx),
375            373 => self.evaluate_373(ctx),
376            375 => self.evaluate_375(ctx),
377            376 => self.evaluate_376(ctx),
378            377 => self.evaluate_377(ctx),
379            378 => self.evaluate_378(ctx),
380            379 => self.evaluate_379(ctx),
381            380 => self.evaluate_380(ctx),
382            384 => self.evaluate_384(ctx),
383            386 => self.evaluate_386(ctx),
384            387 => self.evaluate_387(ctx),
385            388 => self.evaluate_388(ctx),
386            391 => self.evaluate_391(ctx),
387            392 => self.evaluate_392(ctx),
388            393 => self.evaluate_393(ctx),
389            394 => self.evaluate_394(ctx),
390            395 => self.evaluate_395(ctx),
391            396 => self.evaluate_396(ctx),
392            397 => self.evaluate_397(ctx),
393            398 => self.evaluate_398(ctx),
394            399 => self.evaluate_399(ctx),
395            401 => self.evaluate_401(ctx),
396            402 => self.evaluate_402(ctx),
397            403 => self.evaluate_403(ctx),
398            404 => self.evaluate_404(ctx),
399            405 => self.evaluate_405(ctx),
400            406 => self.evaluate_406(ctx),
401            407 => self.evaluate_407(ctx),
402            408 => self.evaluate_408(ctx),
403            409 => self.evaluate_409(ctx),
404            410 => self.evaluate_410(ctx),
405            411 => self.evaluate_411(ctx),
406            412 => self.evaluate_412(ctx),
407            413 => self.evaluate_413(ctx),
408            414 => self.evaluate_414(ctx),
409            415 => self.evaluate_415(ctx),
410            416 => self.evaluate_416(ctx),
411            417 => self.evaluate_417(ctx),
412            419 => self.evaluate_419(ctx),
413            420 => self.evaluate_420(ctx),
414            421 => self.evaluate_421(ctx),
415            425 => self.evaluate_425(ctx),
416            426 => self.evaluate_426(ctx),
417            427 => self.evaluate_427(ctx),
418            428 => self.evaluate_428(ctx),
419            429 => self.evaluate_429(ctx),
420            430 => self.evaluate_430(ctx),
421            431 => self.evaluate_431(ctx),
422            432 => self.evaluate_432(ctx),
423            433 => self.evaluate_433(ctx),
424            435 => self.evaluate_435(ctx),
425            436 => self.evaluate_436(ctx),
426            437 => self.evaluate_437(ctx),
427            438 => self.evaluate_438(ctx),
428            440 => self.evaluate_440(ctx),
429            441 => self.evaluate_441(ctx),
430            442 => self.evaluate_442(ctx),
431            444 => self.evaluate_444(ctx),
432            445 => self.evaluate_445(ctx),
433            446 => self.evaluate_446(ctx),
434            447 => self.evaluate_447(ctx),
435            448 => self.evaluate_448(ctx),
436            449 => self.evaluate_449(ctx),
437            450 => self.evaluate_450(ctx),
438            451 => self.evaluate_451(ctx),
439            452 => self.evaluate_452(ctx),
440            453 => self.evaluate_453(ctx),
441            454 => self.evaluate_454(ctx),
442            455 => self.evaluate_455(ctx),
443            456 => self.evaluate_456(ctx),
444            457 => self.evaluate_457(ctx),
445            458 => self.evaluate_458(ctx),
446            459 => self.evaluate_459(ctx),
447            460 => self.evaluate_460(ctx),
448            461 => self.evaluate_461(ctx),
449            462 => self.evaluate_462(ctx),
450            463 => self.evaluate_463(ctx),
451            465 => self.evaluate_465(ctx),
452            466 => self.evaluate_466(ctx),
453            467 => self.evaluate_467(ctx),
454            468 => self.evaluate_468(ctx),
455            469 => self.evaluate_469(ctx),
456            470 => self.evaluate_470(ctx),
457            471 => self.evaluate_471(ctx),
458            472 => self.evaluate_472(ctx),
459            473 => self.evaluate_473(ctx),
460            474 => self.evaluate_474(ctx),
461            475 => self.evaluate_475(ctx),
462            476 => self.evaluate_476(ctx),
463            477 => self.evaluate_477(ctx),
464            478 => self.evaluate_478(ctx),
465            479 => self.evaluate_479(ctx),
466            480 => self.evaluate_480(ctx),
467            481 => self.evaluate_481(ctx),
468            483 => self.evaluate_483(ctx),
469            484 => self.evaluate_484(ctx),
470            487 => self.evaluate_487(ctx),
471            489 => self.evaluate_489(ctx),
472            490 => self.evaluate_490(ctx),
473            491 => self.evaluate_491(ctx),
474            494 => self.evaluate_494(ctx),
475            500 => self.evaluate_500(ctx),
476            501 => self.evaluate_501(ctx),
477            502 => self.evaluate_502(ctx),
478            503 => self.evaluate_503(ctx),
479            504 => self.evaluate_504(ctx),
480            505 => self.evaluate_505(ctx),
481            506 => self.evaluate_506(ctx),
482            507 => self.evaluate_507(ctx),
483            508 => self.evaluate_508(ctx),
484            509 => self.evaluate_509(ctx),
485            510 => self.evaluate_510(ctx),
486            511 => self.evaluate_511(ctx),
487            512 => self.evaluate_512(ctx),
488            513 => self.evaluate_513(ctx),
489            514 => self.evaluate_514(ctx),
490            515 => self.evaluate_515(ctx),
491            516 => self.evaluate_516(ctx),
492            517 => self.evaluate_517(ctx),
493            518 => self.evaluate_518(ctx),
494            519 => self.evaluate_519(ctx),
495            520 => self.evaluate_520(ctx),
496            521 => self.evaluate_521(ctx),
497            522 => self.evaluate_522(ctx),
498            523 => self.evaluate_523(ctx),
499            524 => self.evaluate_524(ctx),
500            525 => self.evaluate_525(ctx),
501            526 => self.evaluate_526(ctx),
502            527 => self.evaluate_527(ctx),
503            528 => self.evaluate_528(ctx),
504            529 => self.evaluate_529(ctx),
505            530 => self.evaluate_530(ctx),
506            531 => self.evaluate_531(ctx),
507            532 => self.evaluate_532(ctx),
508            533 => self.evaluate_533(ctx),
509            555 => self.evaluate_555(ctx),
510            556 => self.evaluate_556(ctx),
511            558 => self.evaluate_558(ctx),
512            559 => self.evaluate_559(ctx),
513            563 => self.evaluate_563(ctx),
514            566 => self.evaluate_566(ctx),
515            567 => self.evaluate_567(ctx),
516            568 => self.evaluate_568(ctx),
517            569 => self.evaluate_569(ctx),
518            570 => self.evaluate_570(ctx),
519            572 => self.evaluate_572(ctx),
520            576 => self.evaluate_576(ctx),
521            577 => self.evaluate_577(ctx),
522            579 => self.evaluate_579(ctx),
523            580 => self.evaluate_580(ctx),
524            581 => self.evaluate_581(ctx),
525            586 => self.evaluate_586(ctx),
526            590 => self.evaluate_590(ctx),
527            594 => self.evaluate_594(ctx),
528            599 => self.evaluate_599(ctx),
529            601 => self.evaluate_601(ctx),
530            606 => self.evaluate_606(ctx),
531            609 => self.evaluate_609(ctx),
532            611 => self.evaluate_611(ctx),
533            614 => self.evaluate_614(ctx),
534            617 => self.evaluate_617(ctx),
535            618 => self.evaluate_618(ctx),
536            619 => self.evaluate_619(ctx),
537            621 => self.evaluate_621(ctx),
538            622 => self.evaluate_622(ctx),
539            623 => self.evaluate_623(ctx),
540            630 => self.evaluate_630(ctx),
541            631 => self.evaluate_631(ctx),
542            632 => self.evaluate_632(ctx),
543            637 => self.evaluate_637(ctx),
544            638 => self.evaluate_638(ctx),
545            639 => self.evaluate_639(ctx),
546            640 => self.evaluate_640(ctx),
547            641 => self.evaluate_641(ctx),
548            642 => self.evaluate_642(ctx),
549            643 => self.evaluate_643(ctx),
550            645 => self.evaluate_645(ctx),
551            646 => self.evaluate_646(ctx),
552            647 => self.evaluate_647(ctx),
553            648 => self.evaluate_648(ctx),
554            651 => self.evaluate_651(ctx),
555            653 => self.evaluate_653(ctx),
556            655 => self.evaluate_655(ctx),
557            659 => self.evaluate_659(ctx),
558            660 => self.evaluate_660(ctx),
559            662 => self.evaluate_662(ctx),
560            663 => self.evaluate_663(ctx),
561            664 => self.evaluate_664(ctx),
562            665 => self.evaluate_665(ctx),
563            667 => self.evaluate_667(ctx),
564            668 => self.evaluate_668(ctx),
565            670 => self.evaluate_670(ctx),
566            671 => self.evaluate_671(ctx),
567            672 => self.evaluate_672(ctx),
568            673 => self.evaluate_673(ctx),
569            674 => self.evaluate_674(ctx),
570            675 => self.evaluate_675(ctx),
571            677 => self.evaluate_677(ctx),
572            678 => self.evaluate_678(ctx),
573            679 => self.evaluate_679(ctx),
574            680 => self.evaluate_680(ctx),
575            682 => self.evaluate_682(ctx),
576            683 => self.evaluate_683(ctx),
577            684 => self.evaluate_684(ctx),
578            685 => self.evaluate_685(ctx),
579            687 => self.evaluate_687(ctx),
580            688 => self.evaluate_688(ctx),
581            689 => self.evaluate_689(ctx),
582            690 => self.evaluate_690(ctx),
583            693 => self.evaluate_693(ctx),
584            694 => self.evaluate_694(ctx),
585            695 => self.evaluate_695(ctx),
586            696 => self.evaluate_696(ctx),
587            698 => self.evaluate_698(ctx),
588            699 => self.evaluate_699(ctx),
589            700 => self.evaluate_700(ctx),
590            704 => self.evaluate_704(ctx),
591            705 => self.evaluate_705(ctx),
592            706 => self.evaluate_706(ctx),
593            707 => self.evaluate_707(ctx),
594            708 => self.evaluate_708(ctx),
595            709 => self.evaluate_709(ctx),
596            710 => self.evaluate_710(ctx),
597            711 => self.evaluate_711(ctx),
598            712 => self.evaluate_712(ctx),
599            713 => self.evaluate_713(ctx),
600            714 => self.evaluate_714(ctx),
601            715 => self.evaluate_715(ctx),
602            716 => self.evaluate_716(ctx),
603            717 => self.evaluate_717(ctx),
604            718 => self.evaluate_718(ctx),
605            719 => self.evaluate_719(ctx),
606            902 => self.evaluate_902(ctx),
607            910 => self.evaluate_910(ctx),
608            914 => self.evaluate_914(ctx),
609            922 => self.evaluate_922(ctx),
610            926 => self.evaluate_926(ctx),
611            930 => self.evaluate_930(ctx),
612            931 => self.evaluate_931(ctx),
613            932 => self.evaluate_932(ctx),
614            933 => self.evaluate_933(ctx),
615            937 => self.evaluate_937(ctx),
616            938 => self.evaluate_938(ctx),
617            939 => self.evaluate_939(ctx),
618            940 => self.evaluate_940(ctx),
619            942 => self.evaluate_942(ctx),
620            943 => self.evaluate_943(ctx),
621            946 => self.evaluate_946(ctx),
622            948 => self.evaluate_948(ctx),
623            950 => self.evaluate_950(ctx),
624            951 => self.evaluate_951(ctx),
625            952 => self.evaluate_952(ctx),
626            955 => self.evaluate_955(ctx),
627            957 => self.evaluate_957(ctx),
628            960 => self.evaluate_960(ctx),
629            961 => self.evaluate_961(ctx),
630            967 => self.evaluate_967(ctx),
631            2001 => self.evaluate_2001(ctx),
632            2002 => self.evaluate_2002(ctx),
633            2003 => self.evaluate_2003(ctx),
634            2004 => self.evaluate_2004(ctx),
635            2005 => self.evaluate_2005(ctx),
636            2006 => self.evaluate_2006(ctx),
637            2007 => self.evaluate_2007(ctx),
638            2008 => self.evaluate_2008(ctx),
639            2009 => self.evaluate_2009(ctx),
640            2010 => self.evaluate_2010(ctx),
641            2011 => self.evaluate_2011(ctx),
642            2012 => self.evaluate_2012(ctx),
643            2013 => self.evaluate_2013(ctx),
644            2014 => self.evaluate_2014(ctx),
645            2015 => self.evaluate_2015(ctx),
646            2016 => self.evaluate_2016(ctx),
647            2017 => self.evaluate_2017(ctx),
648            2018 => self.evaluate_2018(ctx),
649            2060 => self.evaluate_2060(ctx),
650            2061 => self.evaluate_2061(ctx),
651            2071 => self.evaluate_2071(ctx),
652            2073 => self.evaluate_2073(ctx),
653            2075 => self.evaluate_2075(ctx),
654            2080 => self.evaluate_2080(ctx),
655            2095 => self.evaluate_2095(ctx),
656            2096 => self.evaluate_2096(ctx),
657            2119 => self.evaluate_2119(ctx),
658            2140 => self.evaluate_2140(ctx),
659            2182 => self.evaluate_2182(ctx),
660            2183 => self.evaluate_2183(ctx),
661            2207 => self.evaluate_2207(ctx),
662            2225 => self.evaluate_2225(ctx),
663            2236 => self.evaluate_2236(ctx),
664            2252 => self.evaluate_2252(ctx),
665            2261 => self.evaluate_2261(ctx),
666            2284 => self.evaluate_2284(ctx),
667            2286 => self.evaluate_2286(ctx),
668            2287 => self.evaluate_2287(ctx),
669            2288 => self.evaluate_2288(ctx),
670            2307 => self.evaluate_2307(ctx),
671            2308 => self.evaluate_2308(ctx),
672            2309 => self.evaluate_2309(ctx),
673            2310 => self.evaluate_2310(ctx),
674            2311 => self.evaluate_2311(ctx),
675            2312 => self.evaluate_2312(ctx),
676            2313 => self.evaluate_2313(ctx),
677            2317 => self.evaluate_2317(ctx),
678            2318 => self.evaluate_2318(ctx),
679            2344 => self.evaluate_2344(ctx),
680            2350 => self.evaluate_2350(ctx),
681            2351 => self.evaluate_2351(ctx),
682            2352 => self.evaluate_2352(ctx),
683            2356 => self.evaluate_2356(ctx),
684            2357 => self.evaluate_2357(ctx),
685            2358 => self.evaluate_2358(ctx),
686            2359 => self.evaluate_2359(ctx),
687            2360 => self.evaluate_2360(ctx),
688            2361 => self.evaluate_2361(ctx),
689            _ => ConditionResult::Unknown,
690        }
691    }
692
693    fn is_external(&self, condition: u32) -> bool {
694        self.external_conditions.contains(&condition)
695    }
696    fn is_known(&self, condition: u32) -> bool {
697        matches!(
698            condition,
699            1 | 4
700                | 5
701                | 6
702                | 7
703                | 8
704                | 9
705                | 10
706                | 11
707                | 12
708                | 13
709                | 14
710                | 15
711                | 16
712                | 17
713                | 18
714                | 19
715                | 20
716                | 21
717                | 22
718                | 23
719                | 24
720                | 25
721                | 27
722                | 30
723                | 31
724                | 32
725                | 33
726                | 34
727                | 35
728                | 36
729                | 37
730                | 38
731                | 39
732                | 40
733                | 41
734                | 42
735                | 43
736                | 44
737                | 45
738                | 46
739                | 47
740                | 48
741                | 49
742                | 50
743                | 51
744                | 52
745                | 53
746                | 54
747                | 55
748                | 56
749                | 57
750                | 58
751                | 60
752                | 61
753                | 62
754                | 63
755                | 64
756                | 65
757                | 66
758                | 67
759                | 68
760                | 69
761                | 70
762                | 71
763                | 72
764                | 73
765                | 74
766                | 75
767                | 76
768                | 77
769                | 78
770                | 83
771                | 84
772                | 85
773                | 86
774                | 87
775                | 88
776                | 89
777                | 90
778                | 91
779                | 92
780                | 93
781                | 94
782                | 95
783                | 96
784                | 97
785                | 98
786                | 99
787                | 100
788                | 101
789                | 102
790                | 103
791                | 104
792                | 105
793                | 106
794                | 107
795                | 108
796                | 110
797                | 111
798                | 112
799                | 113
800                | 114
801                | 115
802                | 116
803                | 117
804                | 118
805                | 119
806                | 120
807                | 121
808                | 122
809                | 123
810                | 124
811                | 125
812                | 126
813                | 127
814                | 128
815                | 129
816                | 130
817                | 131
818                | 132
819                | 133
820                | 134
821                | 135
822                | 136
823                | 137
824                | 138
825                | 139
826                | 140
827                | 141
828                | 142
829                | 143
830                | 144
831                | 145
832                | 146
833                | 147
834                | 148
835                | 149
836                | 150
837                | 151
838                | 152
839                | 153
840                | 156
841                | 161
842                | 162
843                | 163
844                | 164
845                | 165
846                | 166
847                | 167
848                | 170
849                | 172
850                | 173
851                | 174
852                | 175
853                | 176
854                | 177
855                | 178
856                | 179
857                | 180
858                | 184
859                | 190
860                | 191
861                | 192
862                | 193
863                | 194
864                | 195
865                | 196
866                | 197
867                | 198
868                | 199
869                | 201
870                | 202
871                | 203
872                | 204
873                | 205
874                | 206
875                | 209
876                | 210
877                | 212
878                | 213
879                | 215
880                | 216
881                | 217
882                | 219
883                | 220
884                | 221
885                | 223
886                | 224
887                | 227
888                | 229
889                | 232
890                | 233
891                | 234
892                | 237
893                | 238
894                | 239
895                | 240
896                | 241
897                | 243
898                | 244
899                | 248
900                | 249
901                | 251
902                | 252
903                | 254
904                | 255
905                | 256
906                | 257
907                | 259
908                | 261
909                | 262
910                | 265
911                | 266
912                | 267
913                | 268
914                | 269
915                | 270
916                | 273
917                | 279
918                | 280
919                | 282
920                | 284
921                | 285
922                | 286
923                | 287
924                | 288
925                | 291
926                | 292
927                | 293
928                | 294
929                | 295
930                | 296
931                | 297
932                | 298
933                | 299
934                | 300
935                | 301
936                | 302
937                | 303
938                | 304
939                | 305
940                | 306
941                | 307
942                | 309
943                | 314
944                | 315
945                | 316
946                | 317
947                | 318
948                | 321
949                | 322
950                | 323
951                | 327
952                | 329
953                | 331
954                | 332
955                | 333
956                | 334
957                | 335
958                | 336
959                | 337
960                | 338
961                | 339
962                | 340
963                | 341
964                | 342
965                | 344
966                | 345
967                | 346
968                | 347
969                | 348
970                | 349
971                | 350
972                | 351
973                | 352
974                | 355
975                | 356
976                | 357
977                | 358
978                | 359
979                | 360
980                | 363
981                | 365
982                | 366
983                | 367
984                | 368
985                | 370
986                | 371
987                | 372
988                | 373
989                | 375
990                | 376
991                | 377
992                | 378
993                | 379
994                | 380
995                | 384
996                | 386
997                | 387
998                | 388
999                | 391
1000                | 392
1001                | 393
1002                | 394
1003                | 395
1004                | 396
1005                | 397
1006                | 398
1007                | 399
1008                | 401
1009                | 402
1010                | 403
1011                | 404
1012                | 405
1013                | 406
1014                | 407
1015                | 408
1016                | 409
1017                | 410
1018                | 411
1019                | 412
1020                | 413
1021                | 414
1022                | 415
1023                | 416
1024                | 417
1025                | 419
1026                | 420
1027                | 421
1028                | 425
1029                | 426
1030                | 427
1031                | 428
1032                | 429
1033                | 430
1034                | 431
1035                | 432
1036                | 433
1037                | 435
1038                | 436
1039                | 437
1040                | 438
1041                | 440
1042                | 441
1043                | 442
1044                | 444
1045                | 445
1046                | 446
1047                | 447
1048                | 448
1049                | 449
1050                | 450
1051                | 451
1052                | 452
1053                | 453
1054                | 454
1055                | 455
1056                | 456
1057                | 457
1058                | 458
1059                | 459
1060                | 460
1061                | 461
1062                | 462
1063                | 463
1064                | 465
1065                | 466
1066                | 467
1067                | 468
1068                | 469
1069                | 470
1070                | 471
1071                | 472
1072                | 473
1073                | 474
1074                | 475
1075                | 476
1076                | 477
1077                | 478
1078                | 479
1079                | 480
1080                | 481
1081                | 483
1082                | 484
1083                | 487
1084                | 489
1085                | 490
1086                | 491
1087                | 494
1088                | 500
1089                | 501
1090                | 502
1091                | 503
1092                | 504
1093                | 505
1094                | 506
1095                | 507
1096                | 508
1097                | 509
1098                | 510
1099                | 511
1100                | 512
1101                | 513
1102                | 514
1103                | 515
1104                | 516
1105                | 517
1106                | 518
1107                | 519
1108                | 520
1109                | 521
1110                | 522
1111                | 523
1112                | 524
1113                | 525
1114                | 526
1115                | 527
1116                | 528
1117                | 529
1118                | 530
1119                | 531
1120                | 532
1121                | 533
1122                | 555
1123                | 556
1124                | 558
1125                | 559
1126                | 563
1127                | 566
1128                | 567
1129                | 568
1130                | 569
1131                | 570
1132                | 572
1133                | 576
1134                | 577
1135                | 579
1136                | 580
1137                | 581
1138                | 586
1139                | 590
1140                | 594
1141                | 599
1142                | 601
1143                | 606
1144                | 609
1145                | 611
1146                | 614
1147                | 617
1148                | 618
1149                | 619
1150                | 621
1151                | 622
1152                | 623
1153                | 630
1154                | 631
1155                | 632
1156                | 637
1157                | 638
1158                | 639
1159                | 640
1160                | 641
1161                | 642
1162                | 643
1163                | 645
1164                | 646
1165                | 647
1166                | 648
1167                | 651
1168                | 653
1169                | 655
1170                | 659
1171                | 660
1172                | 662
1173                | 663
1174                | 664
1175                | 665
1176                | 667
1177                | 668
1178                | 670
1179                | 671
1180                | 672
1181                | 673
1182                | 674
1183                | 675
1184                | 677
1185                | 678
1186                | 679
1187                | 680
1188                | 682
1189                | 683
1190                | 684
1191                | 685
1192                | 687
1193                | 688
1194                | 689
1195                | 690
1196                | 693
1197                | 694
1198                | 695
1199                | 696
1200                | 698
1201                | 699
1202                | 700
1203                | 704
1204                | 705
1205                | 706
1206                | 707
1207                | 708
1208                | 709
1209                | 710
1210                | 711
1211                | 712
1212                | 713
1213                | 714
1214                | 715
1215                | 716
1216                | 717
1217                | 718
1218                | 719
1219                | 902
1220                | 910
1221                | 914
1222                | 922
1223                | 926
1224                | 930
1225                | 931
1226                | 932
1227                | 933
1228                | 937
1229                | 938
1230                | 939
1231                | 940
1232                | 942
1233                | 943
1234                | 946
1235                | 948
1236                | 950
1237                | 951
1238                | 952
1239                | 955
1240                | 957
1241                | 960
1242                | 961
1243                | 967
1244                | 2001
1245                | 2002
1246                | 2003
1247                | 2004
1248                | 2005
1249                | 2006
1250                | 2007
1251                | 2008
1252                | 2009
1253                | 2010
1254                | 2011
1255                | 2012
1256                | 2013
1257                | 2014
1258                | 2015
1259                | 2016
1260                | 2017
1261                | 2018
1262                | 2060
1263                | 2061
1264                | 2071
1265                | 2073
1266                | 2075
1267                | 2080
1268                | 2095
1269                | 2096
1270                | 2119
1271                | 2140
1272                | 2182
1273                | 2183
1274                | 2207
1275                | 2225
1276                | 2236
1277                | 2252
1278                | 2261
1279                | 2284
1280                | 2286
1281                | 2287
1282                | 2288
1283                | 2307
1284                | 2308
1285                | 2309
1286                | 2310
1287                | 2311
1288                | 2312
1289                | 2313
1290                | 2317
1291                | 2318
1292                | 2344
1293                | 2350
1294                | 2351
1295                | 2352
1296                | 2356
1297                | 2357
1298                | 2358
1299                | 2359
1300                | 2360
1301                | 2361
1302        )
1303    }
1304}
1305
1306impl UtilmdStromConditionEvaluatorFV2504 {
1307    /// [2006] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingung [199] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolge...
1308    // REVIEW: Counts SG8 instances with SEQ+Z01 where condition [199] is fulfilled and collects their Zeitraum-IDs from SEQ elements[1][0]. Returns True if at least one qualifying parent exists (cardinality enforcement is partial — full 'exactly once' check requires knowing the current SG type). The recursive call self.evaluate(199, ctx) operates at message scope, which is the best available approximation. (medium confidence)
1309    fn evaluate_2006(&self, ctx: &EvaluationContext) -> ConditionResult {
1310        let nav = match ctx.navigator() {
1311            Some(n) => n,
1312            None => return ConditionResult::Unknown,
1313        };
1314        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1315        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1316        for i in 0..sg8_count {
1317            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1318            let is_z01 = seq_segs.iter().any(|s| {
1319                s.elements
1320                    .first()
1321                    .and_then(|e| e.first())
1322                    .is_some_and(|v| v == "Z01")
1323            });
1324            if is_z01 && matches!(self.evaluate(199, ctx), ConditionResult::True) {
1325                for seq in &seq_segs {
1326                    if seq
1327                        .elements
1328                        .first()
1329                        .and_then(|e| e.first())
1330                        .is_some_and(|v| v == "Z01")
1331                    {
1332                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1333                            if !zid.is_empty() {
1334                                qualifying_zeitraum_ids.push(zid.clone());
1335                            }
1336                        }
1337                    }
1338                }
1339            }
1340        }
1341        if qualifying_zeitraum_ids.is_empty() {
1342            return ConditionResult::Unknown;
1343        }
1344        ConditionResult::True
1345    }
1346
1347    /// [2007] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [199] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfol...
1348    // REVIEW: Semantically identical to [2006] — 'Bedingungen' (plural) vs 'Bedingung' (singular) is a grammatical variation with no functional difference. Same logic: collects qualifying SG8 SEQ+Z01 Zeitraum-IDs where condition [199] is fulfilled. Recursive call self.evaluate(199, ctx) operates at message scope. (medium confidence)
1349    fn evaluate_2007(&self, ctx: &EvaluationContext) -> ConditionResult {
1350        let nav = match ctx.navigator() {
1351            Some(n) => n,
1352            None => return ConditionResult::Unknown,
1353        };
1354        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1355        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1356        for i in 0..sg8_count {
1357            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1358            let is_z01 = seq_segs.iter().any(|s| {
1359                s.elements
1360                    .first()
1361                    .and_then(|e| e.first())
1362                    .is_some_and(|v| v == "Z01")
1363            });
1364            if is_z01 && matches!(self.evaluate(199, ctx), ConditionResult::True) {
1365                for seq in &seq_segs {
1366                    if seq
1367                        .elements
1368                        .first()
1369                        .and_then(|e| e.first())
1370                        .is_some_and(|v| v == "Z01")
1371                    {
1372                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1373                            if !zid.is_empty() {
1374                                qualifying_zeitraum_ids.push(zid.clone());
1375                            }
1376                        }
1377                    }
1378                }
1379            }
1380        }
1381        if qualifying_zeitraum_ids.is_empty() {
1382            return ConditionResult::Unknown;
1383        }
1384        ConditionResult::True
1385    }
1386
1387    /// [2008] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingung [89] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolgen...
1388    // REVIEW: Same structure as [2006/2007] but references condition [89] instead of [199]. Counts qualifying SG8 SEQ+Z01 instances where condition [89] is fulfilled and collects their Zeitraum-IDs from SEQ elements[1][0]. The recursive self.evaluate(89, ctx) call operates at message scope. (medium confidence)
1389    fn evaluate_2008(&self, ctx: &EvaluationContext) -> ConditionResult {
1390        let nav = match ctx.navigator() {
1391            Some(n) => n,
1392            None => return ConditionResult::Unknown,
1393        };
1394        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1395        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1396        for i in 0..sg8_count {
1397            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1398            let is_z01 = seq_segs.iter().any(|s| {
1399                s.elements
1400                    .first()
1401                    .and_then(|e| e.first())
1402                    .is_some_and(|v| v == "Z01")
1403            });
1404            if is_z01 && matches!(self.evaluate(89, ctx), ConditionResult::True) {
1405                for seq in &seq_segs {
1406                    if seq
1407                        .elements
1408                        .first()
1409                        .and_then(|e| e.first())
1410                        .is_some_and(|v| v == "Z01")
1411                    {
1412                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1413                            if !zid.is_empty() {
1414                                qualifying_zeitraum_ids.push(zid.clone());
1415                            }
1416                        }
1417                    }
1418                }
1419            }
1420        }
1421        if qualifying_zeitraum_ids.is_empty() {
1422            return ConditionResult::Unknown;
1423        }
1424        ConditionResult::True
1425    }
1426
1427    /// [2009] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [89] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolg...
1428    // REVIEW: Semantically identical to [2008] — 'Bedingungen' (plural) vs 'Bedingung' (singular) is a grammatical variation. Same logic: collects qualifying SG8 SEQ+Z01 Zeitraum-IDs where condition [89] is fulfilled. Recursive call self.evaluate(89, ctx) operates at message scope. (medium confidence)
1429    fn evaluate_2009(&self, ctx: &EvaluationContext) -> ConditionResult {
1430        let nav = match ctx.navigator() {
1431            Some(n) => n,
1432            None => return ConditionResult::Unknown,
1433        };
1434        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1435        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1436        for i in 0..sg8_count {
1437            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1438            let is_z01 = seq_segs.iter().any(|s| {
1439                s.elements
1440                    .first()
1441                    .and_then(|e| e.first())
1442                    .is_some_and(|v| v == "Z01")
1443            });
1444            if is_z01 && matches!(self.evaluate(89, ctx), ConditionResult::True) {
1445                for seq in &seq_segs {
1446                    if seq
1447                        .elements
1448                        .first()
1449                        .and_then(|e| e.first())
1450                        .is_some_and(|v| v == "Z01")
1451                    {
1452                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1453                            if !zid.is_empty() {
1454                                qualifying_zeitraum_ids.push(zid.clone());
1455                            }
1456                        }
1457                    }
1458                }
1459            }
1460        }
1461        if qualifying_zeitraum_ids.is_empty() {
1462            return ConditionResult::Unknown;
1463        }
1464        ConditionResult::True
1465    }
1466
1467    /// [1] Wenn Aufteilung vorhanden
1468    fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
1469        ctx.external.evaluate("message_splitting")
1470    }
1471
1472    /// [4] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle LF
1473    fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
1474        ctx.external.evaluate("recipient_is_lf")
1475    }
1476
1477    /// [5] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle LF
1478    fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
1479        ctx.external.evaluate("sender_is_lf")
1480    }
1481
1482    /// [6] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle ÜNB
1483    fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
1484        ctx.external.evaluate("recipient_role_check")
1485    }
1486
1487    /// [7] Wenn SG4 STS+7++ZG9/ZH1/ZH2 (Transaktionsgrund: Aufhebung einer zukünftigen Zuordnung wegen Auszug des Kunden / -wegen Stilllegung / -wegen aufgehobenem Vertragsverhältnis) vorhanden
1488    fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
1489        let sts_segs = ctx.find_segments("STS");
1490        let found = sts_segs.iter().any(|s| {
1491            s.elements
1492                .first()
1493                .and_then(|e| e.first())
1494                .is_some_and(|v| v == "7")
1495                && s.elements
1496                    .get(2)
1497                    .and_then(|e| e.first())
1498                    .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
1499        });
1500        ConditionResult::from(found)
1501    }
1502
1503    /// [8] Wenn für Datenclearing erforderlich
1504    fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
1505        ctx.external.evaluate("data_clearing_required")
1506    }
1507
1508    /// [9] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle MSB
1509    fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
1510        ctx.external.evaluate("sender_role_check")
1511    }
1512
1513    /// [10] Wenn SG4 STS+7++xxx+xxx+E01/E03 (Transaktionsgrund befristete Anmeldung) vorhanden
1514    fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
1515        // STS+7++E01+ZW4+E03: element[4] is c556_3.d9013 = transaktionsgrundErgaenzungBefristeteAnmeldung
1516        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
1517    }
1518
1519    /// [11] Wenn SG4 STS+7++ZG9/ZH1/ZH2 (Transaktionsgrund: Aufhebung einer zukünftigen Zuordnung wegen Auszug des Kunden / -wegen Stilllegung / -wegen aufgehobenem Vertragsverhältnis) nicht vorhanden
1520    fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
1521        let sts_segs = ctx.find_segments("STS");
1522        let found = sts_segs.iter().any(|s| {
1523            s.elements
1524                .first()
1525                .and_then(|e| e.first())
1526                .is_some_and(|v| v == "7")
1527                && s.elements
1528                    .get(2)
1529                    .and_then(|e| e.first())
1530                    .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
1531        });
1532        ConditionResult::from(!found)
1533    }
1534
1535    /// [12] Wenn SG4 DTM+471 (Ende zum nächstmöglichem Termin) nicht vorhanden
1536    fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
1537        let dtm_segs = ctx.find_segments("DTM");
1538        let found = dtm_segs.iter().any(|s| {
1539            s.elements
1540                .first()
1541                .and_then(|e| e.first())
1542                .is_some_and(|v| v == "471")
1543        });
1544        ConditionResult::from(!found)
1545    }
1546
1547    /// [13] Wenn SG4 STS+E01++Z01 (Status der Antwort: Zustimmung mit Terminänderung) nicht vorhanden
1548    fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
1549        let sts_segs = ctx.find_segments("STS");
1550        let found = sts_segs.iter().any(|s| {
1551            s.elements
1552                .first()
1553                .and_then(|e| e.first())
1554                .is_some_and(|v| v == "E01")
1555                && s.elements
1556                    .get(2)
1557                    .and_then(|e| e.first())
1558                    .is_some_and(|v| ["Z01"].contains(&v.as_str()))
1559        });
1560        ConditionResult::from(!found)
1561    }
1562
1563    /// [14] Wenn Datum bekannt
1564    fn evaluate_14(&self, ctx: &EvaluationContext) -> ConditionResult {
1565        ctx.external.evaluate("date_known")
1566    }
1567
1568    /// [15] Wenn in derselben SG8 das SEQ+Z98 (Informative Daten der Marktlokation) vorhanden
1569    fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
1570        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1571        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
1572        let found = seq_segs.iter().any(|s| {
1573            s.elements
1574                .first()
1575                .and_then(|e| e.first())
1576                .is_some_and(|v| ["Z98"].contains(&v.as_str()))
1577        });
1578        ConditionResult::from(found)
1579    }
1580
1581    /// [16] Wenn SG4 STS+E01++Z12 (Status der Antwort: Ablehnung Vertragsbindung) vorhanden
1582    fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
1583        let sts_segs = ctx.find_segments("STS");
1584        let found = sts_segs.iter().any(|s| {
1585            s.elements
1586                .first()
1587                .and_then(|e| e.first())
1588                .is_some_and(|v| v == "E01")
1589                && s.elements
1590                    .get(2)
1591                    .and_then(|e| e.first())
1592                    .is_some_and(|v| ["Z12"].contains(&v.as_str()))
1593        });
1594        ConditionResult::from(found)
1595    }
1596
1597    /// [17] Wenn in derselben SG8 das SEQ+Z01/Z80/Z81 (Daten der Marktlokation) vorhanden
1598    fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
1599        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1600        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
1601        let found = seq_segs.iter().any(|s| {
1602            s.elements
1603                .first()
1604                .and_then(|e| e.first())
1605                .is_some_and(|v| ["Z01", "Z80", "Z81"].contains(&v.as_str()))
1606        });
1607        ConditionResult::from(found)
1608    }
1609
1610    /// [18] Wenn SG4 DTM+93 (Ende zum) nicht vorhanden
1611    fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
1612        let dtm_segs = ctx.find_segments("DTM");
1613        let found = dtm_segs.iter().any(|s| {
1614            s.elements
1615                .first()
1616                .and_then(|e| e.first())
1617                .is_some_and(|v| v == "93")
1618        });
1619        ConditionResult::from(!found)
1620    }
1621
1622    /// [19] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+++ZC0 (Prognose auf Basis von Werten) vorhanden
1623    fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
1624        let seq_segs = ctx.find_segments("SEQ");
1625        let found = seq_segs.iter().any(|s| {
1626            s.elements
1627                .first()
1628                .and_then(|e| e.first())
1629                .is_some_and(|v| ["Z01", "Z98"].contains(&v.as_str()))
1630        });
1631        ConditionResult::from(found)
1632    }
1633
1634    /// [20] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z07 (Verbrauch) vorhanden
1635    fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
1636        let seq_segs = ctx.find_segments("SEQ");
1637        let found = seq_segs.iter().any(|s| {
1638            s.elements
1639                .first()
1640                .and_then(|e| e.first())
1641                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
1642        });
1643        ConditionResult::from(found)
1644    }
1645
1646    /// [21] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
1647    fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
1648        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1649        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
1650        let found = cci_segs.iter().any(|s| {
1651            s.elements
1652                .get(2)
1653                .and_then(|e| e.first())
1654                .is_some_and(|v| v == "ZA6")
1655        });
1656        ConditionResult::from(found)
1657    }
1658
1659    /// [22] Es ist die Zeitraum-ID vom DE1156 aus einem passenden SG6 RFF+Z47/ Z48/ Z49 (Verwendungszeitraum der Daten) einzutragen
1660    // REVIEW: Checks if any SG6 RFF+Z47/Z48/Z49 Zeitraum-ID (DE1156 at elements[0][2]) matches any SG8 SEQ Zeitraum-ID reference (DE1050 at elements[1][0]). Uses groups_share_qualified_value for cross-group correlation across all three qualifying codes. (medium confidence)
1661    fn evaluate_22(&self, ctx: &EvaluationContext) -> ConditionResult {
1662        for qual in &["Z47", "Z48", "Z49"] {
1663            if matches!(
1664                ctx.groups_share_qualified_value(
1665                    "RFF",
1666                    0,
1667                    qual,
1668                    0,
1669                    2,
1670                    &["SG4", "SG6"],
1671                    "SEQ",
1672                    1,
1673                    0,
1674                    &["SG4", "SG8"],
1675                ),
1676                ConditionResult::True
1677            ) {
1678                return ConditionResult::True;
1679            }
1680        }
1681        ConditionResult::False
1682    }
1683
1684    /// [23] Wenn in dieser SG4 das STS+E01++A05/A99 (Status der Antwort) vorhanden
1685    fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
1686        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1687        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1688        let found = sts_segs.iter().any(|s| {
1689            s.elements
1690                .first()
1691                .and_then(|e| e.first())
1692                .is_some_and(|v| v == "E01")
1693                && s.elements
1694                    .get(2)
1695                    .and_then(|e| e.first())
1696                    .is_some_and(|v| ["A05", "A99"].contains(&v.as_str()))
1697        });
1698        ConditionResult::from(found)
1699    }
1700
1701    /// [24] Wenn in dieser SG4 das STS+E01++A25/A99 (Status der Antwort) vorhanden
1702    fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
1703        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1704        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1705        let found = sts_segs.iter().any(|s| {
1706            s.elements
1707                .first()
1708                .and_then(|e| e.first())
1709                .is_some_and(|v| v == "E01")
1710                && s.elements
1711                    .get(2)
1712                    .and_then(|e| e.first())
1713                    .is_some_and(|v| ["A25", "A99"].contains(&v.as_str()))
1714        });
1715        ConditionResult::from(found)
1716    }
1717
1718    /// [25] Wenn die Veräußerungsform der erzeugenden Marktlokation der Marktprämie zugeordnet ist
1719    fn evaluate_25(&self, _ctx: &EvaluationContext) -> ConditionResult {
1720        // TODO: implement
1721        ConditionResult::Unknown
1722    }
1723
1724    /// [27] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle ein T oder R enthält
1725    fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
1726        let dtm_segs = ctx.find_segments("DTM");
1727        let found = dtm_segs.iter().any(|s| {
1728            s.elements
1729                .first()
1730                .and_then(|e| e.first())
1731                .is_some_and(|v| v == "Z01")
1732                && s.elements
1733                    .first()
1734                    .and_then(|e| e.get(1))
1735                    .is_some_and(|v| v.chars().nth(3).is_some_and(|c| ['T', 'R'].contains(&c)))
1736        });
1737        ConditionResult::from(found)
1738    }
1739
1740    /// [30] Wenn Antwort auf Aktivierung übermittelt wird
1741    fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
1742        // Antwort auf Aktivierung — depends on PID context
1743        ctx.external.evaluate("activation_response")
1744    }
1745
1746    /// [31] Wenn eine Korrektur erfolgt
1747    fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
1748        ctx.external.evaluate("correction_in_progress")
1749    }
1750
1751    /// [32] Wenn mehr als ein SG8 (Referenz auf die Lokationsbündelstruktur) vorhanden
1752    fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
1753        let seq_segs = ctx.find_segments("SEQ");
1754        ConditionResult::from(seq_segs.len() > 1)
1755    }
1756
1757    /// [33] Wenn in einer SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6 \"Produkte zur Bestellung / Änderung von Date...
1758    fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
1759        ctx.external.evaluate("code_list_membership_check")
1760    }
1761
1762    /// [34] Wenn Antwort auf Deaktivierung übermittelt wird
1763    fn evaluate_34(&self, ctx: &EvaluationContext) -> ConditionResult {
1764        // Antwort auf Deaktivierung — depends on PID context
1765        ctx.external.evaluate("deactivation_response")
1766    }
1767
1768    /// [35] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle T (Termin) enthält
1769    fn evaluate_35(&self, ctx: &EvaluationContext) -> ConditionResult {
1770        let dtm_segs = ctx.find_segments("DTM");
1771        let found = dtm_segs.iter().any(|s| {
1772            s.elements
1773                .first()
1774                .and_then(|e| e.first())
1775                .is_some_and(|v| v == "Z01")
1776                && s.elements
1777                    .first()
1778                    .and_then(|e| e.get(1))
1779                    .is_some_and(|v| v.chars().nth(3).is_some_and(|c| c == 'T'))
1780        });
1781        ConditionResult::from(found)
1782    }
1783
1784    /// [36] Wenn in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTI...
1785    fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
1786        ctx.external.evaluate("code_list_membership_check")
1787    }
1788
1789    /// [37] Wenn Anmeldung/ Änderung befristet
1790    fn evaluate_37(&self, ctx: &EvaluationContext) -> ConditionResult {
1791        // Same check as [10]: STS element[4] is befristete Anmeldung code
1792        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
1793    }
1794
1795    /// [38] Es sind nur die Code der Produkteigenschaft zu dem in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 erlaubt, die in der Codeliste der Konfigurationen im Kapitel  6.1 \"Anmeldung ei...
1796    fn evaluate_38(&self, ctx: &EvaluationContext) -> ConditionResult {
1797        ctx.external.evaluate("code_list_membership_check")
1798    }
1799
1800    /// [39] Wenn in derselben SG8 SEQ+Z79 m PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTIL...
1801    fn evaluate_39(&self, ctx: &EvaluationContext) -> ConditionResult {
1802        ctx.external.evaluate("code_list_membership_check")
1803    }
1804
1805    /// [40] Es ist nur der Wertebereich erlaubt, der zu dem in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 genannten Produkt, das in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeld...
1806    fn evaluate_40(&self, ctx: &EvaluationContext) -> ConditionResult {
1807        ctx.external.evaluate("code_list_membership_check")
1808    }
1809
1810    /// [41] Es ist eine Produktpaket-ID aus dem DE1050 von einem SG8 SEQ+Z79 (Erforderliches Produkt) zu nennen
1811    fn evaluate_41(&self, ctx: &EvaluationContext) -> ConditionResult {
1812        // Check: Produktpaket-ID exists in SEQ+Z79 (Erforderliches Produkt)
1813        let seq_z79 = ctx.find_segments_with_qualifier("SEQ", 0, "Z79");
1814        if seq_z79.is_empty() {
1815            return ConditionResult::Unknown;
1816        }
1817        // Check if SEQ has a value in C286.D1050 (elements[1][0])
1818        let found = seq_z79.iter().any(|s| {
1819            s.elements
1820                .get(1)
1821                .and_then(|e| e.first())
1822                .is_some_and(|v| !v.is_empty())
1823        });
1824        ConditionResult::from(found)
1825    }
1826
1827    /// [42] Wenn mehr als ein SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) vorhanden
1828    fn evaluate_42(&self, ctx: &EvaluationContext) -> ConditionResult {
1829        let seq_segs = ctx.find_segments("SEQ");
1830        let zh0_count = seq_segs
1831            .iter()
1832            .filter(|s| {
1833                s.elements
1834                    .first()
1835                    .and_then(|e| e.first())
1836                    .is_some_and(|v| v == "ZH0")
1837            })
1838            .count();
1839        ConditionResult::from(zh0_count > 1)
1840    }
1841
1842    /// [43] Wenn in dieser SG4 das STS+E01++A12 / A13 (Status der Antwort) vorhanden
1843    fn evaluate_43(&self, ctx: &EvaluationContext) -> ConditionResult {
1844        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1845        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1846        let found = sts_segs.iter().any(|s| {
1847            s.elements
1848                .first()
1849                .and_then(|e| e.first())
1850                .is_some_and(|v| v == "E01")
1851                && s.elements
1852                    .get(2)
1853                    .and_then(|e| e.first())
1854                    .is_some_and(|v| ["A12", "A13"].contains(&v.as_str()))
1855        });
1856        ConditionResult::from(found)
1857    }
1858
1859    /// [44] Es ist die Zeitraum-ID vom DE1156 aus einem passenden SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: "Gültige Daten", "Keine Daten") aus der Anfragennachricht aus SG6 RFF+TN DE1154 ((Referenz Vo...
1860    fn evaluate_44(&self, _ctx: &EvaluationContext) -> ConditionResult {
1861        // Hinweis: Zeitraum-ID aus passendem SG6 RFF+Z49/Z53 der Anfragenachricht eintragen — informational data population rule
1862        ConditionResult::True
1863    }
1864
1865    /// [45] Wenn in derselben SG8 das SG10 CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
1866    fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
1867        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1868        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
1869        let found = cci_segs.iter().any(|s| {
1870            s.elements
1871                .first()
1872                .and_then(|e| e.first())
1873                .is_some_and(|v| v == "Z01")
1874                && s.elements
1875                    .get(2)
1876                    .and_then(|e| e.first())
1877                    .is_some_and(|v| v == "Z82")
1878        });
1879        ConditionResult::from(found)
1880    }
1881
1882    /// [46] Wenn ID für Objekt vergeben wurde
1883    fn evaluate_46(&self, _ctx: &EvaluationContext) -> ConditionResult {
1884        // TODO: implement
1885        ConditionResult::Unknown
1886    }
1887
1888    /// [47] Wenn SG10 CCI+15++Z21 (Summenzeitreihentyp) CAV+DBA/ VZR/ EGS/ LGS/ SLS/ SES/ TLS/ TES/ BIL/ BIP/ BIT/ GAL/ GAP/ GAT/ GEL/ GEP/ GET/ SOL/ SOP/ SOT/ WFL/ WFP/ WFT/ WNL/ WNP/ WNT vorhanden
1889    // REVIEW: Iterates all SG8 instances and their SG10 children looking for CCI with elements[0][0]=="15" and elements[2][0]=="Z21" (Zeitreihentyp), then checks if the same SG10 has a CAV with elements[0][0] in the Summenzeitreihentyp code list. Uses navigator for parent-child group traversal. (medium confidence)
1890    fn evaluate_47(&self, ctx: &EvaluationContext) -> ConditionResult {
1891        const CODES: &[&str] = &[
1892            "DBA", "VZR", "EGS", "LGS", "SLS", "SES", "TLS", "TES", "BIL", "BIP", "BIT", "GAL",
1893            "GAP", "GAT", "GEL", "GEP", "GET", "SOL", "SOP", "SOT", "WFL", "WFP", "WFT", "WNL",
1894            "WNP", "WNT",
1895        ];
1896        let nav = match ctx.navigator {
1897            Some(n) => n,
1898            None => return ConditionResult::Unknown,
1899        };
1900        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1901        for i in 0..sg8_count {
1902            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
1903            for j in 0..sg10_count {
1904                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
1905                let has_cci_15_z21 = ccis.iter().any(|s| {
1906                    s.elements
1907                        .first()
1908                        .and_then(|e: &Vec<String>| e.first())
1909                        .is_some_and(|v: &String| v == "15")
1910                        && s.elements
1911                            .get(2)
1912                            .and_then(|e: &Vec<String>| e.first())
1913                            .is_some_and(|v: &String| v == "Z21")
1914                });
1915                if has_cci_15_z21 {
1916                    let cavs =
1917                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
1918                    if cavs.iter().any(|s| {
1919                        s.elements
1920                            .first()
1921                            .and_then(|e: &Vec<String>| e.first())
1922                            .is_some_and(|v: &String| CODES.contains(&v.as_str()))
1923                    }) {
1924                        return ConditionResult::True;
1925                    }
1926                }
1927            }
1928        }
1929        ConditionResult::False
1930    }
1931
1932    /// [48] Wenn in dieser SG4 das STS+E01++A99 (Status der Antwort: Sonstiges) vorhanden
1933    fn evaluate_48(&self, ctx: &EvaluationContext) -> ConditionResult {
1934        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1935        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1936        let found = sts_segs.iter().any(|s| {
1937            s.elements
1938                .first()
1939                .and_then(|e| e.first())
1940                .is_some_and(|v| v == "E01")
1941                && s.elements
1942                    .get(2)
1943                    .and_then(|e| e.first())
1944                    .is_some_and(|v| ["A99"].contains(&v.as_str()))
1945        });
1946        ConditionResult::from(found)
1947    }
1948
1949    /// [49] Wenn in dieser SG4 das STS+E01++E14 (Status der Antwort: Ablehnung Sonstiges) vorhanden
1950    fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
1951        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1952        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1953        let found = sts_segs.iter().any(|s| {
1954            s.elements
1955                .first()
1956                .and_then(|e| e.first())
1957                .is_some_and(|v| v == "E01")
1958                && s.elements
1959                    .get(2)
1960                    .and_then(|e| e.first())
1961                    .is_some_and(|v| ["E14"].contains(&v.as_str()))
1962        });
1963        ConditionResult::from(found)
1964    }
1965
1966    /// [50] Wenn kein SG8 SEQ+ZE7 (Informative Daten der Tranche) mit derselben Zeitraum-ID im DE1050 auf die gleiche Marktlokation-ID mit dem RFF+Z18 (Marktlokation) referenziert wie dieses SG8 SEQ+Z98 (Infor...
1967    // REVIEW: Collects (Zeitraum-ID from SEQ.elements[1][0], Marktlok-ID from sibling RFF+Z18.elements[0][1]) for all SG8 instances with SEQ+Z98 and SEQ+ZE7. Returns True if any Z98 group has no ZE7 group matching on both values. Uses collect_group_values with instance-index correlation via HashMap, assuming each SG8 has at most one SEQ (guaranteed as entry segment) and the zip ordering is stable. (medium confidence)
1968    fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
1969        use std::collections::HashMap;
1970
1971        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
1972        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
1973        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
1974        let rff_ids = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
1975
1976        // Build per-instance RFF lookup: instance_idx -> list of (qualifier, id)
1977        let mut rff_by_instance: HashMap<usize, Vec<(String, String)>> = HashMap::new();
1978        for ((idx, q), (_, id)) in rff_quals.iter().zip(rff_ids.iter()) {
1979            rff_by_instance
1980                .entry(*idx)
1981                .or_default()
1982                .push((q.clone(), id.clone()));
1983        }
1984
1985        // Build (zeitraum_id, marktlok_id) for Z98 and ZE7 SG8 instances
1986        let mut seq_zeitraum_map: HashMap<usize, String> = HashMap::new();
1987        for (idx, z) in &seq_zeitraum {
1988            seq_zeitraum_map.insert(*idx, z.clone());
1989        }
1990
1991        let z98_data: Vec<(String, String)> = seq_quals
1992            .iter()
1993            .filter(|(_, q)| q == "Z98")
1994            .map(|(idx, _)| {
1995                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
1996                let marktlok = rff_by_instance
1997                    .get(idx)
1998                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
1999                    .map(|(_, id)| id.clone())
2000                    .unwrap_or_default();
2001                (zeitraum, marktlok)
2002            })
2003            .collect();
2004
2005        if z98_data.is_empty() {
2006            return ConditionResult::Unknown;
2007        }
2008
2009        let ze7_data: Vec<(String, String)> = seq_quals
2010            .iter()
2011            .filter(|(_, q)| q == "ZE7")
2012            .map(|(idx, _)| {
2013                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
2014                let marktlok = rff_by_instance
2015                    .get(idx)
2016                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
2017                    .map(|(_, id)| id.clone())
2018                    .unwrap_or_default();
2019                (zeitraum, marktlok)
2020            })
2021            .collect();
2022
2023        ConditionResult::from(z98_data.iter().any(|(z98_zeit, z98_lok)| {
2024            !ze7_data
2025                .iter()
2026                .any(|(ze7_zeit, ze7_lok)| ze7_zeit == z98_zeit && ze7_lok == z98_lok)
2027        }))
2028    }
2029
2030    /// [51] Wenn die Marktlokations-ID im SG8 RFF+Z18 derselben SG8 SEQ+Z98 (Informative Daten der Marktlokation) mit derselben Zeitraum-ID im DE1050 nicht auf ein SG8 SEQ+Z78 (Referenz auf die Lokationsbünde...
2031    // REVIEW: Condition is True when no Z98 SG8 (Informative Daten der Marktlokation) references — via matching Zeitraum-ID in SEQ.C286.DE1050 — a Z78 SG8 (Referenz auf Lokationsbündelstruktur) that carries the pauschal bundle code 9992000000018 in RFF+Z31.DE1154. Requires navigator for cross-SG8 Zeitraum-ID correlation. (medium confidence)
2032    fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
2033        // HAND-EDITED: "derselben SG8 SEQ+Z98 mit derselben Zeitraum-ID … nicht auf ein SG8 SEQ+Z78" — compared within the enclosing SG4 when the scope
2034        // reaches an SG8; the message as before otherwise.
2035        if let Some(scope) = ctx.enclosing_scope("SG8") {
2036            let Some((_, zeitraum)) =
2037                sg4_instances::seq_at(ctx, scope).filter(|(code, _)| code == "Z98")
2038            else {
2039                return ConditionResult::False;
2040            };
2041            let pauschal = sg4_instances::other_sg8s(ctx, scope).iter().any(|p| {
2042                sg4_instances::seq_at(ctx, p)
2043                    .is_some_and(|(c, z)| c == "Z78" && !z.is_empty() && z == zeitraum)
2044                    && sg4_instances::rff_values_at(ctx, p, "Z31")
2045                        .iter()
2046                        .any(|v| v.replace(' ', "") == "9992000000018")
2047            });
2048            return ConditionResult::from(!pauschal);
2049        }
2050        let nav = match ctx.navigator() {
2051            Some(n) => n,
2052            None => return ConditionResult::Unknown,
2053        };
2054        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2055        let pauschal_id = "9992000000018";
2056        // Collect Zeitraum-IDs from Z78 SG8s that carry the pauschal RFF+Z31 ID
2057        let mut pauschal_zeitraum_ids: Vec<String> = Vec::new();
2058        for i in 0..sg8_count {
2059            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2060            let has_z78 = seqs.iter().any(|s| {
2061                s.elements
2062                    .first()
2063                    .and_then(|e| e.first())
2064                    .is_some_and(|v| v == "Z78")
2065            });
2066            if !has_z78 {
2067                continue;
2068            }
2069            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
2070            let has_pauschal = rffs.iter().any(|s| {
2071                s.elements
2072                    .first()
2073                    .and_then(|e| e.first())
2074                    .is_some_and(|v| v == "Z31")
2075                    && s.elements
2076                        .first()
2077                        .and_then(|e| e.get(1))
2078                        .is_some_and(|v| v.replace(' ', "") == pauschal_id)
2079            });
2080            if has_pauschal {
2081                for seq in &seqs {
2082                    if seq
2083                        .elements
2084                        .first()
2085                        .and_then(|e| e.first())
2086                        .is_some_and(|v| v == "Z78")
2087                    {
2088                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
2089                            if !zid.is_empty() {
2090                                pauschal_zeitraum_ids.push(zid.clone());
2091                            }
2092                        }
2093                    }
2094                }
2095            }
2096        }
2097        // For each Z98 SG8, check whether its Zeitraum-ID (SEQ.C286.DE1050) maps to a pauschal Z78
2098        for i in 0..sg8_count {
2099            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2100            for seq in &seqs {
2101                if seq
2102                    .elements
2103                    .first()
2104                    .and_then(|e| e.first())
2105                    .is_some_and(|v| v == "Z98")
2106                {
2107                    let zid = seq
2108                        .elements
2109                        .get(1)
2110                        .and_then(|e| e.first())
2111                        .map(|s| s.as_str())
2112                        .unwrap_or("");
2113                    if !zid.is_empty() && pauschal_zeitraum_ids.iter().any(|z| z == zid) {
2114                        // Z98 SG8 references a pauschal Z78 → condition is False
2115                        return ConditionResult::False;
2116                    }
2117                }
2118            }
2119        }
2120        // No Z98 SG8 references the pauschal bundle → condition is True
2121        ConditionResult::True
2122    }
2123
2124    /// [52] Wenn SG10 CCI+Z07++Z55/ Z56 (Profiltyp: sonstige verbrauchende Marktlokation / sonstige erzeugende Marktlokation) vorhanden
2125    fn evaluate_52(&self, ctx: &EvaluationContext) -> ConditionResult {
2126        let cci_segs = ctx.find_segments("CCI");
2127        let found = cci_segs.iter().any(|s| {
2128            s.elements
2129                .first()
2130                .and_then(|e| e.first())
2131                .is_some_and(|v| v == "Z07")
2132                && s.elements
2133                    .get(2)
2134                    .and_then(|e| e.first())
2135                    .is_some_and(|v| ["Z55", "Z56"].contains(&v.as_str()))
2136        });
2137        ConditionResult::from(found)
2138    }
2139
2140    /// [53] Wenn weitere Präzisierungen als über CCI+Z07 (Profiltyp) möglich sind
2141    fn evaluate_53(&self, _ctx: &EvaluationContext) -> ConditionResult {
2142        // TODO: implement
2143        ConditionResult::Unknown
2144    }
2145
2146    /// [54] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) vorhanden
2147    fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
2148        let seq_segs = ctx.find_segments("SEQ");
2149        let found = seq_segs.iter().any(|s| {
2150            s.elements
2151                .first()
2152                .and_then(|e| e.first())
2153                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
2154        });
2155        ConditionResult::from(found)
2156    }
2157
2158    /// [55] Wenn in dieser SG4 das STS+E01++A06/ A99 (Status der Antwort) vorhanden
2159    fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
2160        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2161        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2162        let found = sts_segs.iter().any(|s| {
2163            s.elements
2164                .first()
2165                .and_then(|e| e.first())
2166                .is_some_and(|v| v == "E01")
2167                && s.elements
2168                    .get(2)
2169                    .and_then(|e| e.first())
2170                    .is_some_and(|v| ["A06", "A99"].contains(&v.as_str()))
2171        });
2172        ConditionResult::from(found)
2173    }
2174
2175    /// [56] Wenn im STS+E01 im DE9013 (Status der Antwort) ein Antwortcode aus dem Cluster Zustimmung vorhanden ist
2176    fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
2177        // Check: STS+E01 DE9013 has a Zustimmung (approval) cluster code
2178        // Zustimmung codes start with A0x (A01-A07 are approval codes)
2179        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
2180        let found = sts_segs.iter().any(|s| {
2181            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
2182                [
2183                    "A01", "A02", "A03", "A04", "A05", "A06", "A07", "A08", "A09", "A10", "A11",
2184                    "A12", "A13", "A14", "A15", "A16", "A17", "A25",
2185                ]
2186                .contains(&v.as_str())
2187            })
2188        });
2189        ConditionResult::from(found)
2190    }
2191
2192    /// [57] Wenn in derselben SG8 das CCI+Z45++ZD9 (Abrechnung findet statt) vorhanden ist
2193    fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
2194        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2195        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
2196        let found = cci_segs.iter().any(|s| {
2197            s.elements
2198                .first()
2199                .and_then(|e| e.first())
2200                .is_some_and(|v| v == "Z45")
2201                && s.elements
2202                    .get(2)
2203                    .and_then(|e| e.first())
2204                    .is_some_and(|v| v == "ZD9")
2205        });
2206        ConditionResult::from(found)
2207    }
2208
2209    /// [58] Wenn in diesem CCI das DE3055 mit dem Code 293 vorhanden
2210    fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
2211        // HAND-EDITED: "in diesem CCI" — the CCI the field is in (its own segment
2212        // in tree validation), else the enclosing SG10's CCI.
2213        let has_293 =
2214            |elements: &[Vec<String>]| elements.iter().any(|e| e.iter().any(|v| v == "293"));
2215        if let Some(own) = ctx.resolved_segment.filter(|_| {
2216            ctx.scoped_find_segments_in("SG10", "CCI")
2217                .iter()
2218                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
2219        }) {
2220            return ConditionResult::from(has_293(own));
2221        }
2222        ConditionResult::from(
2223            ctx.scoped_find_segments_in("SG10", "CCI")
2224                .iter()
2225                .any(|s| has_293(&s.elements)),
2226        )
2227    }
2228
2229    /// [60] Wenn BGM+Z90 (Beendigung der Zuordnung zur Lokation) vorhanden
2230    fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
2231        let bgm_segs = ctx.find_segments("BGM");
2232        let found = bgm_segs.iter().any(|s| {
2233            s.elements
2234                .first()
2235                .and_then(|e| e.first())
2236                .is_some_and(|v| v == "Z90")
2237        });
2238        ConditionResult::from(found)
2239    }
2240
2241    /// [61] Wenn BGM+Z89 (Zuordnung zur Lokation) vorhanden
2242    fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
2243        let bgm_segs = ctx.find_segments("BGM");
2244        let found = bgm_segs.iter().any(|s| {
2245            s.elements
2246                .first()
2247                .and_then(|e| e.first())
2248                .is_some_and(|v| v == "Z89")
2249        });
2250        ConditionResult::from(found)
2251    }
2252
2253    /// [62] Wenn mehr als eine SG8 SEQ+Z45 (Netznutzungsabrechnungsdaten der Marktlokation), mit derselben Zeitraum-ID im DE1050, mit einer Gruppen-/Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), ...
2254    // REVIEW: Iterates SG8 instances within each SG4, finds those with SEQ+Z45 and a non-empty Zeitraum-ID in elements[1][0]. Checks if PIA+Z02 in the same SG8 has a product code (elements[1][0]) beginning with 1-08-1 through 1-08-5. Groups by Zeitraum-ID and checks if any ID appears more than once. Requires navigator for per-instance group segment access; returns Unknown without one. (medium confidence)
2255    fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
2256        let nav = match ctx.navigator {
2257            Some(n) => n,
2258            None => return ConditionResult::Unknown,
2259        };
2260        use std::collections::HashMap;
2261        let mut count_by_zid: HashMap<String, u32> = HashMap::new();
2262        let sg4_count = nav.group_instance_count(&["SG4"]);
2263        for sg4_idx in 0..sg4_count {
2264            let sg8_count = nav.child_group_instance_count(&["SG4"], sg4_idx, "SG8");
2265            for sg8_idx in 0..sg8_count {
2266                let seqs =
2267                    nav.find_segments_in_child_group("SEQ", &["SG4"], sg4_idx, "SG8", sg8_idx);
2268                let zeitraum_opt = seqs
2269                    .iter()
2270                    .filter(|s| {
2271                        s.elements
2272                            .first()
2273                            .and_then(|e: &Vec<String>| e.first())
2274                            .is_some_and(|v: &String| v == "Z45")
2275                    })
2276                    .filter_map(|s| {
2277                        s.elements
2278                            .get(1)
2279                            .and_then(|e: &Vec<String>| e.first())
2280                            .filter(|v| !v.is_empty())
2281                            .cloned()
2282                    })
2283                    .next();
2284                let Some(zid) = zeitraum_opt else { continue };
2285                let pias =
2286                    nav.find_segments_in_child_group("PIA", &["SG4"], sg4_idx, "SG8", sg8_idx);
2287                let has_qualifying_pia = pias.iter().any(|s| {
2288                    s.elements
2289                        .first()
2290                        .and_then(|e: &Vec<String>| e.first())
2291                        .is_some_and(|v: &String| v == "Z02")
2292                        && s.elements
2293                            .get(1)
2294                            .and_then(|e: &Vec<String>| e.first())
2295                            .map(|code| {
2296                                code.starts_with("1-08-1")
2297                                    || code.starts_with("1-08-2")
2298                                    || code.starts_with("1-08-3")
2299                                    || code.starts_with("1-08-4")
2300                                    || code.starts_with("1-08-5")
2301                            })
2302                            .unwrap_or(false)
2303                });
2304                if has_qualifying_pia {
2305                    *count_by_zid.entry(zid).or_insert(0) += 1;
2306                }
2307            }
2308        }
2309        ConditionResult::from(count_by_zid.values().any(|&c| c > 1))
2310    }
2311
2312    /// [63] Wenn in dieser SG4 das STS+E01++A15/ A99 (Status der Antwort) vorhanden
2313    fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
2314        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2315        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2316        let found = sts_segs.iter().any(|s| {
2317            s.elements
2318                .first()
2319                .and_then(|e| e.first())
2320                .is_some_and(|v| v == "E01")
2321                && s.elements
2322                    .get(2)
2323                    .and_then(|e| e.first())
2324                    .is_some_and(|v| ["A15", "A99"].contains(&v.as_str()))
2325        });
2326        ConditionResult::from(found)
2327    }
2328
2329    /// [64] Wenn mehr als eine SG8 SEQ+ZE1 (Informative Netznutzungsabrechnungsdaten der Marktlokation) mit einer Gruppen-/ Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5...
2330    fn evaluate_64(&self, ctx: &EvaluationContext) -> ConditionResult {
2331        // Check: more than one SG8 SEQ+ZE1 has PIA+Z02 with code starting with 1-08-1/2/3/4/5
2332        let pia_segs = ctx.find_segments("PIA");
2333        let count = pia_segs
2334            .iter()
2335            .filter(|s| {
2336                s.elements
2337                    .first()
2338                    .and_then(|e| e.first())
2339                    .is_some_and(|v| v == "Z02")
2340                    && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
2341                        ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"]
2342                            .iter()
2343                            .any(|p| v.starts_with(p))
2344                    })
2345            })
2346            .count();
2347        ConditionResult::from(count > 1)
2348    }
2349
2350    /// [65] Wenn in derselben SG8 SEQ+ZE1 (Informative Netznutzungsabrechnungsdaten der Marktlokation) eine Gruppen-/Artikel-ID im PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5 beginnt, v...
2351    fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
2352        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2353        // Check: PIA+Z02 with code starting with 1-08-1/2/3/4/5 exists in same SG8 SEQ+ZE1
2354        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
2355        let found = pia_segs.iter().any(|s| {
2356            s.elements
2357                .first()
2358                .and_then(|e| e.first())
2359                .is_some_and(|v| v == "Z02")
2360                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
2361                    ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"]
2362                        .iter()
2363                        .any(|p| v.starts_with(p))
2364                })
2365        });
2366        ConditionResult::from(found)
2367    }
2368
2369    /// [66] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als einmal vorhanden
2370    fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
2371        let seq_segs = ctx.find_segments("SEQ");
2372        let count = seq_segs
2373            .iter()
2374            .filter(|s| {
2375                s.elements
2376                    .first()
2377                    .and_then(|e| e.first())
2378                    .is_some_and(|v| v == "ZH0")
2379            })
2380            .count();
2381        ConditionResult::from(count > 1)
2382    }
2383
2384    /// [67] Wenn in keinem SG8 SEQ+Z79 (Bestandteil eine Produktpaketes CCI+Z66/CAV+ZH9 (Produkteigenschaft/ Code der Produkteigenschaft) der Code 9991000002933 (Ruhende Marktlokation ausprägen) vorhanden ist.
2385    fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
2386        // Check: no SG8 SEQ+Z79 has CAV+ZH9 with code 9991000002933
2387        let cav_segs = ctx.find_segments("CAV");
2388        let found = cav_segs.iter().any(|s| {
2389            s.elements
2390                .first()
2391                .and_then(|e| e.first())
2392                .is_some_and(|v| v == "ZH9")
2393                && s.elements
2394                    .first()
2395                    .and_then(|e| e.get(1))
2396                    .is_some_and(|v| v == "9991000002933")
2397        });
2398        ConditionResult::from(!found)
2399    }
2400
2401    /// [68] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als zweimal vorhanden
2402    fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
2403        let seq_segs = ctx.find_segments("SEQ");
2404        let count = seq_segs
2405            .iter()
2406            .filter(|s| {
2407                s.elements
2408                    .first()
2409                    .and_then(|e| e.first())
2410                    .is_some_and(|v| v == "ZH0")
2411            })
2412            .count();
2413        ConditionResult::from(count > 2)
2414    }
2415
2416    /// [69] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als dreimal vorhanden
2417    fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
2418        let seq_segs = ctx.find_segments("SEQ");
2419        let count = seq_segs
2420            .iter()
2421            .filter(|s| {
2422                s.elements
2423                    .first()
2424                    .and_then(|e| e.first())
2425                    .is_some_and(|v| v == "ZH0")
2426            })
2427            .count();
2428        ConditionResult::from(count > 3)
2429    }
2430
2431    /// [70] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) fünfmal vorhanden
2432    fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
2433        let seq_segs = ctx.find_segments("SEQ");
2434        let count = seq_segs
2435            .iter()
2436            .filter(|s| {
2437                s.elements
2438                    .first()
2439                    .and_then(|e| e.first())
2440                    .is_some_and(|v| v == "ZH0")
2441            })
2442            .count();
2443        ConditionResult::from(count == 5)
2444    }
2445
2446    /// [71] Wenn Antwort auf Zuordnung übermittelt wird
2447    fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
2448        // Antwort auf Zuordnung — depends on PID context
2449        ctx.external.evaluate("assignment_response")
2450    }
2451
2452    /// [72] Wenn Antwort auf Beendigung der Zuordnung übermittelt wird
2453    fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
2454        // Antwort auf Beendigung der Zuordnung — depends on PID context
2455        ctx.external.evaluate("assignment_end_response")
2456    }
2457
2458    /// [73] Wenn in derselben SG8 im PIA DE7140 ein Code aus der Codeliste der Gruppenartikel- und Artikel-ID vorhanden ist, der in der Spalte UTILMD/Preisangabe mit X gekennzeichnet ist
2459    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
2460    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
2461    // Regenerating this function should preserve the shape, but re-verify
2462    // against the committed body if generate-conditions is run.
2463    fn evaluate_73(&self, ctx: &EvaluationContext) -> ConditionResult {
2464        // [73] Wenn in derselben SG8 im PIA DE7140 ein Code aus der
2465        // Codeliste der Gruppenartikel- und Artikel-ID vorhanden ist,
2466        // der in der Spalte UTILMD/Preisangabe mit X gekennzeichnet ist.
2467        //
2468        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
2469        // narrows to the enclosing SG8 instance; otherwise falls back to
2470        // message-wide find_segments. Matches the AHB wording
2471        // "in derselben SG8 im PIA DE7140 ein Code...".
2472        use crate::eval::CodelistColumn;
2473        let any_match = ctx.scoped_find_segments_in("SG8", "PIA").iter().any(|s| {
2474            let is_z02 = s
2475                .elements
2476                .first()
2477                .and_then(|e| e.first())
2478                .is_some_and(|v| v == "Z02");
2479            if !is_z02 {
2480                return false;
2481            }
2482            let code = match s.elements.get(1).and_then(|e| e.first()) {
2483                Some(c) if !c.is_empty() => c,
2484                _ => return false,
2485            };
2486            ctx.codelist
2487                .flag(code, CodelistColumn::UtilmdPreisangabe)
2488                .unwrap_or(false)
2489        });
2490        ConditionResult::from(any_match)
2491    }
2492
2493    /// [74] Wenn in der selben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) vorhanden
2494    fn evaluate_74(&self, ctx: &EvaluationContext) -> ConditionResult {
2495        let seq_segs = ctx.find_segments("SEQ");
2496        let has_seq = seq_segs.iter().any(|s| {
2497            s.elements
2498                .first()
2499                .and_then(|e| e.first())
2500                .is_some_and(|v| v == "Z59")
2501        });
2502        let pia_segs = ctx.find_segments("PIA");
2503        let has_pia = pia_segs.iter().any(|s| {
2504            s.elements
2505                .first()
2506                .and_then(|e| e.first())
2507                .is_some_and(|v| v == "5")
2508        });
2509        ConditionResult::from(has_seq && has_pia)
2510    }
2511
2512    /// [75] Wenn in diesem PIA+5 in DE7140 der Code 9991000000721 (vgl.: Kapitel 4.2. Konfigurationsprodukte Leistungskurvendefinition der Codeliste der Konfigurationen) vorhanden
2513    fn evaluate_75(&self, ctx: &EvaluationContext) -> ConditionResult {
2514        ctx.external.evaluate("code_list_membership_check")
2515    }
2516
2517    /// [76] Wenn Summenzeitreihe auf Ebene des Bilanzierungsgebiet
2518    // REVIEW: SEQ+Z49 is 'Abgerechnete Daten der Bilanzierungsgebietssummenzeitreihe' — the only SEQ code in the reference that explicitly names a Bilanzierungsgebiet-level Summenzeitreihe. Checking its presence is the most direct mapping of the condition text. A broader interpretation involving CAV codes (Bezeichnung der Summenzeitreihe) is possible but those codes are not fully enumerated in the provided reference. (medium confidence)
2519    fn evaluate_76(&self, ctx: &EvaluationContext) -> ConditionResult {
2520        ctx.has_qualifier("SEQ", 0, "Z49")
2521    }
2522
2523    /// [77] Wenn SG8 SEQ+Z03 (Zähleinrichtungsdaten) CAV+Z30 (Identifikation/Nummer des Gerätes) nicht vorhanden
2524    fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
2525        let seq_segs = ctx.find_segments("SEQ");
2526        let found = seq_segs.iter().any(|s| {
2527            s.elements
2528                .first()
2529                .and_then(|e| e.first())
2530                .is_some_and(|v| ["Z03"].contains(&v.as_str()))
2531        });
2532        ConditionResult::from(!found)
2533    }
2534
2535    /// [78] Wenn SG4 STS+7++E02 (Transaktionsgrund: Einzug in Neuanlage) nicht vorhanden
2536    fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
2537        let sts_segs = ctx.find_segments("STS");
2538        let found = sts_segs.iter().any(|s| {
2539            s.elements
2540                .first()
2541                .and_then(|e| e.first())
2542                .is_some_and(|v| v == "7")
2543                && s.elements
2544                    .get(2)
2545                    .and_then(|e| e.first())
2546                    .is_some_and(|v| ["E02"].contains(&v.as_str()))
2547        });
2548        ConditionResult::from(!found)
2549    }
2550
2551    /// [83] Wenn in dieser SG4 das STS+E01++A08/ A16/ A99 (Status der Antwort) vorhanden
2552    fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
2553        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2554        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2555        let found = sts_segs.iter().any(|s| {
2556            s.elements
2557                .first()
2558                .and_then(|e| e.first())
2559                .is_some_and(|v| v == "E01")
2560                && s.elements
2561                    .get(2)
2562                    .and_then(|e| e.first())
2563                    .is_some_and(|v| ["A08", "A16", "A99"].contains(&v.as_str()))
2564        });
2565        ConditionResult::from(found)
2566    }
2567
2568    /// [84] Wenn SG4 STS+E01++A57 (Status der Antwort) vorhanden
2569    fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
2570        let sts_segs = ctx.find_segments("STS");
2571        let found = sts_segs.iter().any(|s| {
2572            s.elements
2573                .first()
2574                .and_then(|e| e.first())
2575                .is_some_and(|v| v == "E01")
2576                && s.elements
2577                    .get(2)
2578                    .and_then(|e| e.first())
2579                    .is_some_and(|v| ["A57"].contains(&v.as_str()))
2580        });
2581        ConditionResult::from(found)
2582    }
2583
2584    /// [85] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle M, Q, H oder J enthält
2585    fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
2586        let dtm_segs = ctx.find_segments("DTM");
2587        let found = dtm_segs.iter().any(|s| {
2588            s.elements
2589                .first()
2590                .and_then(|e| e.first())
2591                .is_some_and(|v| v == "Z01")
2592                && s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| {
2593                    v.chars()
2594                        .nth(3)
2595                        .is_some_and(|c| ['M', 'Q', 'H', 'J'].contains(&c))
2596                })
2597        });
2598        ConditionResult::from(found)
2599    }
2600
2601    /// [86] Wenn das RFF+Z50 (Termine der Marktlokation) aus dieser SG6 (Termine der Marktlokation) auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der...
2602    fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
2603        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" for this SG6's RFF+Z50 — compared within the enclosing SG4 when the scope
2604        // reaches an SG6; the message as before otherwise.
2605        if let Some(scope) = ctx.enclosing_scope("SG6") {
2606            let refs = sg4_instances::rff_values_at(ctx, scope, "Z50");
2607            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
2608                sg4_instances::has_cci(s, "", "ZA6")
2609            }));
2610        }
2611        // Check: RFF+Z50 references same LOC as RFF+Z18 in another SG8
2612        ctx.groups_share_qualified_value(
2613            "RFF",
2614            0,
2615            "Z50",
2616            0,
2617            2,
2618            &["SG4", "SG6"],
2619            "RFF",
2620            0,
2621            2,
2622            &["SG4", "SG8"],
2623        )
2624    }
2625
2626    /// [87] Es ist ein Monatserster 0 Uhr (gem. deutscher Zeit) anzugeben
2627    fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
2628        // Check: date value is 1st of month at 00:00 (format 303: CCYYMMDDHHmm)
2629        let dtm_segs = ctx.find_segments("DTM");
2630        let found = dtm_segs.iter().any(|s| {
2631            s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| {
2632                v.len() >= 12
2633                    && &v[6..8] == "01"  // day = 01
2634                    && &v[8..12] == "0000" // time = 00:00
2635            })
2636        });
2637        if !found {
2638            // Check if any DTM has a non-matching date (definite False) vs no DTM at all (Unknown)
2639            if dtm_segs.is_empty() {
2640                return ConditionResult::Unknown;
2641            }
2642        }
2643        ConditionResult::from(found)
2644    }
2645
2646    /// [88] Wert muss identisch mit DE2380 aus dem SG4 DTM+157 (Änderung zum) sein
2647    fn evaluate_88(&self, ctx: &EvaluationContext) -> ConditionResult {
2648        // Check: value matches DTM+157 (Änderung zum)
2649        let dtm157 = ctx.find_segments_with_qualifier("DTM", 0, "157");
2650        if dtm157.is_empty() {
2651            return ConditionResult::Unknown;
2652        }
2653        // Condition is True when DTM+157 exists (the value check is structural)
2654        ConditionResult::True
2655    }
2656
2657    /// [89] Wenn im SG8 SEQ+Z01 (Daten der Marktlokation) mit identischer Zeitraum-ID im DE1050 wie in diesem SG8, das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+...
2658    // REVIEW: Checks whether any SG8 instance contains an SG10 child with CCI at elements[2][0]=ZA6 and CAV at elements[0][0]=E14. The condition text requires the Zeitraum-ID in DE1050 to match 'this SG8', but a per-group evaluation context is not available in the current API — the Zeitraum-ID cross-reference is therefore not enforced and is instead approximated as a message-wide check. The CCI qualifier position (elements[2][0]) is confirmed by the MIG reference for CCI segments in SG10. (medium confidence)
2659    fn evaluate_89(&self, ctx: &EvaluationContext) -> ConditionResult {
2660        let nav = match ctx.navigator {
2661            Some(n) => n,
2662            None => {
2663                return ctx.filtered_parent_child_has_qualifier(
2664                    &["SG4", "SG8"],
2665                    "SEQ",
2666                    0,
2667                    "Z01",
2668                    "SG10",
2669                    "CCI",
2670                    2,
2671                    "ZA6",
2672                )
2673            }
2674        };
2675        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2676        for i in 0..sg8_count {
2677            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2678            for j in 0..sg10_count {
2679                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2680                let has_cci_za6 = ccis.iter().any(|s| {
2681                    s.elements
2682                        .get(2)
2683                        .and_then(|e: &Vec<String>| e.first())
2684                        .is_some_and(|v: &String| v == "ZA6")
2685                });
2686                if !has_cci_za6 {
2687                    continue;
2688                }
2689                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2690                if cavs.iter().any(|s| {
2691                    s.elements
2692                        .first()
2693                        .and_then(|e: &Vec<String>| e.first())
2694                        .is_some_and(|v: &String| v == "E14")
2695                }) {
2696                    return ConditionResult::True;
2697                }
2698            }
2699        }
2700        ConditionResult::False
2701    }
2702
2703    /// [90] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) einmal mit CAV+E14 (TLP/TEP) und einmal mit CAV+E02 (SLP/SEP) in dieser SG8 vorhanden
2704    // REVIEW: Within each SG8, iterates all SG10 child instances. For each SG10 that has a CCI with elements[2][0]=ZA6, checks the CAV elements[0][0] value. Tracks whether E14 and E02 have both been seen within the same SG8. Returns True when both are found in a single SG8, satisfying the requirement that both Prognosegrundlage options appear 'in dieser SG8'. (medium confidence)
2705    fn evaluate_90(&self, ctx: &EvaluationContext) -> ConditionResult {
2706        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
2707        // scope reaches one; the message as before otherwise.
2708        if let Some(_scope) = ctx.enclosing_scope("SG8") {
2709            return ConditionResult::from(
2710                ctx.enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
2711                    .unwrap_or_default()
2712                    .iter()
2713                    .any(|segs| {
2714                        segs.iter()
2715                            .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
2716                            && segs
2717                                .iter()
2718                                .any(|s| s.id == "CAV" && s.get_element(0) == "E14")
2719                    })
2720                    && ctx
2721                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
2722                        .unwrap_or_default()
2723                        .iter()
2724                        .any(|segs| {
2725                            segs.iter()
2726                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
2727                                && segs
2728                                    .iter()
2729                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E02")
2730                        }),
2731            );
2732        }
2733        let nav = match ctx.navigator {
2734            Some(n) => n,
2735            None => return ConditionResult::Unknown,
2736        };
2737        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2738        for i in 0..sg8_count {
2739            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2740            let mut has_za6_e14 = false;
2741            let mut has_za6_e02 = false;
2742            for j in 0..sg10_count {
2743                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2744                let has_cci_za6 = ccis.iter().any(|s| {
2745                    s.elements
2746                        .get(2)
2747                        .and_then(|e: &Vec<String>| e.first())
2748                        .is_some_and(|v: &String| v == "ZA6")
2749                });
2750                if !has_cci_za6 {
2751                    continue;
2752                }
2753                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2754                for cav in &cavs {
2755                    if let Some(code) = cav.elements.first().and_then(|e: &Vec<String>| e.first()) {
2756                        if code == "E14" {
2757                            has_za6_e14 = true;
2758                        }
2759                        if code == "E02" {
2760                            has_za6_e02 = true;
2761                        }
2762                    }
2763                }
2764            }
2765            if has_za6_e14 && has_za6_e02 {
2766                return ConditionResult::True;
2767            }
2768        }
2769        ConditionResult::False
2770    }
2771
2772    /// [91] Wenn nicht SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) einmal mit CAV+E14 (TLP/ TEP) und einmal mit CAV+E02 (SLP/SEP) in dieser SG8 vorhanden
2773    // REVIEW: Logical negation of condition 90. Condition 91 is explicitly defined as 'Wenn NICHT (CCI+++ZA6 einmal mit CAV+E14 und einmal mit CAV+E02 in dieser SG8 vorhanden)', which is the exact complement of condition 90. Preserving Unknown propagation handles the no-navigator fallback case correctly. (medium confidence)
2774    fn evaluate_91(&self, ctx: &EvaluationContext) -> ConditionResult {
2775        match self.evaluate_90(ctx) {
2776            ConditionResult::True => ConditionResult::False,
2777            ConditionResult::False => ConditionResult::True,
2778            ConditionResult::Unknown => ConditionResult::Unknown,
2779        }
2780    }
2781
2782    /// [92] Wenn Wert innerhalb SG bzw. Segment geändert wird
2783    fn evaluate_92(&self, _ctx: &EvaluationContext) -> ConditionResult {
2784        // TODO: implement
2785        ConditionResult::Unknown
2786    }
2787
2788    /// [93] Erlaubte Codes aus der PRICAT BGM+Z32 (Preisblatt Messstellenbetrieb) des verantwortlichen MSB
2789    fn evaluate_93(&self, _ctx: &EvaluationContext) -> ConditionResult {
2790        // TODO: implement
2791        ConditionResult::Unknown
2792    }
2793
2794    /// [94] Wenn ein Segment innerhalb der SG vorhanden
2795    fn evaluate_94(&self, _ctx: &EvaluationContext) -> ConditionResult {
2796        // Structural: segment exists within SG (always true for valid messages)
2797        ConditionResult::True
2798    }
2799
2800    /// [95] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) CAV+ZE5 (E-Mobilität) vorhanden
2801    fn evaluate_95(&self, ctx: &EvaluationContext) -> ConditionResult {
2802        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
2803        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
2804        let has_cci = cci_segs.iter().any(|s| {
2805            s.elements
2806                .first()
2807                .and_then(|e| e.first())
2808                .is_some_and(|v| v == "Z17")
2809        });
2810        let cav_segs = ctx.scoped_find_segments_in("SG10", "CAV");
2811        let has_cav = cav_segs.iter().any(|s| {
2812            s.elements
2813                .first()
2814                .and_then(|e| e.first())
2815                .is_some_and(|v| ["ZE5"].contains(&v.as_str()))
2816        });
2817        ConditionResult::from(has_cci && has_cav)
2818    }
2819
2820    /// [96] Wenn SG4 STS+7++xxx+ZAP (Transaktionsgrundergänzung ruhende Marktlokation) vorhanden
2821    fn evaluate_96(&self, ctx: &EvaluationContext) -> ConditionResult {
2822        let sts_segs = ctx.find_segments("STS");
2823        let found = sts_segs.iter().any(|s| {
2824            s.elements
2825                .first()
2826                .and_then(|e| e.first())
2827                .is_some_and(|v| v == "7")
2828                && s.elements
2829                    .get(3)
2830                    .and_then(|e| e.first())
2831                    .is_some_and(|v| ["ZAP"].contains(&v.as_str()))
2832        });
2833        ConditionResult::from(found)
2834    }
2835
2836    /// [97] Wenn in einem SG10 CAV+ZH9 DE7110 der Code der Produkteigenschaft (Wertebereich) 9991000002420 (Marktprämie) vorhanden ist
2837    fn evaluate_97(&self, ctx: &EvaluationContext) -> ConditionResult {
2838        // Check: CAV+ZH9 has code 9991000002420 (Marktprämie)
2839        let cav_segs = ctx.find_segments("CAV");
2840        let found = cav_segs.iter().any(|s| {
2841            s.elements
2842                .first()
2843                .and_then(|e| e.first())
2844                .is_some_and(|v| v == "ZH9")
2845                && s.elements
2846                    .first()
2847                    .and_then(|e| e.get(1))
2848                    .is_some_and(|v| v == "9991000002420")
2849        });
2850        ConditionResult::from(found)
2851    }
2852
2853    /// [98] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle NB
2854    fn evaluate_98(&self, ctx: &EvaluationContext) -> ConditionResult {
2855        ctx.external.evaluate("sender_role_check")
2856    }
2857
2858    /// [99] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle ÜNB
2859    fn evaluate_99(&self, ctx: &EvaluationContext) -> ConditionResult {
2860        ctx.external.evaluate("sender_role_check")
2861    }
2862
2863    /// [100] Wenn SG10 CAV+TLS/ TES/ BIT/ GET/ GAT/ SOT/ WNT/ WFT/ WAT vorhanden
2864    fn evaluate_100(&self, ctx: &EvaluationContext) -> ConditionResult {
2865        let cav_segs = ctx.find_segments("CAV");
2866        let found = cav_segs.iter().any(|s| {
2867            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2868                [
2869                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
2870                ]
2871                .contains(&v.as_str())
2872            })
2873        });
2874        ConditionResult::from(found)
2875    }
2876
2877    /// [101] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E14/ Z36 (TLP/ TEP/ TEP mit Ref.messung) in dieser SG8 vorhanden
2878    fn evaluate_101(&self, ctx: &EvaluationContext) -> ConditionResult {
2879        // HAND-EDITED: the CCI and the CAV of one SG10 of this SG8.
2880        sg10_has(ctx, true, |segs| {
2881            sg4_instances::has_cci(segs, "", "ZA6")
2882                && (sg4_instances::has_cav(segs, "E14") || sg4_instances::has_cav(segs, "Z36"))
2883        })
2884    }
2885
2886    /// [102] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+Z36 (TEP mit Ref.messung) in dieser SG8 vorhanden
2887    fn evaluate_102(&self, ctx: &EvaluationContext) -> ConditionResult {
2888        // HAND-EDITED: the CCI and the CAV of one SG10 of this SG8.
2889        sg10_has(ctx, true, |segs| {
2890            sg4_instances::has_cci(segs, "", "ZA6") && sg4_instances::has_cav(segs, "Z36")
2891        })
2892    }
2893
2894    /// [103] Wenn SG9 QTY+265 (Arbeit / Leistung für tagesparameterabhängig e Marktlokation: Veranschlagte Jahresmenge Gesamt) in dieser SG8 vorhanden
2895    fn evaluate_103(&self, ctx: &EvaluationContext) -> ConditionResult {
2896        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2897        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2898        let found = qty_segs.iter().any(|s| {
2899            s.elements
2900                .first()
2901                .and_then(|e| e.first())
2902                .is_some_and(|v| v == "265")
2903        });
2904        ConditionResult::from(found)
2905    }
2906
2907    /// [104] Wenn SG9 QTY+Z08 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: angepasste elektrische Arbeit nach Anhang D) in dieser SG8 vorhanden
2908    fn evaluate_104(&self, ctx: &EvaluationContext) -> ConditionResult {
2909        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2910        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2911        let found = qty_segs.iter().any(|s| {
2912            s.elements
2913                .first()
2914                .and_then(|e| e.first())
2915                .is_some_and(|v| v == "Z08")
2916        });
2917        ConditionResult::from(found)
2918    }
2919
2920    /// [105] Wenn SG9 QTY+Z10 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: Leistung der Marktlokation) in dieser SG8 vorhanden
2921    fn evaluate_105(&self, ctx: &EvaluationContext) -> ConditionResult {
2922        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2923        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2924        let found = qty_segs.iter().any(|s| {
2925            s.elements
2926                .first()
2927                .and_then(|e| e.first())
2928                .is_some_and(|v| v == "Z10")
2929        });
2930        ConditionResult::from(found)
2931    }
2932
2933    /// [106] Wenn in dieser SG8 SEQ+Z01 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
2934    fn evaluate_106(&self, ctx: &EvaluationContext) -> ConditionResult {
2935        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2936        // Check: in SG8 with SEQ+Z01, SG10 has CCI+++ZA6 and CAV+E02
2937        ctx.this_group_parent_child_has_qualifier(
2938            &["SG4", "SG8"],
2939            "SEQ",
2940            0,
2941            "Z01",
2942            "SG10",
2943            "CAV",
2944            0,
2945            "E02",
2946        )
2947    }
2948
2949    /// [107] Wenn in derselben SG8 das SG10 CAV+SLS/ SES/ BIP/ GEP/ GAP/ SOP/ WNP/ WFP/ WAP vorhanden
2950    fn evaluate_107(&self, ctx: &EvaluationContext) -> ConditionResult {
2951        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2952        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
2953        let found = cav_segs.iter().any(|s| {
2954            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2955                [
2956                    "SLS", "SES", "BIP", "GEP", "GAP", "SOP", "WNP", "WFP", "WAP",
2957                ]
2958                .contains(&v.as_str())
2959            })
2960        });
2961        ConditionResult::from(found)
2962    }
2963
2964    /// [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
2965    // HAND-EDITED — do not let generate-conditions regenerate this function
2966    // without first confirming prompt.rs B6 SCOPE RULES section is in the
2967    // system prompt. Regeneration without that guidance would emit
2968    // ctx.has_qualifier (message-wide) and silently reintroduce the
2969    // per-instance bug this function fixes.
2970    // See: docs/plans/2026-04-23-ahb-codelist-and-per-instance-plan.md B7.
2971    fn evaluate_108(&self, ctx: &EvaluationContext) -> ConditionResult {
2972        // [108] Wenn in derselben SG8 ein PIA+Z02 mit einem Code aus
2973        // 1-08-1/2/3/4/5 vorhanden. Scope-aware: narrows to the enclosing
2974        // SG8 instance when GroupScope is set; falls back to message-wide.
2975        const PREFIXES: &[&str] = &["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"];
2976        let found = ctx
2977            .scoped_find_segments_in("SG8", "PIA")
2978            .iter()
2979            .filter(|s| {
2980                s.elements
2981                    .first()
2982                    .and_then(|e| e.first())
2983                    .is_some_and(|q| q == "Z02")
2984            })
2985            .any(|s| {
2986                s.elements
2987                    .get(1)
2988                    .and_then(|e| e.first())
2989                    .is_some_and(|v| PREFIXES.iter().any(|p| v.starts_with(p)))
2990            });
2991        ConditionResult::from(found)
2992    }
2993
2994    /// [110] Wenn SG10 CAV+LGS/ EGS/ BIL/ GEL/ GAL/ SOL/ WNL/ WFL / WAL vorhanden
2995    fn evaluate_110(&self, ctx: &EvaluationContext) -> ConditionResult {
2996        let cav_segs = ctx.find_segments("CAV");
2997        let found = cav_segs.iter().any(|s| {
2998            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2999                [
3000                    "LGS", "EGS", "BIL", "GEL", "GAL", "SOL", "WNL", "WFL", "WAL",
3001                ]
3002                .contains(&v.as_str())
3003            })
3004        });
3005        ConditionResult::from(found)
3006    }
3007
3008    /// [111] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) nicht vorhanden
3009    fn evaluate_111(&self, ctx: &EvaluationContext) -> ConditionResult {
3010        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3011        // scope reaches one; the message as before otherwise.
3012        if let Some(scope) = ctx.enclosing_scope("SG8") {
3013            return ConditionResult::from(
3014                ctx.find_segments_at(scope, "SEQ")
3015                    .iter()
3016                    .any(|s| matches!(s.get_element(0), "Z59"))
3017                    && !ctx
3018                        .find_segments_at(scope, "PIA")
3019                        .iter()
3020                        .any(|s| s.get_element(0) == "5"),
3021            );
3022        }
3023        ctx.any_group_has_qualifier_without("SEQ", 0, "Z59", "PIA", 0, "5", &["SG4", "SG8"])
3024    }
3025
3026    /// [112] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das SG10 CCI+11 (Details zum Produkt der Marktlokation) nicht vorhanden
3027    fn evaluate_112(&self, ctx: &EvaluationContext) -> ConditionResult {
3028        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3029        // scope reaches one; the message as before otherwise.
3030        if let Some(scope) = ctx.enclosing_scope("SG8") {
3031            return ConditionResult::from(
3032                ctx.find_segments_at(scope, "SEQ")
3033                    .iter()
3034                    .any(|s| matches!(s.get_element(0), "Z59"))
3035                    && !ctx
3036                        .find_segments_at(scope, "CCI")
3037                        .iter()
3038                        .any(|s| s.get_element(0) == "11"),
3039            );
3040        }
3041        let result = ctx.filtered_parent_child_has_qualifier(
3042            &["SG4", "SG8"],
3043            "SEQ",
3044            0,
3045            "Z59",
3046            "SG10",
3047            "CCI",
3048            0,
3049            "11",
3050        );
3051        match result {
3052            ConditionResult::True => ConditionResult::False,
3053            ConditionResult::False => ConditionResult::True,
3054            ConditionResult::Unknown => ConditionResult::Unknown,
3055        }
3056    }
3057
3058    /// [113] Wenn es sich um eine Antwort auf die Bestellung einer Zählzeit handelt
3059    fn evaluate_113(&self, ctx: &EvaluationContext) -> ConditionResult {
3060        // Antwort auf Bestellung einer Zählzeit — depends on PID context
3061        ctx.external.evaluate("counting_time_order_response")
3062    }
3063
3064    /// [114] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E06 (Niederspannung) in dieser SG vorhanden
3065    // 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)
3066    fn evaluate_114(&self, ctx: &EvaluationContext) -> ConditionResult {
3067        let nav = match ctx.navigator {
3068            Some(n) => n,
3069            None => {
3070                return match (
3071                    ctx.has_qualifier("CCI", 0, "E03"),
3072                    ctx.has_qualifier("CAV", 0, "E06"),
3073                ) {
3074                    (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
3075                    (ConditionResult::False, _) | (_, ConditionResult::False) => {
3076                        ConditionResult::False
3077                    }
3078                    _ => ConditionResult::Unknown,
3079                };
3080            }
3081        };
3082        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3083        for i in 0..sg8_count {
3084            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3085            for j in 0..sg10_count {
3086                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3087                let has_cci_e03 = ccis.iter().any(|s| {
3088                    s.elements
3089                        .first()
3090                        .and_then(|e: &Vec<String>| e.first())
3091                        .is_some_and(|v: &String| v == "E03")
3092                });
3093                if has_cci_e03 {
3094                    let cavs =
3095                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3096                    if cavs.iter().any(|s| {
3097                        s.elements
3098                            .first()
3099                            .and_then(|e: &Vec<String>| e.first())
3100                            .is_some_and(|v: &String| v == "E06")
3101                    }) {
3102                        return ConditionResult::True;
3103                    }
3104                }
3105            }
3106        }
3107        ConditionResult::False
3108    }
3109
3110    /// [115] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E05 (Mittelspannung) in dieser SG vorhanden
3111    fn evaluate_115(&self, ctx: &EvaluationContext) -> ConditionResult {
3112        let cci_segs = ctx.find_segments("CCI");
3113        let has_cci = cci_segs.iter().any(|s| {
3114            s.elements
3115                .first()
3116                .and_then(|e| e.first())
3117                .is_some_and(|v| v == "E03")
3118        });
3119        let cav_segs = ctx.find_segments("CAV");
3120        let has_cav = cav_segs.iter().any(|s| {
3121            s.elements
3122                .first()
3123                .and_then(|e| e.first())
3124                .is_some_and(|v| v == "E05")
3125        });
3126        ConditionResult::from(has_cci && has_cav)
3127    }
3128
3129    /// [116] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E04 (Hochspannung) in dieser SG vorhanden
3130    fn evaluate_116(&self, ctx: &EvaluationContext) -> ConditionResult {
3131        let cci_segs = ctx.find_segments("CCI");
3132        let has_cci = cci_segs.iter().any(|s| {
3133            s.elements
3134                .first()
3135                .and_then(|e| e.first())
3136                .is_some_and(|v| v == "E03")
3137        });
3138        let cav_segs = ctx.find_segments("CAV");
3139        let has_cav = cav_segs.iter().any(|s| {
3140            s.elements
3141                .first()
3142                .and_then(|e| e.first())
3143                .is_some_and(|v| v == "E04")
3144        });
3145        ConditionResult::from(has_cci && has_cav)
3146    }
3147
3148    /// [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 (...
3149    // 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)
3150    fn evaluate_117(&self, ctx: &EvaluationContext) -> ConditionResult {
3151        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (TLP/TEP) — compared within the enclosing SG4 when the scope
3152        // reaches an SG8; the message as before otherwise.
3153        if let Some(scope) = ctx.enclosing_scope("SG8") {
3154            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
3155            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
3156                sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "E14")
3157            }));
3158        }
3159        let nav = match ctx.navigator {
3160            Some(n) => n,
3161            None => return ConditionResult::Unknown,
3162        };
3163        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3164        for i in 0..sg8_count {
3165            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3166            for j in 0..sg10_count {
3167                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3168                let has_cci_za6 = ccis.iter().any(|s| {
3169                    s.elements
3170                        .get(2)
3171                        .and_then(|e: &Vec<String>| e.first())
3172                        .is_some_and(|v: &String| v == "ZA6")
3173                });
3174                if has_cci_za6 {
3175                    let cavs =
3176                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3177                    if cavs.iter().any(|s| {
3178                        s.elements
3179                            .first()
3180                            .and_then(|e: &Vec<String>| e.first())
3181                            .is_some_and(|v: &String| v == "E14")
3182                    }) {
3183                        return ConditionResult::True;
3184                    }
3185                }
3186            }
3187        }
3188        ConditionResult::False
3189    }
3190
3191    /// [118] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/ SEP) vorhanden
3192    fn evaluate_118(&self, ctx: &EvaluationContext) -> ConditionResult {
3193        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3194        // scope reaches one; the message as before otherwise.
3195        if let Some(scope) = ctx.enclosing_scope("SG8") {
3196            return ConditionResult::from(
3197                ctx.find_segments_at(scope, "SEQ")
3198                    .iter()
3199                    .any(|s| matches!(s.get_element(0), "Z98"))
3200                    && ctx
3201                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
3202                        .unwrap_or_default()
3203                        .iter()
3204                        .any(|segs| {
3205                            segs.iter()
3206                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
3207                                && segs
3208                                    .iter()
3209                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E02")
3210                        }),
3211            );
3212        }
3213        let has_cci = ctx.filtered_parent_child_has_qualifier(
3214            &["SG4", "SG8"],
3215            "SEQ",
3216            0,
3217            "Z98",
3218            "SG10",
3219            "CCI",
3220            2,
3221            "ZA6",
3222        );
3223        if has_cci != ConditionResult::True {
3224            return has_cci;
3225        }
3226        ctx.filtered_parent_child_has_qualifier(
3227            &["SG4", "SG8"],
3228            "SEQ",
3229            0,
3230            "Z98",
3231            "SG10",
3232            "CAV",
3233            0,
3234            "E02",
3235        )
3236    }
3237
3238    /// [119] Wenn in der SG8 SEQ+Z01/ Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
3239    fn evaluate_119(&self, ctx: &EvaluationContext) -> ConditionResult {
3240        // Check: in SG8 with SEQ+Z01 or Z98, SG10 has CCI+++ZA6 and CAV+E02
3241        let r1 = ctx.filtered_parent_child_has_qualifier(
3242            &["SG4", "SG8"],
3243            "SEQ",
3244            0,
3245            "Z01",
3246            "SG10",
3247            "CAV",
3248            0,
3249            "E02",
3250        );
3251        if r1 == ConditionResult::True {
3252            return ConditionResult::True;
3253        }
3254        ctx.filtered_parent_child_has_qualifier(
3255            &["SG4", "SG8"],
3256            "SEQ",
3257            0,
3258            "Z98",
3259            "SG10",
3260            "CAV",
3261            0,
3262            "E02",
3263        )
3264    }
3265
3266    /// [120] Wenn von NB Abtretungserklärung benötigt wird
3267    fn evaluate_120(&self, _ctx: &EvaluationContext) -> ConditionResult {
3268        // TODO: implement
3269        ConditionResult::Unknown
3270    }
3271
3272    /// [121] Wenn in dem SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) das SG8 RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden
3273    fn evaluate_121(&self, ctx: &EvaluationContext) -> ConditionResult {
3274        let z03 =
3275            ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"]);
3276        if z03 == ConditionResult::True {
3277            return ConditionResult::True;
3278        }
3279        ctx.any_group_has_qualifier_without("SEQ", 0, "ZF5", "RFF", 0, "Z14", &["SG4", "SG8"])
3280    }
3281
3282    /// [122] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++E03 (Spannungsebene der Marktlokation) CAV+E06 (Niederspannung) vorhanden
3283    // 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)
3284    fn evaluate_122(&self, ctx: &EvaluationContext) -> ConditionResult {
3285        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3286        // scope reaches one; the message as before otherwise.
3287        if let Some(scope) = ctx.enclosing_scope("SG8") {
3288            return ConditionResult::from(
3289                ctx.find_segments_at(scope, "SEQ")
3290                    .iter()
3291                    .any(|s| matches!(s.get_element(0), "Z98"))
3292                    && ctx
3293                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
3294                        .unwrap_or_default()
3295                        .iter()
3296                        .any(|segs| {
3297                            segs.iter()
3298                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "E03")
3299                                && segs
3300                                    .iter()
3301                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E06")
3302                        }),
3303            );
3304        }
3305        // Check: in SG8 with SEQ+Z98, SG10 child has CCI+++E03 (Spannungsebene, elements[2][0]="E03")
3306        let has_cci_e03 = ctx.filtered_parent_child_has_qualifier(
3307            &["SG4", "SG8"],
3308            "SEQ",
3309            0,
3310            "Z98",
3311            "SG10",
3312            "CCI",
3313            2,
3314            "E03",
3315        );
3316        if has_cci_e03 != ConditionResult::True {
3317            return has_cci_e03;
3318        }
3319        // Also verify CAV+E06 (Niederspannung, elements[0][0]="E06") exists in the same SG8 scope
3320        ctx.any_group_has_qualifier("CAV", 0, "E06", &["SG4", "SG8"])
3321    }
3322
3323    /// [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...
3324    // 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)
3325    fn evaluate_123(&self, ctx: &EvaluationContext) -> ConditionResult {
3326        struct Sg8Z20Entry {
3327            mg_id: Option<String>,
3328        }
3329        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3330        let mut current: Option<Sg8Z20Entry> = None;
3331        for seg in ctx.segments {
3332            match seg.id.as_str() {
3333                "SEQ" => {
3334                    if let Some(entry) = current.take() {
3335                        entries.push(entry);
3336                    }
3337                    if seg
3338                        .elements
3339                        .first()
3340                        .and_then(|e: &Vec<String>| e.first())
3341                        .is_some_and(|v: &String| v == "Z20")
3342                    {
3343                        current = Some(Sg8Z20Entry { mg_id: None });
3344                    }
3345                }
3346                "RFF" if current.is_some() => {
3347                    if seg
3348                        .elements
3349                        .first()
3350                        .and_then(|e: &Vec<String>| e.first())
3351                        .is_some_and(|v: &String| v == "MG")
3352                    {
3353                        if let Some(ref mut entry) = current {
3354                            if entry.mg_id.is_none() {
3355                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3356                            }
3357                        }
3358                    }
3359                }
3360                _ => {}
3361            }
3362        }
3363        if let Some(entry) = current {
3364            entries.push(entry);
3365        }
3366        let ids: Vec<&str> = entries
3367            .iter()
3368            .filter_map(|e| e.mg_id.as_deref().filter(|s| !s.is_empty()))
3369            .collect();
3370        for i in 0..ids.len() {
3371            for j in (i + 1)..ids.len() {
3372                if ids[i] == ids[j] {
3373                    return ConditionResult::True;
3374                }
3375            }
3376        }
3377        ConditionResult::False
3378    }
3379
3380    /// [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...
3381    // 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)
3382    fn evaluate_124(&self, ctx: &EvaluationContext) -> ConditionResult {
3383        // HAND-EDITED: "noch mindestens eine weitere SG8 SEQ+Z20 mit derselben Zeitraum-ID … RFF+MG" — compared within the enclosing SG4 when the scope
3384        // reaches an SG8; the message as before otherwise.
3385        if let Some(scope) = ctx.enclosing_scope("SG8") {
3386            let Some((_, zeitraum)) = sg4_instances::seq_at(ctx, scope)
3387                .filter(|(code, zeitraum)| code == "Z20" && !zeitraum.is_empty())
3388            else {
3389                return ConditionResult::False;
3390            };
3391            let meters = sg4_instances::rff_values_at(ctx, scope, "MG");
3392            return ConditionResult::from(sg4_instances::other_sg8s(ctx, scope).iter().any(|p| {
3393                sg4_instances::seq_at(ctx, p).is_some_and(|(c, z)| c == "Z20" && z == zeitraum)
3394                    && sg4_instances::rff_values_at(ctx, p, "MG")
3395                        .iter()
3396                        .any(|m| meters.contains(m))
3397            }));
3398        }
3399        struct Sg8Z20Entry {
3400            zeitraum_id: String,
3401            mg_id: Option<String>,
3402        }
3403        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3404        let mut current: Option<Sg8Z20Entry> = None;
3405        for seg in ctx.segments {
3406            match seg.id.as_str() {
3407                "SEQ" => {
3408                    if let Some(entry) = current.take() {
3409                        entries.push(entry);
3410                    }
3411                    if seg
3412                        .elements
3413                        .first()
3414                        .and_then(|e: &Vec<String>| e.first())
3415                        .is_some_and(|v: &String| v == "Z20")
3416                    {
3417                        let zeitraum_id = seg
3418                            .elements
3419                            .get(1)
3420                            .and_then(|e: &Vec<String>| e.first())
3421                            .cloned()
3422                            .unwrap_or_default();
3423                        current = Some(Sg8Z20Entry {
3424                            zeitraum_id,
3425                            mg_id: None,
3426                        });
3427                    }
3428                }
3429                "RFF" if current.is_some() => {
3430                    if seg
3431                        .elements
3432                        .first()
3433                        .and_then(|e: &Vec<String>| e.first())
3434                        .is_some_and(|v: &String| v == "MG")
3435                    {
3436                        if let Some(ref mut entry) = current {
3437                            if entry.mg_id.is_none() {
3438                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3439                            }
3440                        }
3441                    }
3442                }
3443                _ => {}
3444            }
3445        }
3446        if let Some(entry) = current {
3447            entries.push(entry);
3448        }
3449        for i in 0..entries.len() {
3450            for j in (i + 1)..entries.len() {
3451                let ei = &entries[i];
3452                let ej = &entries[j];
3453                if !ei.zeitraum_id.is_empty()
3454                    && ei.zeitraum_id == ej.zeitraum_id
3455                    && ei.mg_id.is_some()
3456                    && !ei.mg_id.as_deref().unwrap_or("").is_empty()
3457                    && ei.mg_id == ej.mg_id
3458                {
3459                    return ConditionResult::True;
3460                }
3461            }
3462        }
3463        ConditionResult::False
3464    }
3465
3466    /// [125] Wenn in derselben SG8 SEQ+Z76/ ZC5/ ZC6 (Messstellenbetriebsabrechnungsdaten der Marktlokation) im DE4347 des PIA Z02 (Gruppenartikel-ID / Artikel-ID) vorhanden
3467    fn evaluate_125(&self, ctx: &EvaluationContext) -> ConditionResult {
3468        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3469        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
3470        let found = seq_segs.iter().any(|s| {
3471            s.elements
3472                .first()
3473                .and_then(|e| e.first())
3474                .is_some_and(|v| ["Z76", "ZC5", "ZC6"].contains(&v.as_str()))
3475        });
3476        ConditionResult::from(found)
3477    }
3478
3479    /// [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...
3480    fn evaluate_126(&self, ctx: &EvaluationContext) -> ConditionResult {
3481        // Check: RFF+Z49 or Z53 repetition count determines value
3482        // Approximate: check if RFF+Z49 or Z53 exists (counter is structural)
3483        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z49").len()
3484            + ctx.find_segments_with_qualifier("RFF", 0, "Z53").len();
3485        ConditionResult::from(count > 0)
3486    }
3487
3488    /// [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...
3489    fn evaluate_127(&self, ctx: &EvaluationContext) -> ConditionResult {
3490        // Check: RFF+Z48 or Z55 repetition count determines value
3491        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z48").len()
3492            + ctx.find_segments_with_qualifier("RFF", 0, "Z55").len();
3493        ConditionResult::from(count > 0)
3494    }
3495
3496    /// [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...
3497    fn evaluate_128(&self, ctx: &EvaluationContext) -> ConditionResult {
3498        // Check: RFF+Z47 or Z54 repetition count determines value
3499        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z47").len()
3500            + ctx.find_segments_with_qualifier("RFF", 0, "Z54").len();
3501        ConditionResult::from(count > 0)
3502    }
3503
3504    /// [129] Innerhalb eines SG4 IDE müssen alle DE3225 der SG5 LOC+Z16 (Marktlokation) den identischen Wert enthalten
3505    fn evaluate_129(&self, ctx: &EvaluationContext) -> ConditionResult {
3506        // Check: all LOC+Z16 DE3225 values are identical
3507        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z16");
3508        if loc_segs.len() <= 1 {
3509            return ConditionResult::True;
3510        }
3511        let first_val = loc_segs[0].elements.get(1).and_then(|e| e.first());
3512        let all_same = loc_segs
3513            .iter()
3514            .all(|s| s.elements.get(1).and_then(|e| e.first()) == first_val);
3515        ConditionResult::from(all_same)
3516    }
3517
3518    /// [130] Wenn an Messlokation vorhanden
3519    fn evaluate_130(&self, _ctx: &EvaluationContext) -> ConditionResult {
3520        // TODO: implement
3521        ConditionResult::Unknown
3522    }
3523
3524    /// [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...
3525    fn evaluate_131(&self, ctx: &EvaluationContext) -> ConditionResult {
3526        // Check: DTM+Z25 is in SG6 RFF with Zeitraum-ID "1" in DE1156
3527        let has_zeitraum_id_1 = ctx.find_segments("RFF").iter().any(|s| {
3528            s.elements
3529                .first()
3530                .and_then(|e| e.get(2))
3531                .is_some_and(|v| v == "1")
3532        });
3533        ConditionResult::from(has_zeitraum_id_1)
3534    }
3535
3536    /// [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...
3537    // 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)
3538    fn evaluate_132(&self, ctx: &EvaluationContext) -> ConditionResult {
3539        // Condition applies only when RFF in SG6 has Zeitraum-ID "1" in DE1156 (elements[0][2])
3540        let has_zeitraum_id_1 = ctx.find_segments("RFF").iter().any(|s| {
3541            s.elements
3542                .first()
3543                .and_then(|e| e.get(2))
3544                .is_some_and(|v: &String| v == "1")
3545        });
3546        if !has_zeitraum_id_1 {
3547            return ConditionResult::False;
3548        }
3549        // Get message date from DTM+137 (format 303: CCYYMMDDHHmm...)
3550        let msg_date_str = match ctx
3551            .find_segments_with_qualifier("DTM", 0, "137")
3552            .into_iter()
3553            .next()
3554        {
3555            Some(seg) => match seg.elements.first().and_then(|e| e.get(1)) {
3556                Some(v) if v.len() >= 8 => v.clone(),
3557                _ => return ConditionResult::Unknown,
3558            },
3559            None => return ConditionResult::Unknown,
3560        };
3561        // Get DTM+Z25 (Verwendung der Daten ab) value
3562        let usage_date_str = match ctx
3563            .find_segments_with_qualifier("DTM", 0, "Z25")
3564            .into_iter()
3565            .next()
3566        {
3567            Some(seg) => match seg.elements.first().and_then(|e| e.get(1)) {
3568                Some(v) if v.len() >= 12 => v.clone(),
3569                _ => return ConditionResult::Unknown,
3570            },
3571            None => return ConditionResult::Unknown,
3572        };
3573        // Parse YYYYMMDD from DTM+137 value
3574        let year: u32 = match msg_date_str[..4].parse() {
3575            Ok(v) => v,
3576            Err(_) => return ConditionResult::Unknown,
3577        };
3578        let month: u32 = match msg_date_str[4..6].parse() {
3579            Ok(v) => v,
3580            Err(_) => return ConditionResult::Unknown,
3581        };
3582        let day: u32 = match msg_date_str[6..8].parse() {
3583            Ok(v) => v,
3584            Err(_) => return ConditionResult::Unknown,
3585        };
3586        let days_in_month: u32 = match month {
3587            1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
3588            4 | 6 | 9 | 11 => 30,
3589            2 => {
3590                if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) {
3591                    29
3592                } else {
3593                    28
3594                }
3595            }
3596            _ => return ConditionResult::Unknown,
3597        };
3598        let (next_year, next_month, next_day): (u32, u32, u32) = if day >= days_in_month {
3599            if month == 12 {
3600                (year + 1, 1, 1)
3601            } else {
3602                (year, month + 1, 1)
3603            }
3604        } else {
3605            (year, month, day + 1)
3606        };
3607        // Expected DTM+Z25: next day at 00:00 — format 303 prefix YYYYMMDDHHMM = 12 chars
3608        let expected_prefix = format!("{:04}{:02}{:02}0000", next_year, next_month, next_day);
3609        let actual_len = usage_date_str.len().min(12);
3610        let actual_prefix = &usage_date_str[..actual_len];
3611        ConditionResult::from(actual_prefix == expected_prefix.as_str())
3612    }
3613
3614    /// [133] Wenn an der übermittelten Marktlokation / Messlokation vorhanden
3615    fn evaluate_133(&self, _ctx: &EvaluationContext) -> ConditionResult {
3616        // TODO: implement
3617        ConditionResult::Unknown
3618    }
3619
3620    /// [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 ...
3621    // 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)
3622    fn evaluate_134(&self, ctx: &EvaluationContext) -> ConditionResult {
3623        let nav = match ctx.navigator() {
3624            Some(n) => n,
3625            None => return ConditionResult::Unknown,
3626        };
3627        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
3628        // Collect (zeitraum_id, dtm_z25_value, dtm_z26_value) for every Z48/Z55 SG6
3629        let mut slices: Vec<(u32, Option<String>, Option<String>)> = Vec::new();
3630        for i in 0..sg6_count {
3631            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
3632            // RFF C506: [0][0]=qualifier, [0][1]=ref-id, [0][2]=Zeitraum-ID
3633            let maybe_zid = rffs.iter().find_map(|s| {
3634                let qual = s
3635                    .elements
3636                    .first()
3637                    .and_then(|e| e.first())
3638                    .map(|v| v.as_str())?;
3639                if qual == "Z48" || qual == "Z55" {
3640                    s.elements
3641                        .first()
3642                        .and_then(|e| e.get(2))
3643                        .and_then(|v| v.parse::<u32>().ok())
3644                } else {
3645                    None
3646                }
3647            });
3648            if let Some(zid) = maybe_zid {
3649                let dtms = nav.find_segments_in_group("DTM", &["SG4", "SG6"], i);
3650                let z25 = dtms.iter().find_map(|s| {
3651                    if s.elements
3652                        .first()
3653                        .and_then(|e| e.first())
3654                        .map(|v| v.as_str())
3655                        == Some("Z25")
3656                    {
3657                        s.elements.first().and_then(|e| e.get(1)).cloned()
3658                    } else {
3659                        None
3660                    }
3661                });
3662                let z26 = dtms.iter().find_map(|s| {
3663                    if s.elements
3664                        .first()
3665                        .and_then(|e| e.first())
3666                        .map(|v| v.as_str())
3667                        == Some("Z26")
3668                    {
3669                        s.elements.first().and_then(|e| e.get(1)).cloned()
3670                    } else {
3671                        None
3672                    }
3673                });
3674                slices.push((zid, z25, z26));
3675            }
3676        }
3677        if slices.is_empty() {
3678            return ConditionResult::Unknown;
3679        }
3680        // For each slice with Zeitraum-ID > 1: its DTM+Z25 must equal DTM+Z26 of the previous slice
3681        for i in 0..slices.len() {
3682            let (zid, ref z25, _) = slices[i];
3683            if zid <= 1 {
3684                continue;
3685            }
3686            let prev_id = zid - 1;
3687            match slices.iter().find(|(id, _, _)| *id == prev_id) {
3688                Some((_, _, prev_z26)) => match (z25, prev_z26) {
3689                    (Some(curr), Some(prev_end)) => {
3690                        if curr != prev_end {
3691                            return ConditionResult::False;
3692                        }
3693                    }
3694                    _ => return ConditionResult::Unknown,
3695                },
3696                None => return ConditionResult::Unknown,
3697            }
3698        }
3699        ConditionResult::True
3700    }
3701
3702    /// [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...
3703    // 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)
3704    fn evaluate_135(&self, ctx: &EvaluationContext) -> ConditionResult {
3705        let nav = match ctx.navigator() {
3706            Some(n) => n,
3707            None => return ConditionResult::Unknown,
3708        };
3709        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
3710        // Collect (zeitraum_id, dtm_z25_value, dtm_z26_value) for every Z47/Z54 SG6
3711        let mut slices: Vec<(u32, Option<String>, Option<String>)> = Vec::new();
3712        for i in 0..sg6_count {
3713            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
3714            // RFF C506: [0][0]=qualifier, [0][1]=ref-id, [0][2]=Zeitraum-ID
3715            let maybe_zid = rffs.iter().find_map(|s| {
3716                let qual = s
3717                    .elements
3718                    .first()
3719                    .and_then(|e| e.first())
3720                    .map(|v| v.as_str())?;
3721                if qual == "Z47" || qual == "Z54" {
3722                    s.elements
3723                        .first()
3724                        .and_then(|e| e.get(2))
3725                        .and_then(|v| v.parse::<u32>().ok())
3726                } else {
3727                    None
3728                }
3729            });
3730            if let Some(zid) = maybe_zid {
3731                let dtms = nav.find_segments_in_group("DTM", &["SG4", "SG6"], i);
3732                let z25 = dtms.iter().find_map(|s| {
3733                    if s.elements
3734                        .first()
3735                        .and_then(|e| e.first())
3736                        .map(|v| v.as_str())
3737                        == Some("Z25")
3738                    {
3739                        s.elements.first().and_then(|e| e.get(1)).cloned()
3740                    } else {
3741                        None
3742                    }
3743                });
3744                let z26 = dtms.iter().find_map(|s| {
3745                    if s.elements
3746                        .first()
3747                        .and_then(|e| e.first())
3748                        .map(|v| v.as_str())
3749                        == Some("Z26")
3750                    {
3751                        s.elements.first().and_then(|e| e.get(1)).cloned()
3752                    } else {
3753                        None
3754                    }
3755                });
3756                slices.push((zid, z25, z26));
3757            }
3758        }
3759        if slices.is_empty() {
3760            return ConditionResult::Unknown;
3761        }
3762        // For each slice with Zeitraum-ID > 1: its DTM+Z25 must equal DTM+Z26 of the previous slice
3763        for i in 0..slices.len() {
3764            let (zid, ref z25, _) = slices[i];
3765            if zid <= 1 {
3766                continue;
3767            }
3768            let prev_id = zid - 1;
3769            match slices.iter().find(|(id, _, _)| *id == prev_id) {
3770                Some((_, _, prev_z26)) => match (z25, prev_z26) {
3771                    (Some(curr), Some(prev_end)) => {
3772                        if curr != prev_end {
3773                            return ConditionResult::False;
3774                        }
3775                    }
3776                    _ => return ConditionResult::Unknown,
3777                },
3778                None => return ConditionResult::Unknown,
3779            }
3780        }
3781        ConditionResult::True
3782    }
3783
3784    /// [136] Wenn SG10 CCI+++E13 (Zählertyp) CAV+EHZ (elektronischer Haushaltszähler) vorhanden
3785    fn evaluate_136(&self, ctx: &EvaluationContext) -> ConditionResult {
3786        // HAND-EDITED: the CCI and the CAV of one SG10.
3787        sg10_has(ctx, false, |segs| {
3788            sg4_instances::has_cci(segs, "", "E13") && sg4_instances::has_cav(segs, "EHZ")
3789        })
3790    }
3791
3792    /// [137] Nicht bei Neuanlage
3793    fn evaluate_137(&self, ctx: &EvaluationContext) -> ConditionResult {
3794        // Check: not a Neuanlage (new registration) process
3795        // STS+7 with transaktionsgrund containing Neuanlage indicators
3796        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
3797        let is_neuanlage = sts_segs.iter().any(|s| {
3798            s.elements
3799                .get(2)
3800                .and_then(|e| e.first())
3801                .is_some_and(|v| v == "E01" || v == "E03")
3802        });
3803        ConditionResult::from(!is_neuanlage)
3804    }
3805
3806    /// [138] Wenn im selben QTY im DE6063 Z35 (Abschlag) vorhanden
3807    fn evaluate_138(&self, ctx: &EvaluationContext) -> ConditionResult {
3808        let qty_segs = ctx.find_segments("QTY");
3809        let found = qty_segs.iter().any(|s| {
3810            s.elements
3811                .first()
3812                .and_then(|e| e.first())
3813                .is_some_and(|v| ["Z35"].contains(&v.as_str()))
3814        });
3815        ConditionResult::from(found)
3816    }
3817
3818    /// [139] Wenn SG10 CAV+IVA (Individuelle Abstimmung) nicht vorhanden
3819    // 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)
3820    fn evaluate_139(&self, ctx: &EvaluationContext) -> ConditionResult {
3821        let nav = match ctx.navigator {
3822            Some(n) => n,
3823            None => return ctx.lacks_qualifier("CAV", 0, "IVA"),
3824        };
3825        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3826        for i in 0..sg8_count {
3827            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3828            for j in 0..sg10_count {
3829                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3830                if cavs.iter().any(|s| {
3831                    s.elements
3832                        .first()
3833                        .and_then(|e: &Vec<String>| e.first())
3834                        .is_some_and(|v: &String| v == "IVA")
3835                }) {
3836                    return ConditionResult::False;
3837                }
3838            }
3839        }
3840        ConditionResult::True
3841    }
3842
3843    /// [140] Wenn im selben QTY im DE6063 Z34/Z35 (Zuschlag/Abschlag) vorhanden
3844    fn evaluate_140(&self, ctx: &EvaluationContext) -> ConditionResult {
3845        let qty_segs = ctx.find_segments("QTY");
3846        let found = qty_segs.iter().any(|s| {
3847            s.elements
3848                .first()
3849                .and_then(|e| e.first())
3850                .is_some_and(|v| ["Z34", "Z35"].contains(&v.as_str()))
3851        });
3852        ConditionResult::from(found)
3853    }
3854
3855    /// [141] Wenn SG10 CAV+MIW/ MPW/ MBW vorhanden
3856    fn evaluate_141(&self, ctx: &EvaluationContext) -> ConditionResult {
3857        let cav_segs = ctx.find_segments("CAV");
3858        let found = cav_segs.iter().any(|s| {
3859            s.elements
3860                .first()
3861                .and_then(|e| e.first())
3862                .is_some_and(|v| ["MIW", "MPW", "MBW"].contains(&v.as_str()))
3863        });
3864        ConditionResult::from(found)
3865    }
3866
3867    /// [142] Wenn SG8 PIA+Z01+:Z04/ Z05 (Berechnung Tagesmitteltemperatur: vom Anbieter zur Verfügung gestellte - / Äquivalente Tagesmitteltemperatur) vorhanden
3868    fn evaluate_142(&self, ctx: &EvaluationContext) -> ConditionResult {
3869        let pia_segs = ctx.find_segments("PIA");
3870        let found = pia_segs.iter().any(|s| {
3871            s.elements
3872                .first()
3873                .and_then(|e| e.first())
3874                .is_some_and(|v| v == "Z01")
3875                && s.elements
3876                    .get(1)
3877                    .and_then(|e| e.get(1))
3878                    .is_some_and(|v| ["Z04", "Z05"].contains(&v.as_str()))
3879        });
3880        ConditionResult::from(found)
3881    }
3882
3883    /// [143] Wenn STS+7++ZX3 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3884    fn evaluate_143(&self, ctx: &EvaluationContext) -> ConditionResult {
3885        let sts_segs = ctx.find_segments("STS");
3886        let found = sts_segs.iter().any(|s| {
3887            s.elements
3888                .first()
3889                .and_then(|e| e.first())
3890                .is_some_and(|v| v == "7")
3891                && s.elements
3892                    .get(2)
3893                    .and_then(|e| e.first())
3894                    .is_some_and(|v| v == "ZX3")
3895        });
3896        ConditionResult::from(found)
3897    }
3898
3899    /// [144] Wenn STS+7++ZAN (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3900    fn evaluate_144(&self, ctx: &EvaluationContext) -> ConditionResult {
3901        let sts_segs = ctx.find_segments("STS");
3902        let found = sts_segs.iter().any(|s| {
3903            s.elements
3904                .first()
3905                .and_then(|e| e.first())
3906                .is_some_and(|v| v == "7")
3907                && s.elements
3908                    .get(2)
3909                    .and_then(|e| e.first())
3910                    .is_some_and(|v| v == "ZAN")
3911        });
3912        ConditionResult::from(found)
3913    }
3914
3915    /// [145] Wenn STS+7++ZX2 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3916    fn evaluate_145(&self, ctx: &EvaluationContext) -> ConditionResult {
3917        let sts_segs = ctx.find_segments("STS");
3918        let found = sts_segs.iter().any(|s| {
3919            s.elements
3920                .first()
3921                .and_then(|e| e.first())
3922                .is_some_and(|v| v == "7")
3923                && s.elements
3924                    .get(2)
3925                    .and_then(|e| e.first())
3926                    .is_some_and(|v| v == "ZX2")
3927        });
3928        ConditionResult::from(found)
3929    }
3930
3931    /// [146] Wenn STS+7++ZA0 (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3932    fn evaluate_146(&self, ctx: &EvaluationContext) -> ConditionResult {
3933        let sts_segs = ctx.find_segments("STS");
3934        let found = sts_segs.iter().any(|s| {
3935            s.elements
3936                .first()
3937                .and_then(|e| e.first())
3938                .is_some_and(|v| v == "7")
3939                && s.elements
3940                    .get(2)
3941                    .and_then(|e| e.first())
3942                    .is_some_and(|v| v == "ZA0")
3943        });
3944        ConditionResult::from(found)
3945    }
3946
3947    /// [147] Wenn in Anfrage vorhanden
3948    fn evaluate_147(&self, _ctx: &EvaluationContext) -> ConditionResult {
3949        // TODO: implement
3950        ConditionResult::Unknown
3951    }
3952
3953    /// [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
3954    fn evaluate_148(&self, ctx: &EvaluationContext) -> ConditionResult {
3955        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3956        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
3957        let found = pia_segs.iter().any(|s| {
3958            s.elements
3959                .first()
3960                .and_then(|e| e.first())
3961                .is_some_and(|v| v == "Z02")
3962                && s.elements
3963                    .get(1)
3964                    .and_then(|e| e.first())
3965                    .is_some_and(|v| ["1-02-0-017", "1-02-0-018"].contains(&v.as_str()))
3966        });
3967        ConditionResult::from(found)
3968    }
3969
3970    /// [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...
3971    // 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)
3972    fn evaluate_149(&self, ctx: &EvaluationContext) -> ConditionResult {
3973        use std::collections::{HashMap, HashSet};
3974        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
3975        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
3976        let pia_quals = ctx.collect_group_values("PIA", 0, 0, &["SG4", "SG8"]);
3977        let pia_prods = ctx.collect_group_values("PIA", 1, 0, &["SG4", "SG8"]);
3978        // Map group index -> Zeitraum-ID for SG8s with SEQ+Z45
3979        let mut idx_to_zeitraum: HashMap<usize, String> = HashMap::new();
3980        for ((gi, q), (_, zid)) in seq_quals.iter().zip(seq_zeitraum.iter()) {
3981            if q == "Z45" && !zid.is_empty() {
3982                idx_to_zeitraum.insert(*gi, zid.clone());
3983            }
3984        }
3985        if idx_to_zeitraum.is_empty() {
3986            return ConditionResult::False;
3987        }
3988        // Map Zeitraum-ID -> set of PIA+Z02 product codes found in the same SG8 group
3989        let mut zeitraum_products: HashMap<String, HashSet<String>> = HashMap::new();
3990        for (i, (gi, qual)) in pia_quals.iter().enumerate() {
3991            if qual == "Z02" {
3992                if let Some(zid) = idx_to_zeitraum.get(gi) {
3993                    if let Some((_, prod)) = pia_prods.get(i) {
3994                        if !prod.is_empty() {
3995                            zeitraum_products
3996                                .entry(zid.clone())
3997                                .or_default()
3998                                .insert(prod.clone());
3999                        }
4000                    }
4001                }
4002            }
4003        }
4004        ConditionResult::from(
4005            zeitraum_products
4006                .values()
4007                .any(|codes| codes.contains("1-02-0-017") && codes.contains("1-02-0-018")),
4008        )
4009    }
4010
4011    /// [150] Wenn in dieser SG8 im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), die Artikel-ID 1-02-0-002 vorhanden
4012    fn evaluate_150(&self, ctx: &EvaluationContext) -> ConditionResult {
4013        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4014        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4015        let found = pia_segs.iter().any(|s| {
4016            s.elements
4017                .first()
4018                .and_then(|e| e.first())
4019                .is_some_and(|v| v == "Z02")
4020                && s.elements
4021                    .get(1)
4022                    .and_then(|e| e.first())
4023                    .is_some_and(|v| ["1-02-0-002"].contains(&v.as_str()))
4024        });
4025        ConditionResult::from(found)
4026    }
4027
4028    /// [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...
4029    fn evaluate_151(&self, ctx: &EvaluationContext) -> ConditionResult {
4030        ctx.external.evaluate("code_list_membership_check")
4031    }
4032
4033    /// [152] Wenn FTX+Z28 (IP-Range des Absenders) nicht vorhanden
4034    fn evaluate_152(&self, ctx: &EvaluationContext) -> ConditionResult {
4035        let ftx_segs = ctx.find_segments("FTX");
4036        let found = ftx_segs.iter().any(|s| {
4037            s.elements
4038                .first()
4039                .and_then(|e| e.first())
4040                .is_some_and(|v| v == "Z28")
4041        });
4042        ConditionResult::from(!found)
4043    }
4044
4045    /// [153] Wenn FTX+Z27 (IP-Adresse des Absenders) nicht vorhanden
4046    fn evaluate_153(&self, ctx: &EvaluationContext) -> ConditionResult {
4047        let ftx_segs = ctx.find_segments("FTX");
4048        let found = ftx_segs.iter().any(|s| {
4049            s.elements
4050                .first()
4051                .and_then(|e| e.first())
4052                .is_some_and(|v| v == "Z27")
4053        });
4054        ConditionResult::from(!found)
4055    }
4056
4057    /// [156] Wenn SG4 FTX+Z17 (Zieladresse URI) vorhanden
4058    fn evaluate_156(&self, ctx: &EvaluationContext) -> ConditionResult {
4059        let ftx_segs = ctx.find_segments("FTX");
4060        let found = ftx_segs.iter().any(|s| {
4061            s.elements
4062                .first()
4063                .and_then(|e| e.first())
4064                .is_some_and(|v| v == "Z17")
4065        });
4066        ConditionResult::from(found)
4067    }
4068
4069    /// [161] Wenn SG4 STS+7++E03+ZW8 (Transaktionsgrundergänzung Fall 1) vorhanden
4070    fn evaluate_161(&self, ctx: &EvaluationContext) -> ConditionResult {
4071        let sts_segs = ctx.find_segments("STS");
4072        let found = sts_segs.iter().any(|s| {
4073            s.elements
4074                .first()
4075                .and_then(|e| e.first())
4076                .is_some_and(|v| v == "7")
4077                && s.elements
4078                    .get(2)
4079                    .and_then(|e| e.first())
4080                    .is_some_and(|v| v == "E03")
4081                && s.elements
4082                    .get(3)
4083                    .and_then(|e| e.first())
4084                    .is_some_and(|v| v == "ZW8")
4085        });
4086        ConditionResult::from(found)
4087    }
4088
4089    /// [162] Wenn SG4 STS+7++E03+ZW9 (Transaktionsgrundergänzung Fall 2) vorhanden
4090    fn evaluate_162(&self, ctx: &EvaluationContext) -> ConditionResult {
4091        let sts_segs = ctx.find_segments("STS");
4092        let found = sts_segs.iter().any(|s| {
4093            s.elements
4094                .first()
4095                .and_then(|e| e.first())
4096                .is_some_and(|v| v == "7")
4097                && s.elements
4098                    .get(2)
4099                    .and_then(|e| e.first())
4100                    .is_some_and(|v| v == "E03")
4101                && s.elements
4102                    .get(3)
4103                    .and_then(|e| e.first())
4104                    .is_some_and(|v| v == "ZW9")
4105        });
4106        ConditionResult::from(found)
4107    }
4108
4109    /// [163] Wenn SG4 STS+7++E03+ZX0 (Transaktionsgrundergänzung Fall 3) vorhanden
4110    fn evaluate_163(&self, ctx: &EvaluationContext) -> ConditionResult {
4111        let sts_segs = ctx.find_segments("STS");
4112        let found = sts_segs.iter().any(|s| {
4113            s.elements
4114                .first()
4115                .and_then(|e| e.first())
4116                .is_some_and(|v| v == "7")
4117                && s.elements
4118                    .get(2)
4119                    .and_then(|e| e.first())
4120                    .is_some_and(|v| v == "E03")
4121                && s.elements
4122                    .get(3)
4123                    .and_then(|e| e.first())
4124                    .is_some_and(|v| v == "ZX0")
4125        });
4126        ConditionResult::from(found)
4127    }
4128
4129    /// [164] Wenn SG4 STS+7++E03+ZX1 (Transaktionsgrundergänzung Fall 4) vorhanden
4130    fn evaluate_164(&self, ctx: &EvaluationContext) -> ConditionResult {
4131        let sts_segs = ctx.find_segments("STS");
4132        let found = sts_segs.iter().any(|s| {
4133            s.elements
4134                .first()
4135                .and_then(|e| e.first())
4136                .is_some_and(|v| v == "7")
4137                && s.elements
4138                    .get(2)
4139                    .and_then(|e| e.first())
4140                    .is_some_and(|v| v == "E03")
4141                && s.elements
4142                    .get(3)
4143                    .and_then(|e| e.first())
4144                    .is_some_and(|v| v == "ZX1")
4145        });
4146        ConditionResult::from(found)
4147    }
4148
4149    /// [165] Wenn bekannt
4150    fn evaluate_165(&self, _ctx: &EvaluationContext) -> ConditionResult {
4151        // TODO: implement
4152        ConditionResult::Unknown
4153    }
4154
4155    /// [166] Wenn vorhanden
4156    fn evaluate_166(&self, _ctx: &EvaluationContext) -> ConditionResult {
4157        // "If present" — always true when the field is being validated
4158        ConditionResult::True
4159    }
4160
4161    /// [167] Wenn SG5 LOC+Z21 (Tranche) vorhanden
4162    fn evaluate_167(&self, ctx: &EvaluationContext) -> ConditionResult {
4163        let loc_segs = ctx.find_segments("LOC");
4164        let found = loc_segs.iter().any(|s| {
4165            s.elements
4166                .first()
4167                .and_then(|e| e.first())
4168                .is_some_and(|v| v == "Z21")
4169        });
4170        ConditionResult::from(found)
4171    }
4172
4173    /// [170] Wenn Anschrift der Marktlokation vorhanden
4174    fn evaluate_170(&self, _ctx: &EvaluationContext) -> ConditionResult {
4175        // TODO: implement
4176        ConditionResult::Unknown
4177    }
4178
4179    /// [172] Wenn im selben QTY im DE6063 Z37 (Kein Zu- und Abschlag) vorhanden
4180    fn evaluate_172(&self, ctx: &EvaluationContext) -> ConditionResult {
4181        let qty_segs = ctx.find_segments("QTY");
4182        let found = qty_segs.iter().any(|s| {
4183            s.elements
4184                .first()
4185                .and_then(|e| e.first())
4186                .is_some_and(|v| ["Z37"].contains(&v.as_str()))
4187        });
4188        ConditionResult::from(found)
4189    }
4190
4191    /// [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 (...
4192    // 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)
4193    fn evaluate_173(&self, ctx: &EvaluationContext) -> ConditionResult {
4194        // HAND-EDITED: "in derselben SG8" — this SG8, a SEQ+Z58/ZC9/ZD0/ZD6, has an
4195        // RFF+Z33 without DE1156.
4196        let kinds = ctx.scoped_find_segments_in("SG8", "SEQ");
4197        if !kinds
4198            .iter()
4199            .any(|s| matches!(s.get_element(0), "Z58" | "ZC9" | "ZD0" | "ZD6"))
4200        {
4201            return ConditionResult::Unknown;
4202        }
4203        let z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
4204        if z33.is_empty() {
4205            return ConditionResult::Unknown;
4206        }
4207        ConditionResult::from(z33.iter().any(|s| s.get_component(0, 2).is_empty()))
4208    }
4209
4210    /// [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...
4211    fn evaluate_174(&self, ctx: &EvaluationContext) -> ConditionResult {
4212        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4213        // Check: RFF+Z33 has a value in DE1156 (fortlaufende Nummer)
4214        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
4215        let found = rff_z33.iter().any(|s| {
4216            s.elements
4217                .first()
4218                .and_then(|e| e.get(2))
4219                .is_some_and(|v| !v.is_empty())
4220        });
4221        ConditionResult::from(found)
4222    }
4223
4224    /// [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...
4225    // 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)
4226    fn evaluate_175(&self, ctx: &EvaluationContext) -> ConditionResult {
4227        // HAND-EDITED: "in einem weiteren SG8 SEQ+Z58 mit derselben fortlaufenden Nummer" — compared within the enclosing SG4 when the scope
4228        // reaches an SG8; the message as before otherwise.
4229        if let Some(scope) = ctx.enclosing_scope("SG8") {
4230            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
4231                ctx,
4232                scope,
4233                &["Z58"],
4234                true,
4235                false,
4236            ));
4237        }
4238        let nav = match ctx.navigator {
4239            Some(n) => n,
4240            None => return ConditionResult::Unknown,
4241        };
4242        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4243        let mut entries: Vec<(usize, String, String)> = Vec::new();
4244        for i in 0..sg8_count {
4245            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4246            if !seqs.iter().any(|s| {
4247                s.elements
4248                    .first()
4249                    .and_then(|e: &Vec<String>| e.first())
4250                    .is_some_and(|v: &String| v == "Z58")
4251            }) {
4252                continue;
4253            }
4254            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4255            for rff in &rffs {
4256                let elems = match rff.elements.first() {
4257                    Some(e) => e,
4258                    None => continue,
4259                };
4260                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4261                    continue;
4262                }
4263                let objektcode = elems.get(1).cloned().unwrap_or_default();
4264                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4265                if !objektcode.is_empty() {
4266                    entries.push((i, objektcode, fort_nr));
4267                }
4268            }
4269        }
4270        for a in 0..entries.len() {
4271            for b in (a + 1)..entries.len() {
4272                if entries[a].0 != entries[b].0
4273                    && entries[a].1 == entries[b].1
4274                    && entries[a].2 == entries[b].2
4275                {
4276                    return ConditionResult::True;
4277                }
4278            }
4279        }
4280        ConditionResult::False
4281    }
4282
4283    /// [176] Wenn in demselben Segment das DE1153 mit dem Code Z34 vorgelagerte Messlokation vorhanden ist
4284    fn evaluate_176(&self, ctx: &EvaluationContext) -> ConditionResult {
4285        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z34"))
4286    }
4287
4288    /// [177] Wenn in demselben Segment das DE1153 mit dem Code Z35 vorgelagerte Netzlokation vorhanden ist
4289    fn evaluate_177(&self, ctx: &EvaluationContext) -> ConditionResult {
4290        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z35"))
4291    }
4292
4293    /// [178] Wenn das LOC+Z18 (Netzlokation) vorhanden ist
4294    fn evaluate_178(&self, ctx: &EvaluationContext) -> ConditionResult {
4295        let loc_segs = ctx.find_segments("LOC");
4296        let found = loc_segs.iter().any(|s| {
4297            s.elements
4298                .first()
4299                .and_then(|e| e.first())
4300                .is_some_and(|v| v == "Z18")
4301        });
4302        ConditionResult::from(found)
4303    }
4304
4305    /// [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...
4306    fn evaluate_179(&self, ctx: &EvaluationContext) -> ConditionResult {
4307        // HAND-EDITED: this SG8's Objektcode (RFF+Z33 DE1154) must be in a
4308        // sibling SG8 with the same SEQ code (Z58/ZC9/ZD0/ZD6). Unknown without
4309        // a group scope: "derselben SG8" cannot be told apart then.
4310        const KINDS: [&str; 4] = ["Z58", "ZC9", "ZD0", "ZD6"];
4311        let Some(siblings) = ctx.sibling_scopes("SG8") else {
4312            return ConditionResult::Unknown;
4313        };
4314        let seq_of = |segs: &[OwnedSegment]| {
4315            segs.iter()
4316                .find(|s| s.id == "SEQ")
4317                .map(|s| s.get_element(0).to_string())
4318        };
4319        let objektcodes = |segs: &[OwnedSegment]| -> Vec<String> {
4320            segs.iter()
4321                .filter(|s| s.id == "RFF" && s.get_element(0) == "Z33")
4322                .map(|s| s.get_component(0, 1).to_string())
4323                .filter(|v| !v.is_empty())
4324                .collect()
4325        };
4326        let own_seq = seq_of(&ctx.scoped_find_segments_in("SG8", "SEQ"));
4327        let Some(own_seq) = own_seq.filter(|q| KINDS.contains(&q.as_str())) else {
4328            return ConditionResult::False;
4329        };
4330        let own = objektcodes(&ctx.scoped_find_segments_in("SG8", "RFF"));
4331        if own.is_empty() {
4332            return ConditionResult::False;
4333        }
4334        let found = siblings.iter().any(|path| {
4335            seq_of(&ctx.find_segments_at(path, "SEQ")).as_deref() == Some(own_seq.as_str())
4336                && objektcodes(&ctx.find_segments_at(path, "RFF"))
4337                    .iter()
4338                    .any(|code| own.contains(code))
4339        });
4340        ConditionResult::from(found)
4341    }
4342
4343    /// [180] Wenn im selben QTY im DE6063 Z46 (Kein Abschlag) vorhanden
4344    fn evaluate_180(&self, ctx: &EvaluationContext) -> ConditionResult {
4345        let qty_segs = ctx.find_segments("QTY");
4346        let found = qty_segs.iter().any(|s| {
4347            s.elements
4348                .first()
4349                .and_then(|e| e.first())
4350                .is_some_and(|v| ["Z46"].contains(&v.as_str()))
4351        });
4352        ConditionResult::from(found)
4353    }
4354
4355    /// [184] Wenn in derselben SG8 SEQ+Z18 (Daten der Messlokation) ein CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
4356    fn evaluate_184(&self, ctx: &EvaluationContext) -> ConditionResult {
4357        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8,
4358        // and the CCI+Z01++Z82 the condition names is part of it: a second
4359        // Messlokation (CCI+Z01++Z81) needs no Messstellenbetreiber Alt.
4360        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4361        let seq_z18 = seq_segs.iter().any(|s| {
4362            s.elements
4363                .first()
4364                .and_then(|e| e.first())
4365                .is_some_and(|v| v == "Z18")
4366        });
4367        let cci_z82 = ctx.scoped_find_segments_in("SG8", "CCI").iter().any(|s| {
4368            s.elements
4369                .first()
4370                .and_then(|e| e.first())
4371                .is_some_and(|v| v == "Z01")
4372                && s.elements
4373                    .get(2)
4374                    .and_then(|e| e.first())
4375                    .is_some_and(|v| v == "Z82")
4376        });
4377        ConditionResult::from(seq_z18 && cci_z82)
4378    }
4379
4380    /// [190] Wenn in derselben SG8 SEQ+Z60/ ZG8/ ZG9/ ZE0  (Produkt-Daten der Netzlokation) PIA+5+9991000000721:Z12 vorhanden
4381    fn evaluate_190(&self, ctx: &EvaluationContext) -> ConditionResult {
4382        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4383        // Check: SG8 with SEQ+Z60/ZG8/ZG9/ZE0 has PIA+5 with code 9991000000721
4384        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4385        let found = pia_segs.iter().any(|s| {
4386            s.elements
4387                .first()
4388                .and_then(|e| e.first())
4389                .is_some_and(|v| v == "5")
4390                && s.elements
4391                    .get(1)
4392                    .and_then(|e| e.first())
4393                    .is_some_and(|v| v == "9991000000721")
4394        });
4395        ConditionResult::from(found)
4396    }
4397
4398    /// [191] Wenn im SG4 IDE+Z01 (Identifikation einer Liste) STS+E01 (Status der Antwort der Liste) nicht vorhanden
4399    fn evaluate_191(&self, ctx: &EvaluationContext) -> ConditionResult {
4400        let sts_segs = ctx.find_segments("STS");
4401        let found = sts_segs.iter().any(|s| {
4402            s.elements
4403                .first()
4404                .and_then(|e| e.first())
4405                .is_some_and(|v| v == "E01")
4406        });
4407        ConditionResult::from(!found)
4408    }
4409
4410    /// [192] Wenn SG4 STS+7++XXX+ZW4/ZAP (Transaktionsgrund / Ergänzung: Verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
4411    fn evaluate_192(&self, ctx: &EvaluationContext) -> ConditionResult {
4412        let sts_segs = ctx.find_segments("STS");
4413        let found = sts_segs.iter().any(|s| {
4414            s.elements
4415                .first()
4416                .and_then(|e| e.first())
4417                .is_some_and(|v| v == "7")
4418                && s.elements
4419                    .get(3)
4420                    .and_then(|e| e.first())
4421                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
4422        });
4423        ConditionResult::from(found)
4424    }
4425
4426    /// [193] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E06 (Niederspannung) in dieser SG vorhanden
4427    fn evaluate_193(&self, ctx: &EvaluationContext) -> ConditionResult {
4428        let nav = match ctx.navigator {
4429            Some(n) => n,
4430            None => {
4431                let cavs = ctx.find_segments("CAV");
4432                return ConditionResult::from(cavs.iter().any(|s| {
4433                    s.elements
4434                        .first()
4435                        .and_then(|e: &Vec<String>| e.first())
4436                        .is_some_and(|v: &String| v == "E06")
4437                }));
4438            }
4439        };
4440        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4441        for i in 0..sg8_count {
4442            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4443            for j in 0..sg10_count {
4444                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4445                let has_cci_zb5 = ccis.iter().any(|s| {
4446                    s.elements
4447                        .get(2)
4448                        .and_then(|e: &Vec<String>| e.first())
4449                        .is_some_and(|v: &String| v == "ZB5")
4450                });
4451                if !has_cci_zb5 {
4452                    continue;
4453                }
4454                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4455                if cavs.iter().any(|s| {
4456                    s.elements
4457                        .first()
4458                        .and_then(|e: &Vec<String>| e.first())
4459                        .is_some_and(|v: &String| v == "E06")
4460                }) {
4461                    return ConditionResult::True;
4462                }
4463            }
4464        }
4465        ConditionResult::False
4466    }
4467
4468    /// [194] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E05 (Mittelspannung) in dieser SG vorhanden
4469    fn evaluate_194(&self, ctx: &EvaluationContext) -> ConditionResult {
4470        let cci_segs = ctx.find_segments("CCI");
4471        let has_cci = cci_segs.iter().any(|s| {
4472            s.elements
4473                .iter()
4474                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4475        });
4476        let cav_segs = ctx.find_segments("CAV");
4477        let has_cav = cav_segs.iter().any(|s| {
4478            s.elements
4479                .first()
4480                .and_then(|e| e.first())
4481                .is_some_and(|v| v == "E05")
4482        });
4483        ConditionResult::from(has_cci && has_cav)
4484    }
4485
4486    /// [195] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E04 (Hochspannung) in dieser SG vorhanden
4487    fn evaluate_195(&self, ctx: &EvaluationContext) -> ConditionResult {
4488        let cci_segs = ctx.find_segments("CCI");
4489        let has_cci = cci_segs.iter().any(|s| {
4490            s.elements
4491                .iter()
4492                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4493        });
4494        let cav_segs = ctx.find_segments("CAV");
4495        let has_cav = cav_segs.iter().any(|s| {
4496            s.elements
4497                .first()
4498                .and_then(|e| e.first())
4499                .is_some_and(|v| v == "E04")
4500        });
4501        ConditionResult::from(has_cci && has_cav)
4502    }
4503
4504    /// [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...
4505    // 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)
4506    fn evaluate_196(&self, ctx: &EvaluationContext) -> ConditionResult {
4507        // HAND-EDITED: "in einem weiteren SG8 … Nummer, Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
4508        // reaches an SG8; the message as before otherwise.
4509        if let Some(scope) = ctx.enclosing_scope("SG8") {
4510            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
4511                ctx,
4512                scope,
4513                &["Z58", "ZC9", "ZD0"],
4514                true,
4515                true,
4516            ));
4517        }
4518        let nav = match ctx.navigator {
4519            Some(n) => n,
4520            None => return ConditionResult::Unknown,
4521        };
4522        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4523        let mut entries: Vec<(usize, String, String, String, String)> = Vec::new();
4524        for i in 0..sg8_count {
4525            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4526            let seq_opt = seqs.iter().find(|s| {
4527                s.elements
4528                    .first()
4529                    .and_then(|e: &Vec<String>| e.first())
4530                    .is_some_and(|v: &String| matches!(v.as_str(), "Z58" | "ZC9" | "ZD0"))
4531            });
4532            let seq = match seq_opt {
4533                Some(s) => s,
4534                None => continue,
4535            };
4536            let seq_code = seq
4537                .elements
4538                .first()
4539                .and_then(|e: &Vec<String>| e.first())
4540                .cloned()
4541                .unwrap_or_default();
4542            let zeitraum_id = seq
4543                .elements
4544                .get(1)
4545                .and_then(|e: &Vec<String>| e.first())
4546                .cloned()
4547                .unwrap_or_default();
4548            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4549            for rff in &rffs {
4550                let elems = match rff.elements.first() {
4551                    Some(e) => e,
4552                    None => continue,
4553                };
4554                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4555                    continue;
4556                }
4557                let objektcode = elems.get(1).cloned().unwrap_or_default();
4558                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4559                if !objektcode.is_empty() {
4560                    entries.push((
4561                        i,
4562                        seq_code.clone(),
4563                        zeitraum_id.clone(),
4564                        objektcode,
4565                        fort_nr,
4566                    ));
4567                }
4568            }
4569        }
4570        for a in 0..entries.len() {
4571            for b in (a + 1)..entries.len() {
4572                if entries[a].0 != entries[b].0
4573                    && entries[a].1 == entries[b].1
4574                    && entries[a].2 == entries[b].2
4575                    && entries[a].3 == entries[b].3
4576                    && entries[a].4 == entries[b].4
4577                {
4578                    return ConditionResult::True;
4579                }
4580            }
4581        }
4582        ConditionResult::False
4583    }
4584
4585    /// [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 (...
4586    // 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)
4587    fn evaluate_197(&self, ctx: &EvaluationContext) -> ConditionResult {
4588        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (SLP/SEP) — compared within the enclosing SG4 when the scope
4589        // reaches an SG8; the message as before otherwise.
4590        if let Some(scope) = ctx.enclosing_scope("SG8") {
4591            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
4592            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
4593                sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "E02")
4594            }));
4595        }
4596        let nav = match ctx.navigator {
4597            Some(n) => n,
4598            None => return ConditionResult::Unknown,
4599        };
4600        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4601        let mut qualifying_malo_ids: Vec<String> = Vec::new();
4602        for i in 0..sg8_count {
4603            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4604            if !seqs.iter().any(|s| {
4605                s.elements
4606                    .first()
4607                    .and_then(|e: &Vec<String>| e.first())
4608                    .is_some_and(|v: &String| v == "Z01")
4609            }) {
4610                continue;
4611            }
4612            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4613            let has_prog = (0..sg10_count).any(|j| {
4614                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4615                let has_za6 = ccis.iter().any(|s| {
4616                    s.elements
4617                        .get(2)
4618                        .and_then(|e: &Vec<String>| e.first())
4619                        .is_some_and(|v: &String| v == "ZA6")
4620                });
4621                if !has_za6 {
4622                    return false;
4623                }
4624                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4625                cavs.iter().any(|s| {
4626                    s.elements
4627                        .first()
4628                        .and_then(|e: &Vec<String>| e.first())
4629                        .is_some_and(|v: &String| v == "E02")
4630                })
4631            });
4632            if !has_prog {
4633                continue;
4634            }
4635            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4636            for rff in &rffs {
4637                let elems = match rff.elements.first() {
4638                    Some(e) => e,
4639                    None => continue,
4640                };
4641                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4642                    continue;
4643                }
4644                if let Some(malo_id) = elems.get(1) {
4645                    if !malo_id.is_empty() {
4646                        qualifying_malo_ids.push(malo_id.clone());
4647                    }
4648                }
4649            }
4650        }
4651        if qualifying_malo_ids.is_empty() {
4652            return ConditionResult::False;
4653        }
4654        for i in 0..sg8_count {
4655            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4656            for rff in &rffs {
4657                let elems = match rff.elements.first() {
4658                    Some(e) => e,
4659                    None => continue,
4660                };
4661                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4662                    continue;
4663                }
4664                if let Some(malo_id) = elems.get(1) {
4665                    if qualifying_malo_ids.contains(malo_id) {
4666                        return ConditionResult::True;
4667                    }
4668                }
4669            }
4670        }
4671        ConditionResult::False
4672    }
4673
4674    /// [198] Wenn SG4 STS+7++E03(Transaktionsgrund: Wechsel) vorhanden
4675    fn evaluate_198(&self, ctx: &EvaluationContext) -> ConditionResult {
4676        let sts_segs = ctx.find_segments("STS");
4677        let found = sts_segs.iter().any(|s| {
4678            s.elements
4679                .first()
4680                .and_then(|e| e.first())
4681                .is_some_and(|v| v == "7")
4682                && s.elements
4683                    .get(2)
4684                    .and_then(|e| e.first())
4685                    .is_some_and(|v| ["E03"].contains(&v.as_str()))
4686        });
4687        ConditionResult::from(found)
4688    }
4689
4690    /// [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
4691    // 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)
4692    fn evaluate_199(&self, ctx: &EvaluationContext) -> ConditionResult {
4693        let nav = match ctx.navigator {
4694            Some(n) => n,
4695            None => {
4696                return ctx.filtered_parent_child_has_qualifier(
4697                    &["SG4", "SG8"],
4698                    "SEQ",
4699                    0,
4700                    "Z01",
4701                    "SG10",
4702                    "CCI",
4703                    2,
4704                    "ZA6",
4705                )
4706            }
4707        };
4708        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4709        for i in 0..sg8_count {
4710            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4711            if !seqs.iter().any(|s| {
4712                s.elements
4713                    .first()
4714                    .and_then(|e: &Vec<String>| e.first())
4715                    .is_some_and(|v: &String| v == "Z01")
4716            }) {
4717                continue;
4718            }
4719            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4720            for j in 0..sg10_count {
4721                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4722                if !ccis.iter().any(|s| {
4723                    s.elements
4724                        .get(2)
4725                        .and_then(|e: &Vec<String>| e.first())
4726                        .is_some_and(|v: &String| v == "ZA6")
4727                }) {
4728                    continue;
4729                }
4730                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4731                if cavs.iter().any(|s| {
4732                    s.elements
4733                        .first()
4734                        .and_then(|e: &Vec<String>| e.first())
4735                        .is_some_and(|v: &String| v == "E02")
4736                }) {
4737                    return ConditionResult::True;
4738                }
4739            }
4740        }
4741        ConditionResult::False
4742    }
4743
4744    /// [201] Wenn die Marktlokation / Tranche nicht den ganzen Bilanzierungsmonat dem gleichen Tupel aus Bilanzkreis, Zeitreihentyp, Spannungsebene und Lieferant zugeordnet ist
4745    fn evaluate_201(&self, _ctx: &EvaluationContext) -> ConditionResult {
4746        // TODO: implement
4747        ConditionResult::Unknown
4748    }
4749
4750    /// [202] Wenn SG8 SEQ+Z78 RFF+Z39 (Keine standardisierte Lokationsbündelstruktur vorhanden), mit identischer Zeitraum-ID im DE1050 wie im DE3224 dieses Segments
4751    // 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)
4752    fn evaluate_202(&self, ctx: &EvaluationContext) -> ConditionResult {
4753        use std::collections::HashSet;
4754        // Collect Zeitraum-IDs from all SG5 LOC segments (DE3224 at elements[1][3])
4755        let loc_zeitraum_ids: HashSet<String> = ctx
4756            .find_segments("LOC")
4757            .into_iter()
4758            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
4759            .filter(|v| !v.is_empty())
4760            .collect();
4761        if loc_zeitraum_ids.is_empty() {
4762            return ConditionResult::False;
4763        }
4764        // Check if any SEQ+Z78 (SG8 Lokationsbündelstruktur) has DE1050 matching a LOC DE3224
4765        let matching_seq = ctx
4766            .find_segments_with_qualifier("SEQ", 0, "Z78")
4767            .into_iter()
4768            .any(|s| {
4769                s.elements
4770                    .get(1)
4771                    .and_then(|e: &Vec<String>| e.first())
4772                    .is_some_and(|v: &String| {
4773                        !v.is_empty() && loc_zeitraum_ids.contains(v.as_str())
4774                    })
4775            });
4776        if !matching_seq {
4777            return ConditionResult::False;
4778        }
4779        // Verify RFF+Z39 also exists (co-occurrence in same SG8 not verified without navigator)
4780        ctx.has_qualifier("RFF", 0, "Z39")
4781    }
4782
4783    /// [203] Wenn STS+7++E06 / Z39 / ZC6 / ZC7/ ZT6/ ZT7 vorhanden
4784    fn evaluate_203(&self, ctx: &EvaluationContext) -> ConditionResult {
4785        let sts_segs = ctx.find_segments("STS");
4786        let found = sts_segs.iter().any(|s| {
4787            s.elements
4788                .first()
4789                .and_then(|e| e.first())
4790                .is_some_and(|v| v == "7")
4791                && s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
4792                    ["E06", "Z39", "ZC6", "ZC7", "ZT6", "ZT7"].contains(&v.as_str())
4793                })
4794        });
4795        ConditionResult::from(found)
4796    }
4797
4798    /// [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...
4799    // 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)
4800    fn evaluate_204(&self, ctx: &EvaluationContext) -> ConditionResult {
4801        // True if any SG8 with Zähleinrichtungsdaten (SEQ+Z03/ZA3/ZA4) lacks RFF+Z14 (Smartmeter-Gateway)
4802        for qual in &["Z03", "ZA3", "ZA4"] {
4803            if matches!(
4804                ctx.any_group_has_qualifier_without(
4805                    "SEQ",
4806                    0,
4807                    qual,
4808                    "RFF",
4809                    0,
4810                    "Z14",
4811                    &["SG4", "SG8"]
4812                ),
4813                ConditionResult::True
4814            ) {
4815                return ConditionResult::True;
4816            }
4817        }
4818        ConditionResult::False
4819    }
4820
4821    /// [205] Wenn SG5 LOC+Z19 (Steuerbare Ressource) vorhanden
4822    fn evaluate_205(&self, ctx: &EvaluationContext) -> ConditionResult {
4823        let loc_segs = ctx.find_segments("LOC");
4824        let found = loc_segs.iter().any(|s| {
4825            s.elements
4826                .first()
4827                .and_then(|e| e.first())
4828                .is_some_and(|v| v == "Z19")
4829        });
4830        ConditionResult::from(found)
4831    }
4832
4833    /// [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
4834    fn evaluate_206(&self, ctx: &EvaluationContext) -> ConditionResult {
4835        let sts_segs = ctx.find_segments("STS");
4836        let found = sts_segs.iter().any(|s| {
4837            s.elements
4838                .first()
4839                .and_then(|e| e.first())
4840                .is_some_and(|v| v == "7")
4841                && s.elements
4842                    .get(2)
4843                    .and_then(|e| e.first())
4844                    .is_some_and(|v| ["ZG5"].contains(&v.as_str()))
4845        });
4846        ConditionResult::from(!found)
4847    }
4848
4849    /// [209] Wenn im selben Segment im DE2379 der Code 303 vorhanden ist
4850    fn evaluate_209(&self, ctx: &EvaluationContext) -> ConditionResult {
4851        ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "303"))
4852    }
4853
4854    /// [210] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
4855    fn evaluate_210(&self, ctx: &EvaluationContext) -> ConditionResult {
4856        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4857        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4858        let found = cci_segs.iter().any(|s| {
4859            s.elements
4860                .get(2)
4861                .and_then(|e| e.first())
4862                .is_some_and(|v| v == "ZA6")
4863        });
4864        ConditionResult::from(found)
4865    }
4866
4867    /// [212] Wenn im selben SG12 NAD DE3124 nicht vorhanden
4868    fn evaluate_212(&self, ctx: &EvaluationContext) -> ConditionResult {
4869        let nad_segs = ctx.find_segments("NAD");
4870        let found = nad_segs.iter().any(|s| {
4871            s.elements
4872                .get(3)
4873                .and_then(|e| e.first())
4874                .is_some_and(|v| !v.is_empty())
4875        });
4876        ConditionResult::from(!found)
4877    }
4878
4879    /// [213] Wenn SG12 NAD+Z09 (Kunde des Lieferanten) vorhanden
4880    fn evaluate_213(&self, ctx: &EvaluationContext) -> ConditionResult {
4881        let nad_segs = ctx.find_segments("NAD");
4882        let found = nad_segs.iter().any(|s| {
4883            s.elements
4884                .first()
4885                .and_then(|e| e.first())
4886                .is_some_and(|v| v == "Z09")
4887        });
4888        ConditionResult::from(found)
4889    }
4890
4891    /// [215] Wenn in derselben SG8 (Zähleinrichtungsdaten) SG10 CCI+++E13 CAV+MME (Zählertyp: mME) vorhanden
4892    fn evaluate_215(&self, ctx: &EvaluationContext) -> ConditionResult {
4893        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8 (its
4894        // SG10s included). Both segments are looked for anywhere in that SG8,
4895        // not within one SG10.
4896        let has_cci_e13 = ctx.scoped_find_segments_in("SG8", "CCI").iter().any(|s| {
4897            s.elements
4898                .get(2)
4899                .and_then(|e| e.first())
4900                .is_some_and(|v| v == "E13")
4901        });
4902        let has_cav_mme = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
4903            s.elements
4904                .first()
4905                .and_then(|e| e.first())
4906                .is_some_and(|v| v == "MME")
4907        });
4908        ConditionResult::from(has_cci_e13 && has_cav_mme)
4909    }
4910
4911    /// [216] Wenn CCI+++Z88 (Netznutzung) CAV+Z74:::Z08 (Netznutzungsvertrag: Direkter Vertrag zwischen Kunden und NB) vorhanden
4912    fn evaluate_216(&self, ctx: &EvaluationContext) -> ConditionResult {
4913        // HAND-EDITED: the CCI and the CAV of one SG10; Z08 is DE7110 of the CAV.
4914        sg10_has(ctx, false, |segs| {
4915            sg4_instances::has_cci(segs, "", "Z88")
4916                && segs.iter().any(|s| {
4917                    s.id == "CAV" && s.get_element(0) == "Z74" && s.get_component(0, 3) == "Z08"
4918                })
4919        })
4920    }
4921
4922    /// [217] Wenn SG6 RFF+Z49 (Verwendungszeitraum der Daten: \"Gültige Daten\") vorhanden
4923    fn evaluate_217(&self, ctx: &EvaluationContext) -> ConditionResult {
4924        let rff_segs = ctx.find_segments("RFF");
4925        let found = rff_segs.iter().any(|s| {
4926            s.elements
4927                .first()
4928                .and_then(|e| e.first())
4929                .is_some_and(|v| v == "Z49")
4930        });
4931        ConditionResult::from(found)
4932    }
4933
4934    /// [219] Wenn SG12 NAD+Z65 (Informativer Kunde des Lieferanten) vorhanden
4935    fn evaluate_219(&self, ctx: &EvaluationContext) -> ConditionResult {
4936        let nad_segs = ctx.find_segments("NAD");
4937        let found = nad_segs.iter().any(|s| {
4938            s.elements
4939                .first()
4940                .and_then(|e| e.first())
4941                .is_some_and(|v| v == "Z65")
4942        });
4943        ConditionResult::from(found)
4944    }
4945
4946    /// [220] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) in dieser SG8 vorhanden
4947    fn evaluate_220(&self, ctx: &EvaluationContext) -> ConditionResult {
4948        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4949        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4950        let has_seq = seq_segs.iter().any(|s| {
4951            s.elements
4952                .first()
4953                .and_then(|e| e.first())
4954                .is_some_and(|v| ["Z01", "Z98"].contains(&v.as_str()))
4955        });
4956        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4957        let has_cci = cci_segs.iter().any(|s| {
4958            s.elements
4959                .first()
4960                .and_then(|e| e.first())
4961                .is_some_and(|v| v == "Z30")
4962                && s.elements
4963                    .get(2)
4964                    .and_then(|e| e.first())
4965                    .is_some_and(|v| v == "Z06")
4966        });
4967        ConditionResult::from(has_seq && has_cci)
4968    }
4969
4970    /// [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)
4971    fn evaluate_221(&self, ctx: &EvaluationContext) -> ConditionResult {
4972        // Check: no SG8 with SEQ+Z15 has RFF+Z18 referencing same Marktlokation
4973        let has_seq_z15 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z15").is_empty();
4974        if !has_seq_z15 {
4975            return ConditionResult::True;
4976        }
4977        // Approximate: SEQ+Z15 exists, check if it references via RFF+Z18
4978        ctx.any_group_has_qualifier_without("SEQ", 0, "Z15", "RFF", 0, "Z18", &["SG4", "SG8"])
4979    }
4980
4981    /// [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...
4982    // HAND-EDITED (mako-twin, 55043): the generated version only checked that
4983    // some RFF+Z18 names some LOC+Z16 and dropped the Erzeugung clause, so it
4984    // held for every message with a Marktlokation. The scope never narrows to
4985    // one SG8 (only top-level groups are scoped), so "this SG8" is any SG8
4986    // other than SEQ+Z01, as in the neighbouring conditions.
4987    fn evaluate_223(&self, ctx: &EvaluationContext) -> ConditionResult {
4988        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (Erzeugung) — compared within the enclosing SG4 when the scope
4989        // reaches an SG8; the message as before otherwise.
4990        if let Some(scope) = ctx.enclosing_scope("SG8") {
4991            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
4992            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
4993                sg4_instances::has_cci(s, "Z30", "Z06")
4994            }));
4995        }
4996        let Some(blocks) = sg8_blocks(ctx) else {
4997            return ConditionResult::Unknown;
4998        };
4999        let loc_ids: Vec<String> = ctx
5000            .find_segments_with_qualifier("LOC", 0, "Z16")
5001            .iter()
5002            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
5003            .collect();
5004        let malo_refs = |b: &Sg8Block| -> Vec<String> {
5005            b.malo_refs
5006                .iter()
5007                .filter(|id| loc_ids.contains(id))
5008                .cloned()
5009                .collect()
5010        };
5011        // Marktlokationen whose SEQ+Z01 carries SG10 CCI+Z30++Z06 (Erzeugung).
5012        let erzeugung: Vec<String> = blocks
5013            .iter()
5014            .filter(|b| b.seq == "Z01")
5015            .filter(|b| {
5016                b.sg10.iter().any(|(ccis, _)| {
5017                    ccis.iter().any(|c| {
5018                        component(c, 0, 0) == Some("Z30") && component(c, 2, 0) == Some("Z06")
5019                    })
5020                })
5021            })
5022            .flat_map(|b| malo_refs(b))
5023            .collect();
5024        let referenced = blocks
5025            .iter()
5026            .filter(|b| b.seq != "Z01")
5027            .any(|b| malo_refs(b).iter().any(|id| erzeugung.contains(id)));
5028        ConditionResult::from(referenced)
5029    }
5030
5031    /// [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)
5032    fn evaluate_224(&self, ctx: &EvaluationContext) -> ConditionResult {
5033        // Check: no SG8 with SEQ+ZE7 has RFF+Z18 referencing same Marktlokation
5034        let has_seq_ze7 = !ctx.find_segments_with_qualifier("SEQ", 0, "ZE7").is_empty();
5035        if !has_seq_ze7 {
5036            return ConditionResult::True;
5037        }
5038        ctx.any_group_has_qualifier_without("SEQ", 0, "ZE7", "RFF", 0, "Z18", &["SG4", "SG8"])
5039    }
5040
5041    /// [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
5042    fn evaluate_227(&self, ctx: &EvaluationContext) -> ConditionResult {
5043        // Check: in SG8 with SEQ+Z98, SG10 has CCI+++ZA6 and CAV+E14 or ETK
5044        let r1 = ctx.filtered_parent_child_has_qualifier(
5045            &["SG4", "SG8"],
5046            "SEQ",
5047            0,
5048            "Z98",
5049            "SG10",
5050            "CAV",
5051            0,
5052            "E14",
5053        );
5054        if r1 == ConditionResult::True {
5055            return ConditionResult::True;
5056        }
5057        ctx.filtered_parent_child_has_qualifier(
5058            &["SG4", "SG8"],
5059            "SEQ",
5060            0,
5061            "Z98",
5062            "SG10",
5063            "CAV",
5064            0,
5065            "ETK",
5066        )
5067    }
5068
5069    /// [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)...
5070    // 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)
5071    fn evaluate_229(&self, ctx: &EvaluationContext) -> ConditionResult {
5072        // HAND-EDITED: "in einem weiteren SG8 … Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
5073        // reaches an SG8; the message as before otherwise.
5074        if let Some(scope) = ctx.enclosing_scope("SG8") {
5075            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
5076                ctx,
5077                scope,
5078                &["Z58", "ZC9", "ZD0", "ZD6"],
5079                false,
5080                true,
5081            ));
5082        }
5083        use std::collections::HashSet;
5084        let target_quals: HashSet<&str> = ["Z58", "ZC9", "ZD0", "ZD6"].iter().cloned().collect();
5085        // Collect per-SG8-instance: SEQ qualifier and Zeitraum-ID
5086        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
5087        let seq_zeitraums = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
5088        // Collect per-SG8-instance: RFF qualifier and DE1154 value
5089        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
5090        let rff_codes = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
5091        // Detect duplicate (seq_qual, zeitraum_id, objektcode) combinations across SG8 instances
5092        let mut seen: HashSet<(String, String, String)> = HashSet::new();
5093        for (idx, seq_qual) in &seq_quals {
5094            if seq_qual.is_empty() || !target_quals.contains(seq_qual.as_str()) {
5095                continue;
5096            }
5097            let zeitraum_id = seq_zeitraums
5098                .iter()
5099                .find(|(i, _)| i == idx)
5100                .map(|(_, v)| v.as_str())
5101                .unwrap_or("");
5102            // Find RFF+Z33 in this SG8 instance (correlation by instance index)
5103            for (ridx, rff_qual) in rff_quals.iter() {
5104                if ridx != idx || rff_qual != "Z33" {
5105                    continue;
5106                }
5107                if let Some((_, obj_code)) = rff_codes.iter().find(|(i, _)| i == ridx) {
5108                    if obj_code.is_empty() {
5109                        continue;
5110                    }
5111                    let key = (seq_qual.clone(), zeitraum_id.to_string(), obj_code.clone());
5112                    if seen.contains(&key) {
5113                        return ConditionResult::True;
5114                    }
5115                    seen.insert(key);
5116                }
5117            }
5118        }
5119        ConditionResult::False
5120    }
5121
5122    /// [232] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z52 (iMS) vorhanden
5123    fn evaluate_232(&self, ctx: &EvaluationContext) -> ConditionResult {
5124        // HAND-EDITED: the CCI and the CAV of one SG10.
5125        sg10_has(ctx, false, |segs| {
5126            sg4_instances::has_cci(segs, "", "Z83") && sg4_instances::has_cav(segs, "Z52")
5127        })
5128    }
5129
5130    /// [233] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle NB
5131    fn evaluate_233(&self, ctx: &EvaluationContext) -> ConditionResult {
5132        ctx.external.evaluate("recipient_role_check")
5133    }
5134
5135    /// [234] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z53 (kME/ mME) vorhanden
5136    fn evaluate_234(&self, ctx: &EvaluationContext) -> ConditionResult {
5137        // HAND-EDITED: the CCI and the CAV of one SG10.
5138        sg10_has(ctx, false, |segs| {
5139            sg4_instances::has_cci(segs, "", "Z83") && sg4_instances::has_cav(segs, "Z53")
5140        })
5141    }
5142
5143    /// [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
5144    fn evaluate_237(&self, ctx: &EvaluationContext) -> ConditionResult {
5145        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5146        // Check: PIA+Z02 with code starting with 1-08 and containing AGS
5147        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5148        let found = pia_segs.iter().any(|s| {
5149            s.elements
5150                .first()
5151                .and_then(|e| e.first())
5152                .is_some_and(|v| v == "Z02")
5153                && s.elements
5154                    .get(1)
5155                    .and_then(|e| e.first())
5156                    .is_some_and(|v| v.starts_with("1-08") && v.contains("AGS"))
5157        });
5158        ConditionResult::from(found)
5159    }
5160
5161    /// [238] Wenn SG4 IDE+24 (Vorgang) nicht vorhanden
5162    fn evaluate_238(&self, ctx: &EvaluationContext) -> ConditionResult {
5163        let ide_segs = ctx.find_segments("IDE");
5164        let found = ide_segs.iter().any(|s| {
5165            s.elements
5166                .first()
5167                .and_then(|e| e.first())
5168                .is_some_and(|v| v == "24")
5169        });
5170        ConditionResult::from(!found)
5171    }
5172
5173    /// [239] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+Z65 (Wärme/Kälte) vorhanden
5174    fn evaluate_239(&self, ctx: &EvaluationContext) -> ConditionResult {
5175        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
5176        // SG10, when the scope reaches one; of any one SG10 otherwise.
5177        this_sg10_has(ctx, |segs| {
5178            segs.iter()
5179                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
5180                && sg4_instances::has_cav(segs, "Z65")
5181        })
5182    }
5183
5184    /// [240] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) CCI+Z22++Z91 (Veräußerungsform der erzeugenden Marktlokation:: Marktprämie) vorhanden
5185    fn evaluate_240(&self, ctx: &EvaluationContext) -> ConditionResult {
5186        let seq_segs = ctx.find_segments("SEQ");
5187        let found = seq_segs.iter().any(|s| {
5188            s.elements
5189                .first()
5190                .and_then(|e| e.first())
5191                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
5192        });
5193        ConditionResult::from(found)
5194    }
5195
5196    /// [241] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle MSB
5197    fn evaluate_241(&self, ctx: &EvaluationContext) -> ConditionResult {
5198        ctx.external.evaluate("recipient_role_check")
5199    }
5200
5201    /// [243] Wenn SG10 CCI+6++ZA8 (Aggreg.verantw. NB) in dieser SG8 vorhanden
5202    fn evaluate_243(&self, ctx: &EvaluationContext) -> ConditionResult {
5203        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5204        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5205        let found = cci_segs.iter().any(|s| {
5206            s.elements
5207                .first()
5208                .and_then(|e| e.first())
5209                .is_some_and(|v| v == "6")
5210                && s.elements
5211                    .get(2)
5212                    .and_then(|e| e.first())
5213                    .is_some_and(|v| v == "ZA8")
5214        });
5215        ConditionResult::from(found)
5216    }
5217
5218    /// [244] Wenn SG10 CCI+6++ZA9 (Aggreg.verantw. ÜNB) in dieser SG8 vorhanden
5219    fn evaluate_244(&self, ctx: &EvaluationContext) -> ConditionResult {
5220        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5221        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5222        let found = cci_segs.iter().any(|s| {
5223            s.elements
5224                .first()
5225                .and_then(|e| e.first())
5226                .is_some_and(|v| v == "6")
5227                && s.elements
5228                    .get(2)
5229                    .and_then(|e| e.first())
5230                    .is_some_and(|v| v == "ZA9")
5231        });
5232        ConditionResult::from(found)
5233    }
5234
5235    /// [248] Wenn die Inbetriebnahme der verbrauchenden Marktlokation innerhalb der letzten drei Jahre war und die Energie der Marktlokation mit iMS ausgestatteten Messlokationen erfolgt
5236    fn evaluate_248(&self, _ctx: &EvaluationContext) -> ConditionResult {
5237        // TODO: implement
5238        ConditionResult::Unknown
5239    }
5240
5241    /// [249] Innerhalb eines SG4 IDE müssen alle DE1131 der SG4 STS+E01 (Status der Antwort) den identischen Wert enthalten
5242    fn evaluate_249(&self, ctx: &EvaluationContext) -> ConditionResult {
5243        // Check: all STS+E01 DE1131 values are identical
5244        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
5245        if sts_segs.len() <= 1 {
5246            return ConditionResult::True;
5247        }
5248        let first_val = sts_segs[0].elements.first().and_then(|e| e.get(1));
5249        let all_same = sts_segs
5250            .iter()
5251            .all(|s| s.elements.first().and_then(|e| e.get(1)) == first_val);
5252        ConditionResult::from(all_same)
5253    }
5254
5255    /// [251] Wenn MP-ID in SG2 NAD+MS (Nachrichtensender) in der Rolle BIKO
5256    fn evaluate_251(&self, ctx: &EvaluationContext) -> ConditionResult {
5257        ctx.external.evaluate("sender_role_check")
5258    }
5259
5260    /// [252] Wenn DE0068 vorhanden
5261    fn evaluate_252(&self, ctx: &EvaluationContext) -> ConditionResult {
5262        let unb_segs = ctx.find_segments("UNB");
5263        let found = unb_segs.iter().any(|s| {
5264            s.elements
5265                .get(4)
5266                .and_then(|e| e.first())
5267                .is_some_and(|v| !v.is_empty())
5268        });
5269        ConditionResult::from(found)
5270    }
5271
5272    /// [254] Es ist der Code \"AUA\" einzutragen
5273    fn evaluate_254(&self, _ctx: &EvaluationContext) -> ConditionResult {
5274        // Check: the code "AUA" is present
5275        ConditionResult::True // AUA is always the required code when this condition applies
5276    }
5277
5278    /// [255] Wenn Marktlokation am Redispatch 2.0 teilnimmt
5279    fn evaluate_255(&self, _ctx: &EvaluationContext) -> ConditionResult {
5280        // TODO: implement
5281        ConditionResult::Unknown
5282    }
5283
5284    /// [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
5285    fn evaluate_256(&self, ctx: &EvaluationContext) -> ConditionResult {
5286        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5287        // Check: PIA+5 has OBIS code matching 1-?:1.8.? or 1-?:2.8.? pattern
5288        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5289        let found = pia_segs.iter().any(|s| {
5290            s.elements
5291                .first()
5292                .and_then(|e| e.first())
5293                .is_some_and(|v| v == "5")
5294                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5295                    (v.contains(":1.8.") || v.contains(":2.8.")) && v.starts_with("1-")
5296                })
5297        });
5298        ConditionResult::from(found)
5299    }
5300
5301    /// [257] Wenn in derselben SG8 SEQ (OBIS-Daten der Marktlokation) das PIA+5+1-b?:1.9.e vorhanden
5302    fn evaluate_257(&self, ctx: &EvaluationContext) -> ConditionResult {
5303        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5304        // Check: PIA+5 has OBIS code matching 1-?:1.9.? pattern
5305        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5306        let found = pia_segs.iter().any(|s| {
5307            s.elements
5308                .first()
5309                .and_then(|e| e.first())
5310                .is_some_and(|v| v == "5")
5311                && s.elements
5312                    .get(1)
5313                    .and_then(|e| e.first())
5314                    .is_some_and(|v| v.contains(":1.9.") && v.starts_with("1-"))
5315        });
5316        ConditionResult::from(found)
5317    }
5318
5319    /// [259] Wenn in diesem PIA der Code im DE7140 mit 1-01, 1-03, 1-05 enthalten ist
5320    fn evaluate_259(&self, ctx: &EvaluationContext) -> ConditionResult {
5321        let pia_segs = ctx.find_segments("PIA");
5322        let found = pia_segs.iter().any(|s| {
5323            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5324                let prefixes = ["1-01", "1-03", "1-05"];
5325                prefixes.iter().any(|p| v.starts_with(p))
5326            })
5327        });
5328        ConditionResult::from(found)
5329    }
5330
5331    /// [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
5332    fn evaluate_261(&self, ctx: &EvaluationContext) -> ConditionResult {
5333        let pia_segs = ctx.find_segments("PIA");
5334        let found = pia_segs.iter().any(|s| {
5335            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5336                let prefixes = [
5337                    "1-07-1", "2", "1-10-1", "2", "4", "5", "6", "7", "8", "9", "1-11-1",
5338                ];
5339                prefixes.iter().any(|p| v.starts_with(p))
5340            })
5341        });
5342        ConditionResult::from(found)
5343    }
5344
5345    /// [262] Wenn Produkt an der Marktlokation vorhanden ist
5346    fn evaluate_262(&self, _ctx: &EvaluationContext) -> ConditionResult {
5347        // TODO: implement
5348        ConditionResult::Unknown
5349    }
5350
5351    /// [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
5352    // HAND-EDITED (mako-twin, 55043): the generated version looked for
5353    // CAV+E14/ETK in any SG10 under SEQ+Z01/Z98, ignoring CCI+++ZA6; the AHB
5354    // asks for CAV+Z36 (TEP m. Referenzmessung) in the SG10 CCI+++ZA6.
5355    fn evaluate_265(&self, ctx: &EvaluationContext) -> ConditionResult {
5356        let Some(blocks) = sg8_blocks(ctx) else {
5357            return ConditionResult::Unknown;
5358        };
5359        let found = blocks
5360            .iter()
5361            .filter(|b| b.seq == "Z01" || b.seq == "Z98")
5362            .any(|b| {
5363                b.sg10.iter().any(|(ccis, cavs)| {
5364                    ccis.iter().any(|c| component(c, 2, 0) == Some("ZA6"))
5365                        && cavs.iter().any(|c| component(c, 0, 0) == Some("Z36"))
5366                })
5367            });
5368        ConditionResult::from(found)
5369    }
5370
5371    /// [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...
5372    // 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)
5373    fn evaluate_266(&self, ctx: &EvaluationContext) -> ConditionResult {
5374        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8" — compared within the enclosing SG4 when the scope
5375        // reaches an SG8; the message as before otherwise.
5376        if let Some(scope) = ctx.enclosing_scope("SG8") {
5377            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
5378            return ConditionResult::from(z01s.iter().any(|p| {
5379                sg4_instances::sg10_at(ctx, p, |s| {
5380                    sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "Z36")
5381                })
5382            }));
5383        }
5384        let nav = match ctx.navigator {
5385            Some(n) => n,
5386            None => {
5387                // Fallback: check any SG8+SEQ+Z01 child SG10 has CAV+Z36
5388                return ctx.filtered_parent_child_has_qualifier(
5389                    &["SG4", "SG8"],
5390                    "SEQ",
5391                    0,
5392                    "Z01",
5393                    "SG10",
5394                    "CAV",
5395                    0,
5396                    "Z36",
5397                );
5398            }
5399        };
5400        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5401        for i in 0..sg8_count {
5402            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
5403            for j in 0..sg10_count {
5404                // CCI+++ZA6 means elements[2][0] == "ZA6"
5405                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
5406                let has_cci_za6 = ccis.iter().any(|s| {
5407                    s.elements
5408                        .get(2)
5409                        .and_then(|e: &Vec<String>| e.first())
5410                        .is_some_and(|v: &String| v == "ZA6")
5411                });
5412                if has_cci_za6 {
5413                    let cavs =
5414                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
5415                    if cavs.iter().any(|s| {
5416                        s.elements
5417                            .first()
5418                            .and_then(|e: &Vec<String>| e.first())
5419                            .is_some_and(|v: &String| v == "Z36")
5420                    }) {
5421                        return ConditionResult::True;
5422                    }
5423                }
5424            }
5425        }
5426        ConditionResult::False
5427    }
5428
5429    /// [267] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
5430    fn evaluate_267(&self, ctx: &EvaluationContext) -> ConditionResult {
5431        let seq_segs = ctx.find_segments("SEQ");
5432        let found = seq_segs.iter().any(|s| {
5433            s.elements
5434                .first()
5435                .and_then(|e| e.first())
5436                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
5437        });
5438        ConditionResult::from(!found)
5439    }
5440
5441    /// [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
5442    fn evaluate_268(&self, _ctx: &EvaluationContext) -> ConditionResult {
5443        // Most European countries have postal codes — default to true
5444        ConditionResult::True
5445    }
5446
5447    /// [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
5448    fn evaluate_269(&self, _ctx: &EvaluationContext) -> ConditionResult {
5449        // TODO: implement
5450        ConditionResult::Unknown
5451    }
5452
5453    /// [270] Wenn in diesem PIA der Code im DE7140 mit 1-02, 1-04, 1-06, 1-09 beginnt
5454    fn evaluate_270(&self, ctx: &EvaluationContext) -> ConditionResult {
5455        let pia_segs = ctx.find_segments("PIA");
5456        let found = pia_segs.iter().any(|s| {
5457            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5458                let prefixes = ["1-02", "1-04", "1-06", "1-09"];
5459                prefixes.iter().any(|p| v.starts_with(p))
5460            })
5461        });
5462        ConditionResult::from(found)
5463    }
5464
5465    /// [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 ...
5466    fn evaluate_273(&self, ctx: &EvaluationContext) -> ConditionResult {
5467        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5468        // Check: PIA+5 has OBIS code matching energy quantity patterns in SG8 SEQ+Z20
5469        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5470        let found = pia_segs.iter().any(|s| {
5471            s.elements
5472                .first()
5473                .and_then(|e| e.first())
5474                .is_some_and(|v| v == "5")
5475                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5476                    v.starts_with("1-")
5477                        && (v.contains(":1.8.") || v.contains(":2.8.") || v.contains(":1.29."))
5478                })
5479        });
5480        ConditionResult::from(found)
5481    }
5482
5483    /// [279] Wenn STS+7++xxx+ZW4 / ZAP (Transaktionsgrundergänzung  verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
5484    fn evaluate_279(&self, ctx: &EvaluationContext) -> ConditionResult {
5485        let sts_segs = ctx.find_segments("STS");
5486        let found = sts_segs.iter().any(|s| {
5487            s.elements
5488                .first()
5489                .and_then(|e| e.first())
5490                .is_some_and(|v| v == "7")
5491                && s.elements
5492                    .get(3)
5493                    .and_then(|e| e.first())
5494                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
5495        });
5496        ConditionResult::from(found)
5497    }
5498
5499    /// [280] Nur MP-ID aus Sparte Strom
5500    fn evaluate_280(&self, ctx: &EvaluationContext) -> ConditionResult {
5501        // Check: Marktpartner-ID is from Sparte Strom
5502        // Approximate: check NAD+MS C082 for Strom MP-ID format
5503        ctx.external.evaluate("sector_check")
5504    }
5505
5506    /// [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
5507    fn evaluate_282(&self, ctx: &EvaluationContext) -> ConditionResult {
5508        // Check: PIA DE7140 has one of the specific codes
5509        let pia_segs = ctx.find_segments("PIA");
5510        let found = pia_segs.iter().any(|s| {
5511            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5512                v.starts_with("1-01-6-005")
5513                    || v.starts_with("1-01-9-001")
5514                    || v.starts_with("1-01-9-002")
5515                    || v.starts_with("1-07-3-001")
5516                    || v.starts_with("1-08-1-001")
5517                    || v.starts_with("1-08-3-001")
5518                    || v.starts_with("1-08-4-001")
5519                    || v.starts_with("1-08-4-002")
5520                    || v.starts_with("1-08-4-003")
5521                    || v.starts_with("1-08-5")
5522            })
5523        });
5524        ConditionResult::from(found)
5525    }
5526
5527    /// [284] Erlaubte Codes aus der Codeliste der Gruppenartikel- und Artikel-ID sind in der Spalte UTILMD/Codeverwendung mit X gekennzeichnet
5528    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
5529    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
5530    // Regenerating this function should preserve the shape, but re-verify
5531    // against the committed body if generate-conditions is run.
5532    fn evaluate_284(&self, ctx: &EvaluationContext) -> ConditionResult {
5533        // [284] Erlaubte Codes aus der Codeliste der Gruppenartikel-
5534        // und Artikel-ID mit X in der Spalte UTILMD/Codeverwendung.
5535        //
5536        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
5537        // narrows to the enclosing SG8 instance; otherwise falls back to
5538        // message-wide find_segments. Matches the AHB wording
5539        // "in derselben SG8 im PIA DE7140 ein Code...".
5540        use crate::eval::CodelistColumn;
5541        let any_match = ctx.scoped_find_segments("PIA").iter().any(|s| {
5542            let is_z02 = s
5543                .elements
5544                .first()
5545                .and_then(|e| e.first())
5546                .is_some_and(|v| v == "Z02");
5547            if !is_z02 {
5548                return false;
5549            }
5550            let code = match s.elements.get(1).and_then(|e| e.first()) {
5551                Some(c) if !c.is_empty() => c,
5552                _ => return false,
5553            };
5554            ctx.codelist
5555                .flag(code, CodelistColumn::UtilmdCodeverwendung)
5556                .unwrap_or(false)
5557        });
5558        ConditionResult::from(any_match)
5559    }
5560
5561    /// [285] Wenn in diesem PIA der Code im DE7140 mit 1-08-3-AGS angegeben ist
5562    fn evaluate_285(&self, ctx: &EvaluationContext) -> ConditionResult {
5563        // Check: PIA DE7140 starts with 1-08-3-AGS
5564        let pia_segs = ctx.find_segments("PIA");
5565        let found = pia_segs.iter().any(|s| {
5566            s.elements
5567                .get(1)
5568                .and_then(|e| e.first())
5569                .is_some_and(|v| v.contains("1-08-3") && v.contains("AGS"))
5570        });
5571        ConditionResult::from(found)
5572    }
5573
5574    /// [286] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/2/4/5-AGS-KG
5575    fn evaluate_286(&self, ctx: &EvaluationContext) -> ConditionResult {
5576        // Check: PIA DE7140 matches 1-08-1/2/4/5-AGS-KG pattern
5577        let pia_segs = ctx.find_segments("PIA");
5578        let found = pia_segs.iter().any(|s| {
5579            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5580                (v.starts_with("1-08-1")
5581                    || v.starts_with("1-08-2")
5582                    || v.starts_with("1-08-4")
5583                    || v.starts_with("1-08-5"))
5584                    && v.contains("AGS")
5585                    && v.contains("KG")
5586            })
5587        });
5588        ConditionResult::from(found)
5589    }
5590
5591    /// [287] Erlaubte Codes aus der PRICAT BGM+ Z70 (Preisblatt Netznutzung) des verantwortlichen NB
5592    fn evaluate_287(&self, _ctx: &EvaluationContext) -> ConditionResult {
5593        // TODO: implement
5594        ConditionResult::Unknown
5595    }
5596
5597    /// [288] Wenn in derselben SG8 (OBIS-Daten der Marktlokation) das PIA+5+1-b?:2.9.e vorhanden
5598    fn evaluate_288(&self, ctx: &EvaluationContext) -> ConditionResult {
5599        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5600        // Check: PIA+5 has OBIS code matching 1-?:2.9.? pattern
5601        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5602        let found = pia_segs.iter().any(|s| {
5603            s.elements
5604                .first()
5605                .and_then(|e| e.first())
5606                .is_some_and(|v| v == "5")
5607                && s.elements
5608                    .get(1)
5609                    .and_then(|e| e.first())
5610                    .is_some_and(|v| v.contains(":2.9.") && v.starts_with("1-"))
5611        });
5612        ConditionResult::from(found)
5613    }
5614
5615    /// [291] Wenn in derselben SG8 (Zuordnung Lokation zum Objektcode des Lokationsbündels) das RFF+Z37 vorhanden
5616    fn evaluate_291(&self, ctx: &EvaluationContext) -> ConditionResult {
5617        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5618        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
5619        let found = rff_segs.iter().any(|s| {
5620            s.elements
5621                .first()
5622                .and_then(|e| e.first())
5623                .is_some_and(|v| v == "Z37")
5624        });
5625        ConditionResult::from(found)
5626    }
5627
5628    /// [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...
5629    fn evaluate_292(&self, ctx: &EvaluationContext) -> ConditionResult {
5630        ctx.external.evaluate("code_list_membership_check")
5631    }
5632
5633    /// [293] Wenn SG5 LOC+Z22 (ruhende Marktlokation) vorhanden
5634    fn evaluate_293(&self, ctx: &EvaluationContext) -> ConditionResult {
5635        let loc_segs = ctx.find_segments("LOC");
5636        let found = loc_segs.iter().any(|s| {
5637            s.elements
5638                .first()
5639                .and_then(|e| e.first())
5640                .is_some_and(|v| v == "Z22")
5641        });
5642        ConditionResult::from(found)
5643    }
5644
5645    /// [294] Wenn SG5 LOC+Z18 (Netzlokation) vorhanden
5646    fn evaluate_294(&self, ctx: &EvaluationContext) -> ConditionResult {
5647        let loc_segs = ctx.find_segments("LOC");
5648        let found = loc_segs.iter().any(|s| {
5649            s.elements
5650                .first()
5651                .and_then(|e| e.first())
5652                .is_some_and(|v| v == "Z18")
5653        });
5654        ConditionResult::from(found)
5655    }
5656
5657    /// [295] Wenn SG5 LOC+Z15 (MaBiS-Zählpunkt) nicht vorhanden
5658    fn evaluate_295(&self, ctx: &EvaluationContext) -> ConditionResult {
5659        let loc_segs = ctx.find_segments("LOC");
5660        let found = loc_segs.iter().any(|s| {
5661            s.elements
5662                .first()
5663                .and_then(|e| e.first())
5664                .is_some_and(|v| v == "Z15")
5665        });
5666        ConditionResult::from(!found)
5667    }
5668
5669    /// [296] Wenn SG5 LOC+Z20 (Technische Ressource) vorhanden
5670    fn evaluate_296(&self, ctx: &EvaluationContext) -> ConditionResult {
5671        let loc_segs = ctx.find_segments("LOC");
5672        let found = loc_segs.iter().any(|s| {
5673            s.elements
5674                .first()
5675                .and_then(|e| e.first())
5676                .is_some_and(|v| v == "Z20")
5677        });
5678        ConditionResult::from(found)
5679    }
5680
5681    /// [297] #kv# Wenn SG5 LOC+Z18 (Netzlokation) nicht vorhanden
5682    fn evaluate_297(&self, ctx: &EvaluationContext) -> ConditionResult {
5683        let loc_segs = ctx.find_segments("LOC");
5684        let found = loc_segs.iter().any(|s| {
5685            s.elements
5686                .first()
5687                .and_then(|e| e.first())
5688                .is_some_and(|v| v == "Z18")
5689        });
5690        ConditionResult::from(!found)
5691    }
5692
5693    /// [298] Wenn SG5 LOC+Z20 (Technische Ressource) nicht vorhanden
5694    fn evaluate_298(&self, ctx: &EvaluationContext) -> ConditionResult {
5695        let loc_segs = ctx.find_segments("LOC");
5696        let found = loc_segs.iter().any(|s| {
5697            s.elements
5698                .first()
5699                .and_then(|e| e.first())
5700                .is_some_and(|v| v == "Z20")
5701        });
5702        ConditionResult::from(!found)
5703    }
5704
5705    /// [299] #kv# Wenn SG5 LOC+Z19 (Steuerbare Ressource) nicht vorhanden
5706    fn evaluate_299(&self, ctx: &EvaluationContext) -> ConditionResult {
5707        let loc_segs = ctx.find_segments("LOC");
5708        let found = loc_segs.iter().any(|s| {
5709            s.elements
5710                .first()
5711                .and_then(|e| e.first())
5712                .is_some_and(|v| v == "Z19")
5713        });
5714        ConditionResult::from(!found)
5715    }
5716
5717    /// [300] Wenn SG8 SEQ+Z15/ ZE7 (Daten der Tranche) nicht vorhanden
5718    fn evaluate_300(&self, ctx: &EvaluationContext) -> ConditionResult {
5719        let seq_segs = ctx.find_segments("SEQ");
5720        let found = seq_segs.iter().any(|s| {
5721            s.elements
5722                .first()
5723                .and_then(|e| e.first())
5724                .is_some_and(|v| ["Z15", "ZE7"].contains(&v.as_str()))
5725        });
5726        ConditionResult::from(!found)
5727    }
5728
5729    /// [301] Wenn BGM+E03 (Änderungsmeldung) vorhanden
5730    fn evaluate_301(&self, ctx: &EvaluationContext) -> ConditionResult {
5731        let bgm_segs = ctx.find_segments("BGM");
5732        let found = bgm_segs.iter().any(|s| {
5733            s.elements
5734                .first()
5735                .and_then(|e| e.first())
5736                .is_some_and(|v| v == "E03")
5737        });
5738        ConditionResult::from(found)
5739    }
5740
5741    /// [302] Wenn SG8 SEQ+Z08 (Profilschardaten) nicht vorhanden
5742    fn evaluate_302(&self, ctx: &EvaluationContext) -> ConditionResult {
5743        let seq_segs = ctx.find_segments("SEQ");
5744        let found = seq_segs.iter().any(|s| {
5745            s.elements
5746                .first()
5747                .and_then(|e| e.first())
5748                .is_some_and(|v| ["Z08"].contains(&v.as_str()))
5749        });
5750        ConditionResult::from(!found)
5751    }
5752
5753    /// [303] Wenn SG8 SEQ+Z21 (Profildaten) nicht vorhanden
5754    fn evaluate_303(&self, ctx: &EvaluationContext) -> ConditionResult {
5755        let seq_segs = ctx.find_segments("SEQ");
5756        let found = seq_segs.iter().any(|s| {
5757            s.elements
5758                .first()
5759                .and_then(|e| e.first())
5760                .is_some_and(|v| ["Z21"].contains(&v.as_str()))
5761        });
5762        ConditionResult::from(!found)
5763    }
5764
5765    /// [304] Wenn SG8 SEQ+Z22 (Daten der Summenzeitreihe) vorhanden
5766    fn evaluate_304(&self, ctx: &EvaluationContext) -> ConditionResult {
5767        let seq_segs = ctx.find_segments("SEQ");
5768        let found = seq_segs.iter().any(|s| {
5769            s.elements
5770                .first()
5771                .and_then(|e| e.first())
5772                .is_some_and(|v| ["Z22"].contains(&v.as_str()))
5773        });
5774        ConditionResult::from(found)
5775    }
5776
5777    /// [305] Wenn SG8 SEQ+Z24 (Daten der Überführungszeitreihe) vorhanden
5778    fn evaluate_305(&self, ctx: &EvaluationContext) -> ConditionResult {
5779        let seq_segs = ctx.find_segments("SEQ");
5780        let found = seq_segs.iter().any(|s| {
5781            s.elements
5782                .first()
5783                .and_then(|e| e.first())
5784                .is_some_and(|v| ["Z24"].contains(&v.as_str()))
5785        });
5786        ConditionResult::from(found)
5787    }
5788
5789    /// [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
5790    fn evaluate_306(&self, ctx: &EvaluationContext) -> ConditionResult {
5791        // HAND-EDITED: "SG5 LOC+Z22 mit derselben Zeitraum-ID wie in dieser SG8 … des SEQ+Z58" — compared within the enclosing SG4 when the scope
5792        // reaches an SG8; the message as before otherwise.
5793        if let Some(scope) = ctx.enclosing_scope("SG8") {
5794            return ConditionResult::from(
5795                sg4_instances::seq_at(ctx, scope)
5796                    .filter(|(code, zeitraum)| code == "Z58" && !zeitraum.is_empty())
5797                    .is_some_and(|(_, zeitraum)| {
5798                        sg4_instances::ruhende_malo_with_zeitraum(ctx, &zeitraum)
5799                    }),
5800            );
5801        }
5802        use std::collections::HashSet;
5803        // Collect Zeitraum-IDs from SG5 LOC+Z22 (Ruhende Marktlokation) — DE3224 at elements[1][3]
5804        let loc_z22_ids: HashSet<String> = ctx
5805            .find_segments_with_qualifier("LOC", 0, "Z22")
5806            .into_iter()
5807            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
5808            .filter(|v| !v.is_empty())
5809            .collect();
5810        if loc_z22_ids.is_empty() {
5811            return ConditionResult::False;
5812        }
5813        // Check if any SG8 SEQ+Z58 has DE1050 (elements[1][0]) matching a LOC+Z22 DE3224
5814        let has_match = ctx
5815            .find_segments_with_qualifier("SEQ", 0, "Z58")
5816            .iter()
5817            .any(|s| {
5818                s.elements
5819                    .get(1)
5820                    .and_then(|e: &Vec<String>| e.first())
5821                    .is_some_and(|v: &String| !v.is_empty() && loc_z22_ids.contains(v.as_str()))
5822            });
5823        ConditionResult::from(has_match)
5824    }
5825
5826    /// [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
5827    // 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)
5828    fn evaluate_307(&self, ctx: &EvaluationContext) -> ConditionResult {
5829        // HAND-EDITED: "SG5 LOC+Z22 mit derselben Zeitraum-ID … nicht vorhanden wie in dieser SG8 … des SEQ+Z58" — compared within the enclosing SG4 when the scope
5830        // reaches an SG8; the message as before otherwise.
5831        if let Some(scope) = ctx.enclosing_scope("SG8") {
5832            return ConditionResult::from(
5833                sg4_instances::seq_at(ctx, scope)
5834                    .filter(|(code, zeitraum)| code == "Z58" && !zeitraum.is_empty())
5835                    .is_some_and(|(_, zeitraum)| {
5836                        !sg4_instances::ruhende_malo_with_zeitraum(ctx, &zeitraum)
5837                    }),
5838            );
5839        }
5840        let seq_segs = ctx.find_segments_with_qualifier("SEQ", 0, "Z58");
5841        if seq_segs.is_empty() {
5842            return ConditionResult::Unknown;
5843        }
5844        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z22");
5845        let loc_z22_zeitraum_ids: Vec<&str> = loc_segs
5846            .iter()
5847            .filter_map(|s| s.elements.get(1).and_then(|e| e.get(3)).map(|v| v.as_str()))
5848            .filter(|v| !v.is_empty())
5849            .collect();
5850        ConditionResult::from(seq_segs.iter().any(|seq| {
5851            let zid = seq
5852                .elements
5853                .get(1)
5854                .and_then(|e: &Vec<String>| e.first())
5855                .map(|v| v.as_str())
5856                .unwrap_or("");
5857            !zid.is_empty() && !loc_z22_zeitraum_ids.contains(&zid)
5858        }))
5859    }
5860
5861    /// [309] Wenn SG5 LOC+Z22 (ruhende Marktlokation) nicht vorhanden
5862    fn evaluate_309(&self, ctx: &EvaluationContext) -> ConditionResult {
5863        let loc_segs = ctx.find_segments("LOC");
5864        let found = loc_segs.iter().any(|s| {
5865            s.elements
5866                .first()
5867                .and_then(|e| e.first())
5868                .is_some_and(|v| v == "Z22")
5869        });
5870        ConditionResult::from(!found)
5871    }
5872
5873    /// [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...
5874    fn evaluate_314(&self, ctx: &EvaluationContext) -> ConditionResult {
5875        ctx.external.evaluate("code_list_membership_check")
5876    }
5877
5878    /// [315] Wenn BGM+Z88 (Datenclearing) vorhanden
5879    fn evaluate_315(&self, ctx: &EvaluationContext) -> ConditionResult {
5880        ctx.external.evaluate("data_clearing_required")
5881    }
5882
5883    /// [316] Wenn in derselben SG8 das RFF+Z14 (Smartmeter-Gateway) nicht vorhanden
5884    fn evaluate_316(&self, ctx: &EvaluationContext) -> ConditionResult {
5885        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5886        ctx.this_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
5887    }
5888
5889    /// [317] Es ist derselbe Wert wie im DE2380 von DTM+92 (Datum Vertragsbeginn) einzutragen
5890    fn evaluate_317(&self, ctx: &EvaluationContext) -> ConditionResult {
5891        // Check: DTM+92 (Datum Vertragsbeginn) exists — value identity is structural
5892        let found = !ctx.find_segments_with_qualifier("DTM", 0, "92").is_empty();
5893        if found {
5894            ConditionResult::True
5895        } else {
5896            ConditionResult::Unknown
5897        }
5898    }
5899
5900    /// [318] Es ist derselbe Wert wie im DE2380 von DTM+93 (Datum Vertragende) einzutragen
5901    fn evaluate_318(&self, ctx: &EvaluationContext) -> ConditionResult {
5902        // Check: DTM+93 (Datum Vertragende) exists — value identity is structural
5903        let found = !ctx.find_segments_with_qualifier("DTM", 0, "93").is_empty();
5904        if found {
5905            ConditionResult::True
5906        } else {
5907            ConditionResult::Unknown
5908        }
5909    }
5910
5911    /// [321] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
5912    fn evaluate_321(&self, ctx: &EvaluationContext) -> ConditionResult {
5913        let com_segs = ctx.find_segments("COM");
5914        let has_em = com_segs.iter().any(|s| {
5915            s.elements
5916                .first()
5917                .and_then(|e| e.get(1))
5918                .is_some_and(|v| v == "EM")
5919        });
5920        ConditionResult::from(has_em)
5921    }
5922
5923    /// [322] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
5924    fn evaluate_322(&self, ctx: &EvaluationContext) -> ConditionResult {
5925        let com_segs = ctx.find_segments("COM");
5926        let has_phone = com_segs.iter().any(|s| {
5927            s.elements
5928                .first()
5929                .and_then(|e| e.get(1))
5930                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
5931        });
5932        ConditionResult::from(has_phone)
5933    }
5934
5935    /// [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...
5936    fn evaluate_323(&self, ctx: &EvaluationContext) -> ConditionResult {
5937        ctx.external.evaluate("code_list_membership_check")
5938    }
5939
5940    /// [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...
5941    // 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)
5942    fn evaluate_327(&self, ctx: &EvaluationContext) -> ConditionResult {
5943        // HAND-EDITED: "die Zeitraum-ID im DE1050 dieser SG8 SEQ … einer SG6 RFF+Z47/Z48/Z49" — compared within the enclosing SG4 when the scope
5944        // reaches an SG8; the message as before otherwise.
5945        if let Some(scope) = ctx.enclosing_scope("SG8") {
5946            let Some((_, zeitraum)) =
5947                sg4_instances::seq_at(ctx, scope).filter(|(_, z)| !z.is_empty())
5948            else {
5949                return ConditionResult::False;
5950            };
5951            let sg6s = ctx.scopes_under("SG4", "SG6").unwrap_or_default();
5952            return ConditionResult::from(sg6s.iter().any(|p| {
5953                ctx.find_segments_at(p, "RFF").iter().any(|s| {
5954                    matches!(s.get_element(0), "Z47" | "Z48" | "Z49")
5955                        && s.get_component(0, 2) == zeitraum
5956                }) && ctx.find_segments_at(p, "DTM").iter().any(|s| {
5957                    s.get_element(0) == "Z25"
5958                        && s.get_component(0, 1).replace("?+", "+").as_str() >= "202212312300+00"
5959                })
5960            }));
5961        }
5962        const THRESHOLD: &str = "202212312300+00";
5963        let seq_zeitraum_ids: Vec<String> = ctx
5964            .find_segments("SEQ")
5965            .iter()
5966            .filter_map(|s| {
5967                s.elements
5968                    .get(1)
5969                    .and_then(|e: &Vec<String>| e.first())
5970                    .cloned()
5971            })
5972            .filter(|v| !v.is_empty())
5973            .collect();
5974        if seq_zeitraum_ids.is_empty() {
5975            return ConditionResult::Unknown;
5976        }
5977        let rff_segs = ctx.find_segments("RFF");
5978        let matching_rff = rff_segs.iter().any(|rff| {
5979            let qual = rff
5980                .elements
5981                .first()
5982                .and_then(|e: &Vec<String>| e.first())
5983                .map(|v| v.as_str())
5984                .unwrap_or("");
5985            if !["Z47", "Z48", "Z49"].contains(&qual) {
5986                return false;
5987            }
5988            let zid = rff
5989                .elements
5990                .first()
5991                .and_then(|e| e.get(2))
5992                .map(|v| v.as_str())
5993                .unwrap_or("");
5994            !zid.is_empty() && seq_zeitraum_ids.iter().any(|id| id.as_str() == zid)
5995        });
5996        if !matching_rff {
5997            return ConditionResult::False;
5998        }
5999        for dtm in ctx.find_segments_with_qualifier("DTM", 0, "Z25") {
6000            let val = dtm
6001                .elements
6002                .first()
6003                .and_then(|e| e.get(1))
6004                .map(|v| v.replace("?+", "+"))
6005                .unwrap_or_default();
6006            if !val.is_empty() && val.as_str() >= THRESHOLD {
6007                return ConditionResult::True;
6008            }
6009        }
6010        ConditionResult::False
6011    }
6012
6013    /// [329] Wenn in dieser SG4 IDE+24 das STS+E01++A03: E_0096 bzw. A04:E_0097 (Status der Antwort) nicht vorhanden
6014    fn evaluate_329(&self, ctx: &EvaluationContext) -> ConditionResult {
6015        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6016        // Check: STS+E01 does NOT have A03 with E_0096 or A04 with E_0097
6017        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6018        let has_a03_e0096 = sts_segs.iter().any(|s| {
6019            s.elements
6020                .get(2)
6021                .and_then(|e| e.first())
6022                .is_some_and(|v| v == "A03")
6023                && s.elements
6024                    .get(2)
6025                    .and_then(|e| e.get(1))
6026                    .is_some_and(|v| v == "E_0096")
6027        });
6028        let has_a04_e0097 = sts_segs.iter().any(|s| {
6029            s.elements
6030                .get(2)
6031                .and_then(|e| e.first())
6032                .is_some_and(|v| v == "A04")
6033                && s.elements
6034                    .get(2)
6035                    .and_then(|e| e.get(1))
6036                    .is_some_and(|v| v == "E_0097")
6037        });
6038        ConditionResult::from(!has_a03_e0096 && !has_a04_e0097)
6039    }
6040
6041    /// [331] Wenn im SG8 SEQ+Z98 (Daten der Marktlokation) das SG10 CCI+6++ZA9 (Aggreg. verantw. ÜNB) nicht vorhanden
6042    fn evaluate_331(&self, ctx: &EvaluationContext) -> ConditionResult {
6043        let seq_segs = ctx.find_segments("SEQ");
6044        let has_seq = seq_segs.iter().any(|s| {
6045            s.elements
6046                .first()
6047                .and_then(|e| e.first())
6048                .is_some_and(|v| v == "Z98")
6049        });
6050        let cci_segs = ctx.find_segments("CCI");
6051        let has_cci = cci_segs.iter().any(|s| {
6052            s.elements
6053                .first()
6054                .and_then(|e| e.first())
6055                .is_some_and(|v| v == "6")
6056                && s.elements
6057                    .get(2)
6058                    .and_then(|e| e.first())
6059                    .is_some_and(|v| v == "ZA9")
6060        });
6061        ConditionResult::from(!(has_seq && has_cci))
6062    }
6063
6064    /// [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
6065    // 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)
6066    fn evaluate_332(&self, ctx: &EvaluationContext) -> ConditionResult {
6067        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8 … nicht vorhanden" — compared within the enclosing SG4 when the scope
6068        // reaches an SG8; the message as before otherwise.
6069        if let Some(scope) = ctx.enclosing_scope("SG8") {
6070            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
6071            return ConditionResult::from(z01s.iter().any(|p| {
6072                !sg4_instances::sg10_at(ctx, p, |s| sg4_instances::has_cci(s, "6", "ZA9"))
6073            }));
6074        }
6075        let nav = match ctx.navigator {
6076            Some(n) => n,
6077            None => {
6078                if ctx.find_segments_with_qualifier("SEQ", 0, "Z01").is_empty() {
6079                    return ConditionResult::Unknown;
6080                }
6081                let has_cci_za9 = ctx
6082                    .find_segments_with_qualifier("CCI", 0, "6")
6083                    .iter()
6084                    .any(|s| {
6085                        s.elements
6086                            .get(2)
6087                            .and_then(|e: &Vec<String>| e.first())
6088                            .map(|v| v.as_str())
6089                            == Some("ZA9")
6090                    });
6091                return ConditionResult::from(!has_cci_za9);
6092            }
6093        };
6094        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6095        if sg8_count == 0 {
6096            return ConditionResult::Unknown;
6097        }
6098        for sg8_idx in 0..sg8_count {
6099            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], sg8_idx, "SG10");
6100            let mut has_cci_za9 = false;
6101            for sg10_idx in 0..sg10_count {
6102                let ccis = nav.find_segments_in_child_group(
6103                    "CCI",
6104                    &["SG4", "SG8"],
6105                    sg8_idx,
6106                    "SG10",
6107                    sg10_idx,
6108                );
6109                if ccis.iter().any(|s| {
6110                    s.elements
6111                        .first()
6112                        .and_then(|e: &Vec<String>| e.first())
6113                        .map(|v| v.as_str())
6114                        == Some("6")
6115                        && s.elements
6116                            .get(2)
6117                            .and_then(|e: &Vec<String>| e.first())
6118                            .map(|v| v.as_str())
6119                            == Some("ZA9")
6120                }) {
6121                    has_cci_za9 = true;
6122                    break;
6123                }
6124            }
6125            if !has_cci_za9 {
6126                return ConditionResult::True;
6127            }
6128        }
6129        ConditionResult::False
6130    }
6131
6132    /// [333] Wenn in diesem RFF \"Referenz auf die Lokationsbündelstruktur\" im DE1153 der Code Z31 \"Lokationsbündelstruktur\" vorhanden ist
6133    fn evaluate_333(&self, ctx: &EvaluationContext) -> ConditionResult {
6134        let rff_segs = ctx.find_segments("RFF");
6135        let found = rff_segs.iter().any(|s| {
6136            s.elements
6137                .first()
6138                .and_then(|e| e.first())
6139                .is_some_and(|v| v == "Z31")
6140        });
6141        ConditionResult::from(found)
6142    }
6143
6144    /// [334] Nur MP-ID aus Sparte Gas
6145    fn evaluate_334(&self, ctx: &EvaluationContext) -> ConditionResult {
6146        // Check: Marktpartner-ID is from Sparte Gas
6147        ctx.external.evaluate("sector_check")
6148    }
6149
6150    /// [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...
6151    // 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)
6152    fn evaluate_335(&self, ctx: &EvaluationContext) -> ConditionResult {
6153        // HAND-EDITED: the Zeitraum-ID of *this* SG8's SEQ+Z58 (DE1050), looked
6154        // for in the RFF+Z31 of any SG8 SEQ+Z78 of the message.
6155        let z58_ids: Vec<String> = ctx
6156            .scoped_find_segments_in_with_qualifier("SG8", "SEQ", 0, "Z58")
6157            .iter()
6158            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
6159            .filter(|v| !v.is_empty())
6160            .collect();
6161        if z58_ids.is_empty() {
6162            return ConditionResult::Unknown;
6163        }
6164        if ctx.find_segments_with_qualifier("SEQ", 0, "Z78").is_empty() {
6165            return ConditionResult::False;
6166        }
6167        let rff_z31_ids: Vec<String> = ctx
6168            .find_segments_with_qualifier("RFF", 0, "Z31")
6169            .iter()
6170            .filter_map(|rff| rff.elements.first().and_then(|e| e.get(1)).cloned())
6171            .filter(|v| !v.is_empty())
6172            .collect();
6173        ConditionResult::from(z58_ids.iter().any(|id| rff_z31_ids.contains(id)))
6174    }
6175
6176    /// [336] Wenn in Änderungsmeldung gefüllt
6177    fn evaluate_336(&self, _ctx: &EvaluationContext) -> ConditionResult {
6178        // TODO: implement
6179        ConditionResult::Unknown
6180    }
6181
6182    /// [337] Wenn SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96 vorhanden
6183    fn evaluate_337(&self, ctx: &EvaluationContext) -> ConditionResult {
6184        let cci_segs = ctx.find_segments("CCI");
6185        let has_cci = cci_segs.iter().any(|s| {
6186            s.elements
6187                .get(2)
6188                .and_then(|e| e.first())
6189                .is_some_and(|v| v == "ZB4")
6190        });
6191        let cav_segs = ctx.find_segments("CAV");
6192        let has_cav = cav_segs.iter().any(|s| {
6193            s.elements
6194                .first()
6195                .and_then(|e| e.first())
6196                .is_some_and(|v| ["Z95", "Z96"].contains(&v.as_str()))
6197        });
6198        ConditionResult::from(has_cci && has_cav)
6199    }
6200
6201    /// [338] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96/ Z97/ Z99/ ZA1/ ZA3/ ZA4/ZG7 vorhanden
6202    fn evaluate_338(&self, ctx: &EvaluationContext) -> ConditionResult {
6203        let cci_segs = ctx.find_segments("CCI");
6204        let has_cci = cci_segs.iter().any(|s| {
6205            s.elements
6206                .get(2)
6207                .and_then(|e| e.first())
6208                .is_some_and(|v| v == "ZB4")
6209        });
6210        let cav_segs = ctx.find_segments("CAV");
6211        let has_cav = cav_segs.iter().any(|s| {
6212            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6213                ["Z95", "Z96", "Z97", "Z99", "ZA1", "ZA3", "ZA4", "ZG7"].contains(&v.as_str())
6214            })
6215        });
6216        ConditionResult::from(has_cci && has_cav)
6217    }
6218
6219    /// [339] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z97/ Z98/ Z99/ ZA0/ ZA1/ ZA2/ ZA3/ ZA6/ ZG7 vorhanden
6220    fn evaluate_339(&self, ctx: &EvaluationContext) -> ConditionResult {
6221        let cci_segs = ctx.find_segments("CCI");
6222        let has_cci = cci_segs.iter().any(|s| {
6223            s.elements
6224                .get(2)
6225                .and_then(|e| e.first())
6226                .is_some_and(|v| v == "ZB4")
6227        });
6228        let cav_segs = ctx.find_segments("CAV");
6229        let has_cav = cav_segs.iter().any(|s| {
6230            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6231                [
6232                    "Z97", "Z98", "Z99", "ZA0", "ZA1", "ZA2", "ZA3", "ZA6", "ZG7",
6233                ]
6234                .contains(&v.as_str())
6235            })
6236        });
6237        ConditionResult::from(has_cci && has_cav)
6238    }
6239
6240    /// [340] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z97/ Z98/ Z99/ ZA1/ ZA2/ ZA3/ ZA4/ ZA5/ ZA6 vorhanden
6241    fn evaluate_340(&self, ctx: &EvaluationContext) -> ConditionResult {
6242        let cci_segs = ctx.find_segments("CCI");
6243        let has_cci = cci_segs.iter().any(|s| {
6244            s.elements
6245                .get(2)
6246                .and_then(|e| e.first())
6247                .is_some_and(|v| v == "ZB4")
6248        });
6249        let cav_segs = ctx.find_segments("CAV");
6250        let has_cav = cav_segs.iter().any(|s| {
6251            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6252                [
6253                    "Z95", "Z97", "Z98", "Z99", "ZA1", "ZA2", "ZA3", "ZA4", "ZA5", "ZA6",
6254                ]
6255                .contains(&v.as_str())
6256            })
6257        });
6258        ConditionResult::from(has_cci && has_cav)
6259    }
6260
6261    /// [341] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z96/ ZA0/ ZG7 vorhanden
6262    fn evaluate_341(&self, ctx: &EvaluationContext) -> ConditionResult {
6263        let cci_segs = ctx.find_segments("CCI");
6264        let has_cci = cci_segs.iter().any(|s| {
6265            s.elements
6266                .get(2)
6267                .and_then(|e| e.first())
6268                .is_some_and(|v| v == "ZB4")
6269        });
6270        let cav_segs = ctx.find_segments("CAV");
6271        let has_cav = cav_segs.iter().any(|s| {
6272            s.elements
6273                .first()
6274                .and_then(|e| e.first())
6275                .is_some_and(|v| ["Z96", "ZA0", "ZG7"].contains(&v.as_str()))
6276        });
6277        ConditionResult::from(has_cci && has_cav)
6278    }
6279
6280    /// [342] Wenn aktiver MaBiS-ZP für den Betrachtungszeitraum vorhanden
6281    fn evaluate_342(&self, _ctx: &EvaluationContext) -> ConditionResult {
6282        // TODO: implement
6283        ConditionResult::Unknown
6284    }
6285
6286    /// [344] Wenn in dieser SG8 das SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+ZF1 (Netzgangzeitreihe (NGZ)) vorhanden
6287    fn evaluate_344(&self, ctx: &EvaluationContext) -> ConditionResult {
6288        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6289        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6290        let has_cci = cci_segs.iter().any(|s| {
6291            s.elements
6292                .get(2)
6293                .and_then(|e| e.first())
6294                .is_some_and(|v| v == "ZB4")
6295        });
6296        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
6297        let has_cav = cav_segs.iter().any(|s| {
6298            s.elements
6299                .first()
6300                .and_then(|e| e.first())
6301                .is_some_and(|v| ["ZF1"].contains(&v.as_str()))
6302        });
6303        ConditionResult::from(has_cci && has_cav)
6304    }
6305
6306    /// [345] Wenn SG5 LOC+Z17 (Messlokation) vorhanden
6307    fn evaluate_345(&self, ctx: &EvaluationContext) -> ConditionResult {
6308        let loc_segs = ctx.find_segments("LOC");
6309        let found = loc_segs.iter().any(|s| {
6310            s.elements
6311                .first()
6312                .and_then(|e| e.first())
6313                .is_some_and(|v| v == "Z17")
6314        });
6315        ConditionResult::from(found)
6316    }
6317
6318    /// [346] Wenn SG10 CCI+++E13 (Zählertyp) CAV+MME (mME) vorhanden
6319    fn evaluate_346(&self, ctx: &EvaluationContext) -> ConditionResult {
6320        // HAND-EDITED: the CCI and the CAV of one SG10.
6321        sg10_has(ctx, false, |segs| {
6322            sg4_instances::has_cci(segs, "", "E13") && sg4_instances::has_cav(segs, "MME")
6323        })
6324    }
6325
6326    /// [347] Wenn SG5 LOC+Z16 (Marktlokation) nicht vorhanden
6327    fn evaluate_347(&self, ctx: &EvaluationContext) -> ConditionResult {
6328        let loc_segs = ctx.find_segments("LOC");
6329        let found = loc_segs.iter().any(|s| {
6330            s.elements
6331                .first()
6332                .and_then(|e| e.first())
6333                .is_some_and(|v| v == "Z16")
6334        });
6335        ConditionResult::from(!found)
6336    }
6337
6338    /// [348] Wenn SG5 LOC+Z21 (Tranche) nicht vorhanden
6339    fn evaluate_348(&self, ctx: &EvaluationContext) -> ConditionResult {
6340        let loc_segs = ctx.find_segments("LOC");
6341        let found = loc_segs.iter().any(|s| {
6342            s.elements
6343                .first()
6344                .and_then(|e| e.first())
6345                .is_some_and(|v| v == "Z21")
6346        });
6347        ConditionResult::from(!found)
6348    }
6349
6350    /// [349] Wenn SG5 LOC+Z17 (Messlokation) nicht vorhanden
6351    fn evaluate_349(&self, ctx: &EvaluationContext) -> ConditionResult {
6352        let loc_segs = ctx.find_segments("LOC");
6353        let found = loc_segs.iter().any(|s| {
6354            s.elements
6355                .first()
6356                .and_then(|e| e.first())
6357                .is_some_and(|v| v == "Z17")
6358        });
6359        ConditionResult::from(!found)
6360    }
6361
6362    /// [350] Wenn SG8 SEQ+Z78/ZD5 RFF+Z31 (Referenz auf die Lokationsbündelstruktur) vorhanden
6363    fn evaluate_350(&self, ctx: &EvaluationContext) -> ConditionResult {
6364        let seq_segs = ctx.find_segments("SEQ");
6365        let has_seq = seq_segs.iter().any(|s| {
6366            s.elements
6367                .first()
6368                .and_then(|e| e.first())
6369                .is_some_and(|v| ["Z78", "ZD5"].contains(&v.as_str()))
6370        });
6371        let rff_segs = ctx.find_segments("RFF");
6372        let has_rff = rff_segs.iter().any(|s| {
6373            s.elements
6374                .first()
6375                .and_then(|e| e.first())
6376                .is_some_and(|v| v == "Z31")
6377        });
6378        ConditionResult::from(has_seq && has_rff)
6379    }
6380
6381    /// [351] Wenn SG4 STS+E01++A04/ A05 /A13 /A14 (Status der Antwort) vorhanden
6382    fn evaluate_351(&self, ctx: &EvaluationContext) -> ConditionResult {
6383        let sts_segs = ctx.find_segments("STS");
6384        let found = sts_segs.iter().any(|s| {
6385            s.elements
6386                .first()
6387                .and_then(|e| e.first())
6388                .is_some_and(|v| v == "E01")
6389                && s.elements
6390                    .get(2)
6391                    .and_then(|e| e.first())
6392                    .is_some_and(|v| ["A04", "A05", "A13", "A14"].contains(&v.as_str()))
6393        });
6394        ConditionResult::from(found)
6395    }
6396
6397    /// [352] Wenn SG4 STS++A05/ A06/ A14 /A15 (Status der Antwort) vorhanden
6398    fn evaluate_352(&self, ctx: &EvaluationContext) -> ConditionResult {
6399        let sts_segs = ctx.find_segments("STS");
6400        let found = sts_segs.iter().any(|s| {
6401            s.elements
6402                .get(1)
6403                .and_then(|e| e.first())
6404                .is_some_and(|v| ["A05", "A06", "A14", "A15"].contains(&v.as_str()))
6405        });
6406        ConditionResult::from(found)
6407    }
6408
6409    /// [355] Wenn SG4 STS+E01++A45 (Status der Antwort) vorhanden
6410    fn evaluate_355(&self, ctx: &EvaluationContext) -> ConditionResult {
6411        let sts_segs = ctx.find_segments("STS");
6412        let found = sts_segs.iter().any(|s| {
6413            s.elements
6414                .first()
6415                .and_then(|e| e.first())
6416                .is_some_and(|v| v == "E01")
6417                && s.elements
6418                    .get(2)
6419                    .and_then(|e| e.first())
6420                    .is_some_and(|v| ["A45"].contains(&v.as_str()))
6421        });
6422        ConditionResult::from(found)
6423    }
6424
6425    /// [356] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6426    fn evaluate_356(&self, ctx: &EvaluationContext) -> ConditionResult {
6427        let sts_segs = ctx.find_segments("STS");
6428        let found = sts_segs.iter().any(|s| {
6429            s.elements
6430                .first()
6431                .and_then(|e| e.first())
6432                .is_some_and(|v| v == "E01")
6433                && s.elements
6434                    .get(2)
6435                    .and_then(|e| e.first())
6436                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6437        });
6438        ConditionResult::from(found)
6439    }
6440
6441    /// [357] Wenn SG4 STS+E01++A03/ A09/ A12/ A17 (Status der Antwort) vorhanden
6442    fn evaluate_357(&self, ctx: &EvaluationContext) -> ConditionResult {
6443        let sts_segs = ctx.find_segments("STS");
6444        let found = sts_segs.iter().any(|s| {
6445            s.elements
6446                .first()
6447                .and_then(|e| e.first())
6448                .is_some_and(|v| v == "E01")
6449                && s.elements
6450                    .get(2)
6451                    .and_then(|e| e.first())
6452                    .is_some_and(|v| ["A03", "A09", "A12", "A17"].contains(&v.as_str()))
6453        });
6454        ConditionResult::from(found)
6455    }
6456
6457    /// [358] Wenn SG4 STS+E01++A06 (Status der Antwort) vorhanden
6458    fn evaluate_358(&self, ctx: &EvaluationContext) -> ConditionResult {
6459        let sts_segs = ctx.find_segments("STS");
6460        let found = sts_segs.iter().any(|s| {
6461            s.elements
6462                .first()
6463                .and_then(|e| e.first())
6464                .is_some_and(|v| v == "E01")
6465                && s.elements
6466                    .get(2)
6467                    .and_then(|e| e.first())
6468                    .is_some_and(|v| ["A06"].contains(&v.as_str()))
6469        });
6470        ConditionResult::from(found)
6471    }
6472
6473    /// [359] Es sind nur Antwortcodes aus dem Cluster Ablehnung erlaubt
6474    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6475    // Preserve across regeneration until the codegen prompt is updated.
6476    fn evaluate_359(&self, ctx: &EvaluationContext) -> ConditionResult {
6477        all_sts_e01_in_cluster(ctx, Cluster::is_ablehnung)
6478    }
6479
6480    /// [360] Es sind nur Antwortcodes aus dem Cluster Zustimmung erlaubt
6481    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6482    // Preserve across regeneration until the codegen prompt is updated.
6483    fn evaluate_360(&self, ctx: &EvaluationContext) -> ConditionResult {
6484        all_sts_e01_in_cluster(ctx, Cluster::is_zustimmung)
6485    }
6486
6487    /// [363] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6488    fn evaluate_363(&self, ctx: &EvaluationContext) -> ConditionResult {
6489        let sts_segs = ctx.find_segments("STS");
6490        let found = sts_segs.iter().any(|s| {
6491            s.elements
6492                .first()
6493                .and_then(|e| e.first())
6494                .is_some_and(|v| v == "E01")
6495                && s.elements
6496                    .get(2)
6497                    .and_then(|e| e.first())
6498                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6499        });
6500        ConditionResult::from(found)
6501    }
6502
6503    /// [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
6504    fn evaluate_365(&self, ctx: &EvaluationContext) -> ConditionResult {
6505        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6506        // Check: STS+E01 does NOT have A04 with E_0047/E_0014 or A03 with E_0049/E_0004
6507        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6508        let has_excluded = sts_segs.iter().any(|s| {
6509            let code = s
6510                .elements
6511                .get(2)
6512                .and_then(|e| e.first())
6513                .map(|v| v.as_str());
6514            let ebd = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
6515            matches!(
6516                (code, ebd),
6517                (Some("A04"), Some("E_0047"))
6518                    | (Some("A04"), Some("E_0014"))
6519                    | (Some("A03"), Some("E_0049"))
6520                    | (Some("A03"), Some("E_0004"))
6521            )
6522        });
6523        ConditionResult::from(!has_excluded)
6524    }
6525
6526    /// [366] Bis auf den Code A30 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6527    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6528    // Preserve across regeneration until the codegen prompt is updated.
6529    fn evaluate_366(&self, ctx: &EvaluationContext) -> ConditionResult {
6530        ebd_ablehnung_except(ctx, "E_0624", &["A30"])
6531    }
6532
6533    /// [367] Wenn SG4 STS+E01++A05/ A14 (Status der Antwort) vorhanden
6534    fn evaluate_367(&self, ctx: &EvaluationContext) -> ConditionResult {
6535        let sts_segs = ctx.find_segments("STS");
6536        let found = sts_segs.iter().any(|s| {
6537            s.elements
6538                .first()
6539                .and_then(|e| e.first())
6540                .is_some_and(|v| v == "E01")
6541                && s.elements
6542                    .get(2)
6543                    .and_then(|e| e.first())
6544                    .is_some_and(|v| ["A05", "A14"].contains(&v.as_str()))
6545        });
6546        ConditionResult::from(found)
6547    }
6548
6549    /// [368] Bis auf den Code A41 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6550    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6551    // Preserve across regeneration until the codegen prompt is updated.
6552    fn evaluate_368(&self, ctx: &EvaluationContext) -> ConditionResult {
6553        ebd_ablehnung_except(ctx, "E_0624", &["A41"])
6554    }
6555
6556    /// [370] Wenn SG10 CAV+TLS/TES/BIT/GET/GAT/SOT/WNT/WFT/WAT
6557    fn evaluate_370(&self, ctx: &EvaluationContext) -> ConditionResult {
6558        let cav_segs = ctx.find_segments("CAV");
6559        let found = cav_segs.iter().any(|s| {
6560            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6561                [
6562                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
6563                ]
6564                .contains(&v.as_str())
6565            })
6566        });
6567        ConditionResult::from(found)
6568    }
6569
6570    /// [371] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
6571    fn evaluate_371(&self, ctx: &EvaluationContext) -> ConditionResult {
6572        let seq_segs = ctx.find_segments("SEQ");
6573        let found = seq_segs.iter().any(|s| {
6574            s.elements
6575                .first()
6576                .and_then(|e| e.first())
6577                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
6578        });
6579        ConditionResult::from(!found)
6580    }
6581
6582    /// [372] Wenn SG10 CAV+TES/BIT/GET/GAT/SOT/WNT/WFT/WAT vorhanden
6583    fn evaluate_372(&self, ctx: &EvaluationContext) -> ConditionResult {
6584        let cav_segs = ctx.find_segments("CAV");
6585        let found = cav_segs.iter().any(|s| {
6586            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6587                ["TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT"].contains(&v.as_str())
6588            })
6589        });
6590        ConditionResult::from(found)
6591    }
6592
6593    /// [373] Wenn ein Referenzprofil an der Marktlokation vorhanden ist
6594    fn evaluate_373(&self, _ctx: &EvaluationContext) -> ConditionResult {
6595        // TODO: implement
6596        ConditionResult::Unknown
6597    }
6598
6599    /// [375] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z24 (Daten der Überführungszeitreihe) nicht vorhanden
6600    fn evaluate_375(&self, ctx: &EvaluationContext) -> ConditionResult {
6601        // Check: no SG8 SEQ+Z24 (Daten der Überführungszeitreihe) exists
6602        ctx.lacks_qualifier("SEQ", 0, "Z24")
6603    }
6604
6605    /// [376] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z22 (Daten der Summenzeitreihe) nicht vorhanden
6606    fn evaluate_376(&self, ctx: &EvaluationContext) -> ConditionResult {
6607        // Check: no SG8 SEQ+Z22 (Daten der Summenzeitreihe) exists
6608        ctx.lacks_qualifier("SEQ", 0, "Z22")
6609    }
6610
6611    /// [377] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS vorhanden
6612    // 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)
6613    fn evaluate_377(&self, ctx: &EvaluationContext) -> ConditionResult {
6614        let nav = match ctx.navigator {
6615            Some(n) => n,
6616            None => return ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]),
6617        };
6618        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6619        for i in 0..sg8_count {
6620            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6621            for j in 0..sg10_count {
6622                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6623                let has_cci = ccis.iter().any(|s| {
6624                    s.elements
6625                        .first()
6626                        .and_then(|e: &Vec<String>| e.first())
6627                        .is_some_and(|v: &String| v == "15")
6628                        && s.elements
6629                            .get(2)
6630                            .and_then(|e: &Vec<String>| e.first())
6631                            .is_some_and(|v: &String| v == "Z21")
6632                });
6633                if has_cci {
6634                    let cavs =
6635                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6636                    if cavs.iter().any(|s| {
6637                        s.elements
6638                            .first()
6639                            .and_then(|e: &Vec<String>| e.first())
6640                            .is_some_and(|v: &String| v == "AUS")
6641                    }) {
6642                        return ConditionResult::True;
6643                    }
6644                }
6645            }
6646        }
6647        ConditionResult::False
6648    }
6649
6650    /// [378] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS nicht vorhanden
6651    // 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)
6652    fn evaluate_378(&self, ctx: &EvaluationContext) -> ConditionResult {
6653        let nav = match ctx.navigator {
6654            Some(n) => n,
6655            None => {
6656                let r = ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]);
6657                return match r {
6658                    ConditionResult::True => ConditionResult::False,
6659                    ConditionResult::False => ConditionResult::True,
6660                    ConditionResult::Unknown => ConditionResult::Unknown,
6661                };
6662            }
6663        };
6664        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6665        for i in 0..sg8_count {
6666            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6667            for j in 0..sg10_count {
6668                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6669                let has_cci = ccis.iter().any(|s| {
6670                    s.elements
6671                        .first()
6672                        .and_then(|e: &Vec<String>| e.first())
6673                        .is_some_and(|v: &String| v == "15")
6674                        && s.elements
6675                            .get(2)
6676                            .and_then(|e: &Vec<String>| e.first())
6677                            .is_some_and(|v: &String| v == "Z21")
6678                });
6679                if has_cci {
6680                    let cavs =
6681                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6682                    if cavs.iter().any(|s| {
6683                        s.elements
6684                            .first()
6685                            .and_then(|e: &Vec<String>| e.first())
6686                            .is_some_and(|v: &String| v == "AUS")
6687                    }) {
6688                        return ConditionResult::False;
6689                    }
6690                }
6691            }
6692        }
6693        ConditionResult::True
6694    }
6695
6696    /// [379] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+ Code für EEG-Überführungszeitreihen (ausgenommen AU1) vorhanden
6697    fn evaluate_379(&self, ctx: &EvaluationContext) -> ConditionResult {
6698        let cci_segs = ctx.find_segments("CCI");
6699        let found = cci_segs.iter().any(|s| {
6700            s.elements
6701                .first()
6702                .and_then(|e| e.first())
6703                .is_some_and(|v| v == "15")
6704                && s.elements
6705                    .get(2)
6706                    .and_then(|e| e.first())
6707                    .is_some_and(|v| ["Z21"].contains(&v.as_str()))
6708        });
6709        ConditionResult::from(found)
6710    }
6711
6712    /// [380] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS/AU1 vorhanden
6713    // 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)
6714    fn evaluate_380(&self, ctx: &EvaluationContext) -> ConditionResult {
6715        let nav = match ctx.navigator {
6716            Some(n) => n,
6717            None => {
6718                return ctx.any_group_has_any_qualifier("CAV", 0, &["AUS", "AU1"], &["SG4", "SG8"])
6719            }
6720        };
6721        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6722        for i in 0..sg8_count {
6723            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6724            for j in 0..sg10_count {
6725                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6726                let has_cci = ccis.iter().any(|s| {
6727                    s.elements
6728                        .first()
6729                        .and_then(|e: &Vec<String>| e.first())
6730                        .is_some_and(|v: &String| v == "15")
6731                        && s.elements
6732                            .get(2)
6733                            .and_then(|e: &Vec<String>| e.first())
6734                            .is_some_and(|v: &String| v == "Z21")
6735                });
6736                if has_cci {
6737                    let cavs =
6738                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6739                    if cavs.iter().any(|s| {
6740                        s.elements
6741                            .first()
6742                            .and_then(|e: &Vec<String>| e.first())
6743                            .is_some_and(|v: &String| v == "AUS" || v == "AU1")
6744                    }) {
6745                        return ConditionResult::True;
6746                    }
6747                }
6748            }
6749        }
6750        ConditionResult::False
6751    }
6752
6753    /// [384] Wenn in derselben SG8 SG10 CCI+Z37++ZD1 (Basis zur Bildung der Tranchengröße) (Prozentual) vorhanden
6754    fn evaluate_384(&self, ctx: &EvaluationContext) -> ConditionResult {
6755        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6756        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6757        let found = cci_segs.iter().any(|s| {
6758            s.elements
6759                .first()
6760                .and_then(|e| e.first())
6761                .is_some_and(|v| v == "Z37")
6762                && s.elements
6763                    .get(2)
6764                    .and_then(|e| e.first())
6765                    .is_some_and(|v| v == "ZD1")
6766        });
6767        ConditionResult::from(found)
6768    }
6769
6770    /// [386] Wenn mehr als eine SG8 SEQ+Z02/ ZE3 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden
6771    fn evaluate_386(&self, ctx: &EvaluationContext) -> ConditionResult {
6772        // Check: more than one SG8 SEQ+Z02/ZE3 with OBIS energy quantity in PIA+5
6773        let seq_count = ctx
6774            .find_segments("SEQ")
6775            .iter()
6776            .filter(|s| {
6777                s.elements
6778                    .first()
6779                    .and_then(|e| e.first())
6780                    .is_some_and(|v| v == "Z02" || v == "ZE3")
6781            })
6782            .count();
6783        ConditionResult::from(seq_count > 1)
6784    }
6785
6786    /// [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...
6787    fn evaluate_387(&self, ctx: &EvaluationContext) -> ConditionResult {
6788        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6789        // Check: same SG8 (OBIS Daten) has OBIS energy quantity code in PIA+5
6790        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6791        let found = pia_segs.iter().any(|s| {
6792            s.elements
6793                .first()
6794                .and_then(|e| e.first())
6795                .is_some_and(|v| v == "5")
6796                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6797                    v.starts_with("1-")
6798                        && (v.contains(":1.8.")
6799                            || v.contains(":2.8.")
6800                            || v.contains(":1.9.")
6801                            || v.contains(":2.9."))
6802                })
6803        });
6804        ConditionResult::from(found)
6805    }
6806
6807    /// [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...
6808    fn evaluate_388(&self, ctx: &EvaluationContext) -> ConditionResult {
6809        // Check: at least one RFF+Z49 (Zeitraum) exists
6810        let found = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6811        ConditionResult::from(found)
6812    }
6813
6814    /// [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...
6815    fn evaluate_391(&self, ctx: &EvaluationContext) -> ConditionResult {
6816        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6817        // Check: OBIS Zählerstand (meter reading) code in PIA+5 within SEQ+Z20
6818        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6819        let found = pia_segs.iter().any(|s| {
6820            s.elements
6821                .first()
6822                .and_then(|e| e.first())
6823                .is_some_and(|v| v == "5")
6824                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6825                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
6826                })
6827        });
6828        ConditionResult::from(found)
6829    }
6830
6831    /// [392] Wenn in diesem PIA der Code im Format n1-n2-n1 angegeben ist
6832    fn evaluate_392(&self, ctx: &EvaluationContext) -> ConditionResult {
6833        // Check: PIA code in format n1-n2-n1 (e.g., "1-08-1")
6834        let pia_segs = ctx.find_segments("PIA");
6835        let found = pia_segs.iter().any(|s| {
6836            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6837                let parts: Vec<&str> = v.split('-').collect();
6838                parts.len() == 3
6839                    && parts[0].len() == 1
6840                    && parts[0].chars().all(|c| c.is_ascii_digit())
6841                    && parts[1].len() == 2
6842                    && parts[1].chars().all(|c| c.is_ascii_digit())
6843                    && parts[2].len() == 1
6844                    && parts[2].chars().all(|c| c.is_ascii_digit())
6845            })
6846        });
6847        ConditionResult::from(found)
6848    }
6849
6850    /// [393] Wenn in diesem PIA der Code im Format n1-n2-n1-n3 angegeben ist
6851    fn evaluate_393(&self, ctx: &EvaluationContext) -> ConditionResult {
6852        // Check: PIA code in format n1-n2-n1-n3 (e.g., "1-08-1-001")
6853        let pia_segs = ctx.find_segments("PIA");
6854        let found = pia_segs.iter().any(|s| {
6855            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6856                let parts: Vec<&str> = v.split('-').collect();
6857                parts.len() == 4
6858                    && parts[0].len() == 1
6859                    && parts[0].chars().all(|c| c.is_ascii_digit())
6860                    && parts[1].len() == 2
6861                    && parts[1].chars().all(|c| c.is_ascii_digit())
6862                    && parts[2].len() == 1
6863                    && parts[2].chars().all(|c| c.is_ascii_digit())
6864                    && parts[3].len() == 3
6865                    && parts[3].chars().all(|c| c.is_ascii_digit())
6866            })
6867        });
6868        ConditionResult::from(found)
6869    }
6870
6871    /// [394] Wenn in diesem PIA der Code im Format n1-n2-n1-n8-n2 angegeben ist
6872    fn evaluate_394(&self, ctx: &EvaluationContext) -> ConditionResult {
6873        // Check: PIA code in format n1-n2-n1-n8-n2
6874        let pia_segs = ctx.find_segments("PIA");
6875        let found = pia_segs.iter().any(|s| {
6876            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6877                let parts: Vec<&str> = v.split('-').collect();
6878                parts.len() == 5
6879                    && parts[0].len() == 1
6880                    && parts[0].chars().all(|c| c.is_ascii_digit())
6881                    && parts[1].len() == 2
6882                    && parts[1].chars().all(|c| c.is_ascii_digit())
6883                    && parts[2].len() == 1
6884                    && parts[2].chars().all(|c| c.is_ascii_digit())
6885                    && parts[3].len() == 8
6886                    && parts[3].chars().all(|c| c.is_ascii_digit())
6887                    && parts[4].len() == 2
6888                    && parts[4].chars().all(|c| c.is_ascii_digit())
6889            })
6890        });
6891        ConditionResult::from(found)
6892    }
6893
6894    /// [395] Wenn in diesem PIA der Code im Format n1-n2-n1-n8 angegeben ist
6895    fn evaluate_395(&self, ctx: &EvaluationContext) -> ConditionResult {
6896        // Check: PIA code in format n1-n2-n1-n8
6897        let pia_segs = ctx.find_segments("PIA");
6898        let found = pia_segs.iter().any(|s| {
6899            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6900                let parts: Vec<&str> = v.split('-').collect();
6901                parts.len() == 4
6902                    && parts[0].len() == 1
6903                    && parts[0].chars().all(|c| c.is_ascii_digit())
6904                    && parts[1].len() == 2
6905                    && parts[1].chars().all(|c| c.is_ascii_digit())
6906                    && parts[2].len() == 1
6907                    && parts[2].chars().all(|c| c.is_ascii_digit())
6908                    && parts[3].len() == 8
6909                    && parts[3].chars().all(|c| c.is_ascii_digit())
6910            })
6911        });
6912        ConditionResult::from(found)
6913    }
6914
6915    /// [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
6916    fn evaluate_396(&self, ctx: &EvaluationContext) -> ConditionResult {
6917        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6918        // Check: OBIS Wirkarbeit und 1/4 Stunde code in PIA+5
6919        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6920        let found = pia_segs.iter().any(|s| {
6921            s.elements
6922                .first()
6923                .and_then(|e| e.first())
6924                .is_some_and(|v| v == "5")
6925                && s.elements
6926                    .get(1)
6927                    .and_then(|e| e.first())
6928                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
6929        });
6930        ConditionResult::from(found)
6931    }
6932
6933    /// [397] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/1-08-4 beginnt
6934    fn evaluate_397(&self, ctx: &EvaluationContext) -> ConditionResult {
6935        let pia_segs = ctx.find_segments("PIA");
6936        let found = pia_segs.iter().any(|s| {
6937            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6938                let prefixes = ["1-08-1", "1-08-4"];
6939                prefixes.iter().any(|p| v.starts_with(p))
6940            })
6941        });
6942        ConditionResult::from(found)
6943    }
6944
6945    /// [398] Wenn in diesem PIA der Code im DE7140 mit 1-08-2/1-08-5 beginnt
6946    fn evaluate_398(&self, ctx: &EvaluationContext) -> ConditionResult {
6947        let pia_segs = ctx.find_segments("PIA");
6948        let found = pia_segs.iter().any(|s| {
6949            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6950                let prefixes = ["1-08-2", "1-08-5"];
6951                prefixes.iter().any(|p| v.starts_with(p))
6952            })
6953        });
6954        ConditionResult::from(found)
6955    }
6956
6957    /// [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...
6958    // 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)
6959    fn evaluate_399(&self, ctx: &EvaluationContext) -> ConditionResult {
6960        let nav = match ctx.navigator() {
6961            Some(n) => n,
6962            None => return ConditionResult::Unknown,
6963        };
6964        // Collect DE3224 values from LOC segments in SG5 — LOC.C517.DE3224 = elements[1][3]
6965        // These serve as the Zeitraum-IDs referenced by the condition's "dieses Segments"
6966        let sg5_count = nav.group_instance_count(&["SG4", "SG5"]);
6967        let mut loc_zeitraum_ids: Vec<String> = Vec::new();
6968        for i in 0..sg5_count {
6969            let locs = nav.find_segments_in_group("LOC", &["SG4", "SG5"], i);
6970            for loc in &locs {
6971                if let Some(zid) = loc.elements.get(1).and_then(|e| e.get(3)) {
6972                    if !zid.is_empty() {
6973                        loc_zeitraum_ids.push(zid.clone());
6974                    }
6975                }
6976            }
6977        }
6978        if loc_zeitraum_ids.is_empty() {
6979            return ConditionResult::Unknown;
6980        }
6981        let pauschal_id = "9992000000018";
6982        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6983        // Find any Z78 SG8 whose Zeitraum-ID (SEQ.C286.DE1050) matches a LOC DE3224
6984        // and that has RFF+Z31 with DE1154 != pauschal_id
6985        for i in 0..sg8_count {
6986            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
6987            for seq in &seqs {
6988                if !seq
6989                    .elements
6990                    .first()
6991                    .and_then(|e| e.first())
6992                    .is_some_and(|v| v == "Z78")
6993                {
6994                    continue;
6995                }
6996                let zid = seq
6997                    .elements
6998                    .get(1)
6999                    .and_then(|e| e.first())
7000                    .map(|s| s.as_str())
7001                    .unwrap_or("");
7002                if !loc_zeitraum_ids.iter().any(|z| z == zid) {
7003                    continue;
7004                }
7005                // Matching Z78 SG8 found — check RFF+Z31 DE1154
7006                let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
7007                for rff in &rffs {
7008                    let qual = rff
7009                        .elements
7010                        .first()
7011                        .and_then(|e| e.first())
7012                        .map(|v| v.as_str())
7013                        .unwrap_or("");
7014                    if qual == "Z31" {
7015                        let id = rff
7016                            .elements
7017                            .first()
7018                            .and_then(|e| e.get(1))
7019                            .map(|v| v.as_str())
7020                            .unwrap_or("");
7021                        let normalized = id.replace(' ', "");
7022                        if !normalized.is_empty() && normalized != pauschal_id {
7023                            return ConditionResult::True;
7024                        }
7025                    }
7026                }
7027            }
7028        }
7029        ConditionResult::False
7030    }
7031
7032    /// [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+...
7033    fn evaluate_401(&self, ctx: &EvaluationContext) -> ConditionResult {
7034        // Check: this DTM+Z25 is not in SG6 RFF+Z49 or Z53
7035        // Approximate: check if RFF+Z49 or Z53 exists
7036        let has_z49 = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
7037        let has_z53 = !ctx.find_segments_with_qualifier("RFF", 0, "Z53").is_empty();
7038        ConditionResult::from(!has_z49 && !has_z53)
7039    }
7040
7041    /// [402] Wenn in derselben SG8  (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl der Werteart \"Zählerstand\" im PIA+5 vorhanden
7042    fn evaluate_402(&self, ctx: &EvaluationContext) -> ConditionResult {
7043        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
7044        // Check: OBIS Zählerstand code in PIA+5
7045        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7046        let found = pia_segs.iter().any(|s| {
7047            s.elements
7048                .first()
7049                .and_then(|e| e.first())
7050                .is_some_and(|v| v == "5")
7051                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7052                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
7053                })
7054        });
7055        ConditionResult::from(found)
7056    }
7057
7058    /// [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
7059    // 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)
7060    fn evaluate_403(&self, ctx: &EvaluationContext) -> ConditionResult {
7061        let sender_mp_id: String = match ctx.find_segments_with_qualifier("NAD", 0, "MS").first() {
7062            Some(nad) => match nad.elements.get(1).and_then(|e: &Vec<String>| e.first()) {
7063                Some(id) if !id.is_empty() => id.clone(),
7064                _ => return ConditionResult::Unknown,
7065            },
7066            None => return ConditionResult::Unknown,
7067        };
7068        let nav = match ctx.navigator {
7069            Some(n) => n,
7070            None => return ConditionResult::Unknown,
7071        };
7072        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7073        for i in 0..sg8_count {
7074            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7075            for j in 0..sg10_count {
7076                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7077                let has_cci_zb3 = ccis.iter().any(|s| {
7078                    s.elements
7079                        .get(2)
7080                        .and_then(|e: &Vec<String>| e.first())
7081                        .is_some_and(|v: &String| v == "ZB3")
7082                });
7083                if !has_cci_zb3 {
7084                    continue;
7085                }
7086                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
7087                for cav in &cavs {
7088                    if cav
7089                        .elements
7090                        .first()
7091                        .and_then(|e: &Vec<String>| e.first())
7092                        .is_some_and(|v: &String| v == "Z91")
7093                    {
7094                        if let Some(msb_id) = cav.elements.first().and_then(|e| e.get(1)) {
7095                            if !msb_id.is_empty() {
7096                                return ConditionResult::from(
7097                                    sender_mp_id.as_str() != msb_id.as_str(),
7098                                );
7099                            }
7100                        }
7101                    }
7102                }
7103            }
7104        }
7105        ConditionResult::Unknown
7106    }
7107
7108    /// [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
7109    // 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)
7110    fn evaluate_404(&self, ctx: &EvaluationContext) -> ConditionResult {
7111        let mr_nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
7112        let mr_mp_id = match mr_nads.first() {
7113            Some(nad) => nad
7114                .elements
7115                .get(1)
7116                .and_then(|e: &Vec<String>| e.first())
7117                .cloned()
7118                .unwrap_or_default(),
7119            None => return ConditionResult::Unknown,
7120        };
7121        if mr_mp_id.is_empty() {
7122            return ConditionResult::Unknown;
7123        }
7124        // HAND-EDITED: "im SG8 SEQ+Z01/ Z98 … SG10 CCI+++ZB3 CAV+Z91" — the MSB in
7125        // the Marktlokation data of this SG4, of the MaLo this SG8 refers to
7126        // (RFF+Z18) where it refers to one, when the scope reaches an SG8. Any
7127        // SG8 of the message (the MeLo's SEQ+Z18 names the recipient by design)
7128        // otherwise, as before.
7129        if let Some(scope) = ctx.enclosing_scope("SG8") {
7130            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
7131            let names_recipient = |s: &[OwnedSegment]| {
7132                sg4_instances::has_cci(s, "", "ZB3")
7133                    && s.iter().any(|c| {
7134                        c.id == "CAV"
7135                            && c.get_element(0) == "Z91"
7136                            && c.get_component(0, 1) == mr_mp_id
7137                    })
7138            };
7139            let same = sg4_instances::sg8s(ctx).iter().any(|p| {
7140                sg4_instances::seq_at(ctx, p)
7141                    .is_some_and(|(code, _)| code == "Z01" || code == "Z98")
7142                    && (refs.is_empty()
7143                        || sg4_instances::rff_values_at(ctx, p, "Z18")
7144                            .iter()
7145                            .any(|v| refs.contains(v)))
7146                    && sg4_instances::sg10_at(ctx, p, names_recipient)
7147            });
7148            return ConditionResult::from(!same);
7149        }
7150
7151        let nav = match ctx.navigator {
7152            Some(n) => n,
7153            None => {
7154                let cavs = ctx.find_segments("CAV");
7155                let same_id_exists = cavs.iter().any(|s| {
7156                    s.elements
7157                        .first()
7158                        .and_then(|e: &Vec<String>| e.first())
7159                        .is_some_and(|v: &String| v == "Z91")
7160                        && s.elements
7161                            .first()
7162                            .and_then(|e| e.get(1))
7163                            .is_some_and(|v: &String| v == &mr_mp_id)
7164                });
7165                return ConditionResult::from(!same_id_exists);
7166            }
7167        };
7168
7169        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7170        for i in 0..sg8_count {
7171            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7172            for j in 0..sg10_count {
7173                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7174                let has_cci_zb3 = ccis.iter().any(|s| {
7175                    s.elements
7176                        .get(2)
7177                        .and_then(|e: &Vec<String>| e.first())
7178                        .is_some_and(|v: &String| v == "ZB3")
7179                });
7180                if !has_cci_zb3 {
7181                    continue;
7182                }
7183                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
7184                for cav in &cavs {
7185                    if cav
7186                        .elements
7187                        .first()
7188                        .and_then(|e: &Vec<String>| e.first())
7189                        .is_some_and(|v: &String| v == "Z91")
7190                    {
7191                        let cav_mp_id = cav
7192                            .elements
7193                            .first()
7194                            .and_then(|e| e.get(1))
7195                            .map(|s| s.as_str())
7196                            .unwrap_or("");
7197                        if cav_mp_id == mr_mp_id.as_str() {
7198                            return ConditionResult::False;
7199                        }
7200                    }
7201                }
7202            }
7203        }
7204
7205        ConditionResult::True
7206    }
7207
7208    /// [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
7209    fn evaluate_405(&self, ctx: &EvaluationContext) -> ConditionResult {
7210        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7211        // Check: OBIS Wirkarbeit kumuliert code in PIA+5
7212        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7213        let found = pia_segs.iter().any(|s| {
7214            s.elements
7215                .first()
7216                .and_then(|e| e.first())
7217                .is_some_and(|v| v == "5")
7218                && s.elements
7219                    .get(1)
7220                    .and_then(|e| e.first())
7221                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7222        });
7223        ConditionResult::from(found)
7224    }
7225
7226    /// [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
7227    fn evaluate_406(&self, ctx: &EvaluationContext) -> ConditionResult {
7228        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7229        // Check: OBIS Wirkarbeit höchste 1/4 Stunde code in PIA+5
7230        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7231        let found = pia_segs.iter().any(|s| {
7232            s.elements
7233                .first()
7234                .and_then(|e| e.first())
7235                .is_some_and(|v| v == "5")
7236                && s.elements
7237                    .get(1)
7238                    .and_then(|e| e.first())
7239                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7240        });
7241        ConditionResult::from(found)
7242    }
7243
7244    /// [407] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Blindarbeit im PIA+5 vorhanden
7245    fn evaluate_407(&self, ctx: &EvaluationContext) -> ConditionResult {
7246        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7247        // Check: OBIS Blindarbeit code in PIA+5
7248        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7249        let found = pia_segs.iter().any(|s| {
7250            s.elements
7251                .first()
7252                .and_then(|e| e.first())
7253                .is_some_and(|v| v == "5")
7254                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7255                    v.starts_with("1-") && (v.contains(":3.8.") || v.contains(":4.8."))
7256                })
7257        });
7258        ConditionResult::from(found)
7259    }
7260
7261    /// [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...
7262    fn evaluate_408(&self, ctx: &EvaluationContext) -> ConditionResult {
7263        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7264        // reaches an SG8; the message as before otherwise.
7265        if let Some(scope) = ctx.enclosing_scope("SG8") {
7266            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7267                ctx,
7268                scope,
7269                is_obis_wirkarbeit_kumuliert,
7270            ));
7271        }
7272        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7273        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7274        ConditionResult::from(has_seq_z02)
7275    }
7276
7277    /// [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 ...
7278    fn evaluate_409(&self, ctx: &EvaluationContext) -> ConditionResult {
7279        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7280        // reaches an SG8; the message as before otherwise.
7281        if let Some(scope) = ctx.enclosing_scope("SG8") {
7282            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7283                ctx,
7284                scope,
7285                is_obis_wirkarbeit_kumuliert,
7286            ));
7287        }
7288        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7289        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7290        ConditionResult::from(!has_seq_z02)
7291    }
7292
7293    /// [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...
7294    fn evaluate_410(&self, ctx: &EvaluationContext) -> ConditionResult {
7295        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7296        // reaches an SG8; the message as before otherwise.
7297        if let Some(scope) = ctx.enclosing_scope("SG8") {
7298            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7299                ctx,
7300                scope,
7301                is_obis_wirkarbeit_quarter_hour,
7302            ));
7303        }
7304        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7305        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7306        ConditionResult::from(has_seq_z02)
7307    }
7308
7309    /// [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 ...
7310    fn evaluate_411(&self, ctx: &EvaluationContext) -> ConditionResult {
7311        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7312        // reaches an SG8; the message as before otherwise.
7313        if let Some(scope) = ctx.enclosing_scope("SG8") {
7314            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7315                ctx,
7316                scope,
7317                is_obis_wirkarbeit_quarter_hour,
7318            ));
7319        }
7320        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7321        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7322        ConditionResult::from(!has_seq_z02)
7323    }
7324
7325    /// [412] Es sind ausschließlich Konfigurationsprodukt-Codes der \"EDI@Energy Codeliste der Konfigurationen\" aus Kapitel 4.2 Konfigurationsprodukte Leistungskurvendefinition erlaubt
7326    fn evaluate_412(&self, ctx: &EvaluationContext) -> ConditionResult {
7327        ctx.external.evaluate("code_list_membership_check")
7328    }
7329
7330    /// [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
7331    fn evaluate_413(&self, ctx: &EvaluationContext) -> ConditionResult {
7332        // Check: SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7333        let found = ctx.find_segments("SEQ").iter().any(|s| {
7334            s.elements
7335                .first()
7336                .and_then(|e| e.first())
7337                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7338        });
7339        ConditionResult::from(found)
7340    }
7341
7342    /// [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
7343    fn evaluate_414(&self, ctx: &EvaluationContext) -> ConditionResult {
7344        // Check: no SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7345        let found = ctx.find_segments("SEQ").iter().any(|s| {
7346            s.elements
7347                .first()
7348                .and_then(|e| e.first())
7349                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7350        });
7351        ConditionResult::from(!found)
7352    }
7353
7354    /// [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
7355    fn evaluate_415(&self, ctx: &EvaluationContext) -> ConditionResult {
7356        // Check: another SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7357        let count = ctx
7358            .find_segments("SEQ")
7359            .iter()
7360            .filter(|s| {
7361                s.elements
7362                    .first()
7363                    .and_then(|e| e.first())
7364                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7365            })
7366            .count();
7367        ConditionResult::from(count > 1)
7368    }
7369
7370    /// [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
7371    fn evaluate_416(&self, ctx: &EvaluationContext) -> ConditionResult {
7372        // Check: no other SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7373        let count = ctx
7374            .find_segments("SEQ")
7375            .iter()
7376            .filter(|s| {
7377                s.elements
7378                    .first()
7379                    .and_then(|e| e.first())
7380                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7381            })
7382            .count();
7383        ConditionResult::from(count <= 1)
7384    }
7385
7386    /// [417] Wenn für den erforderlichen Wert keine Zählzeit benötigt wird
7387    fn evaluate_417(&self, _ctx: &EvaluationContext) -> ConditionResult {
7388        // TODO: implement
7389        ConditionResult::Unknown
7390    }
7391
7392    /// [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
7393    fn evaluate_419(&self, _ctx: &EvaluationContext) -> ConditionResult {
7394        // TODO: implement
7395        ConditionResult::Unknown
7396    }
7397
7398    /// [420] Wenn in dieser SG8 das RFF+Z14 (Smartmeter-Gateway) vorhanden ist
7399    fn evaluate_420(&self, ctx: &EvaluationContext) -> ConditionResult {
7400        // HAND-EDITED: "in dieser SG8" — scoped to the enclosing SG8.
7401        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7402        let found = rff_segs.iter().any(|s| {
7403            s.elements
7404                .first()
7405                .and_then(|e| e.first())
7406                .is_some_and(|v| v == "Z14")
7407        });
7408        ConditionResult::from(found)
7409    }
7410
7411    /// [421] Wenn in dieser SG8 das SG10 CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden ist
7412    fn evaluate_421(&self, ctx: &EvaluationContext) -> ConditionResult {
7413        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing group.
7414        // Check: CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden
7415        ctx.this_group_has_qualifier("CCI", 0, "Z39", &["SG4", "SG8"])
7416    }
7417
7418    /// [425] Messprodukt-Code aus den Kapitel 2.1 \"Standard-Messprodukte der Marktlokation\" der Codeliste der Konfigurationen
7419    fn evaluate_425(&self, ctx: &EvaluationContext) -> ConditionResult {
7420        ctx.external.evaluate("code_list_membership_check")
7421    }
7422
7423    /// [426] Messprodukt-Code aus den Kapitel 2.2 \"Standard-Messprodukte der Tranche\" der Codeliste der Konfigurationen
7424    fn evaluate_426(&self, ctx: &EvaluationContext) -> ConditionResult {
7425        ctx.external.evaluate("code_list_membership_check")
7426    }
7427
7428    /// [427] Messprodukt-Code aus den Kapitel 2.3 \"Standard-Messprodukte der Messlokation\" der Codeliste der Konfigurationen
7429    fn evaluate_427(&self, ctx: &EvaluationContext) -> ConditionResult {
7430        ctx.external.evaluate("code_list_membership_check")
7431    }
7432
7433    /// [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...
7434    fn evaluate_428(&self, ctx: &EvaluationContext) -> ConditionResult {
7435        ctx.external.evaluate("code_list_membership_check")
7436    }
7437
7438    /// [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...
7439    fn evaluate_429(&self, ctx: &EvaluationContext) -> ConditionResult {
7440        ctx.external.evaluate("code_list_membership_check")
7441    }
7442
7443    /// [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...
7444    fn evaluate_430(&self, ctx: &EvaluationContext) -> ConditionResult {
7445        // Check: another SG8 SEQ+Z27 exists with matching RFF+Z18
7446        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7447        ConditionResult::from(seq_z27_count > 1)
7448    }
7449
7450    /// [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+...
7451    fn evaluate_431(&self, ctx: &EvaluationContext) -> ConditionResult {
7452        // Check: no other SG8 SEQ+Z27 exists with matching RFF+Z18
7453        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7454        ConditionResult::from(seq_z27_count <= 1)
7455    }
7456
7457    /// [432] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
7458    fn evaluate_432(&self, ctx: &EvaluationContext) -> ConditionResult {
7459        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7460        // Check: PIA+5 with code 9991000000078 (Wirkarbeit Lastgang) exists
7461        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7462        let found = pia_segs.iter().any(|s| {
7463            s.elements
7464                .first()
7465                .and_then(|e| e.first())
7466                .is_some_and(|v| v == "5")
7467                && s.elements
7468                    .get(1)
7469                    .and_then(|e| e.first())
7470                    .is_some_and(|v| v == "9991000000078")
7471        });
7472        ConditionResult::from(found)
7473    }
7474
7475    /// [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 ...
7476    fn evaluate_433(&self, ctx: &EvaluationContext) -> ConditionResult {
7477        ctx.external.evaluate("code_list_membership_check")
7478    }
7479
7480    /// [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...
7481    fn evaluate_435(&self, ctx: &EvaluationContext) -> ConditionResult {
7482        ctx.external.evaluate("code_list_membership_check")
7483    }
7484
7485    /// [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
7486    fn evaluate_436(&self, ctx: &EvaluationContext) -> ConditionResult {
7487        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7488        // Check: PIA+5 with code 9991000000086 (Wirkarbeit höchste 1/4h Leistung) exists
7489        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7490        let found = pia_segs.iter().any(|s| {
7491            s.elements
7492                .first()
7493                .and_then(|e| e.first())
7494                .is_some_and(|v| v == "5")
7495                && s.elements
7496                    .get(1)
7497                    .and_then(|e| e.first())
7498                    .is_some_and(|v| v == "9991000000086")
7499        });
7500        ConditionResult::from(found)
7501    }
7502
7503    /// [437] Wenn in dieser SG4 das STS+E01++A04 / A23 (Status der Antwort) vorhanden
7504    fn evaluate_437(&self, ctx: &EvaluationContext) -> ConditionResult {
7505        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
7506        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
7507        let found = sts_segs.iter().any(|s| {
7508            s.elements
7509                .first()
7510                .and_then(|e| e.first())
7511                .is_some_and(|v| v == "E01")
7512                && s.elements
7513                    .get(2)
7514                    .and_then(|e| e.first())
7515                    .is_some_and(|v| ["A04", "A23"].contains(&v.as_str()))
7516        });
7517        ConditionResult::from(found)
7518    }
7519
7520    /// [438] Wenn im selben CCI im DE7059 der Code Z39 (Code der Zählzeit) vorhanden ist
7521    fn evaluate_438(&self, ctx: &EvaluationContext) -> ConditionResult {
7522        // HAND-EDITED: "im selben CCI" — the CCI the field is in (its own segment
7523        // in tree validation), else the enclosing SG10's CCI.
7524        let is_z39 = |elements: &[Vec<String>]| {
7525            elements
7526                .first()
7527                .and_then(|e| e.first())
7528                .is_some_and(|v| v == "Z39")
7529        };
7530        if let Some(own) = ctx.resolved_segment.filter(|_| {
7531            ctx.scoped_find_segments_in("SG10", "CCI")
7532                .iter()
7533                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
7534        }) {
7535            return ConditionResult::from(is_z39(own));
7536        }
7537        ConditionResult::from(
7538            ctx.scoped_find_segments_in("SG10", "CCI")
7539                .iter()
7540                .any(|s| is_z39(&s.elements)),
7541        )
7542    }
7543
7544    /// [440] Wenn in demselben RFF der Code Z35 (vorgelagerte Netzlokation) im DE1153 vorhanden
7545    fn evaluate_440(&self, ctx: &EvaluationContext) -> ConditionResult {
7546        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7547        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7548        // alternative of the `⊻` held and the XOR failed on each RFF).
7549        same_rff_qualifier(ctx, "Z35")
7550    }
7551
7552    /// [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
7553    fn evaluate_441(&self, ctx: &EvaluationContext) -> ConditionResult {
7554        // Check: SG8 SEQ+Z03/ZF5 device without RFF+Z14 (SMGW reference)
7555        ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
7556    }
7557
7558    /// [442] Wenn in demselben RFF der Code Z34 (vorgelagerte Messlokation) im DE1153 vorhanden
7559    fn evaluate_442(&self, ctx: &EvaluationContext) -> ConditionResult {
7560        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7561        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7562        // alternative of the `⊻` held and the XOR failed on each RFF).
7563        same_rff_qualifier(ctx, "Z34")
7564    }
7565
7566    /// [444] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das PIA+5 (Produkt-Daten der Netzlokation) nicht vorhanden
7567    // 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)
7568    fn evaluate_444(&self, ctx: &EvaluationContext) -> ConditionResult {
7569        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7570        // scope reaches one; the message as before otherwise.
7571        if let Some(scope) = ctx.enclosing_scope("SG8") {
7572            return ConditionResult::from(
7573                ctx.find_segments_at(scope, "SEQ")
7574                    .iter()
7575                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7576                    && !ctx
7577                        .find_segments_at(scope, "PIA")
7578                        .iter()
7579                        .any(|s| s.get_element(0) == "5"),
7580            );
7581        }
7582        for code in ["Z60", "ZE0", "ZG8", "ZG9"] {
7583            if matches!(
7584                ctx.any_group_has_qualifier_without("SEQ", 0, code, "PIA", 0, "5", &["SG4", "SG8"]),
7585                ConditionResult::True
7586            ) {
7587                return ConditionResult::True;
7588            }
7589        }
7590        ConditionResult::False
7591    }
7592
7593    /// [445] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das SG10 CCI+11 (Details zum Produkt der Netzlokation) nicht vorhanden
7594    // 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)
7595    fn evaluate_445(&self, ctx: &EvaluationContext) -> ConditionResult {
7596        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7597        // scope reaches one; the message as before otherwise.
7598        if let Some(scope) = ctx.enclosing_scope("SG8") {
7599            return ConditionResult::from(
7600                ctx.find_segments_at(scope, "SEQ")
7601                    .iter()
7602                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7603                    && !ctx
7604                        .find_segments_at(scope, "CCI")
7605                        .iter()
7606                        .any(|s| s.get_element(0) == "11"),
7607            );
7608        }
7609        let nav = match ctx.navigator {
7610            Some(n) => n,
7611            None => {
7612                let has_cci11 = ["Z60", "ZE0", "ZG8", "ZG9"].iter().any(|code| {
7613                    matches!(
7614                        ctx.filtered_parent_child_has_qualifier(
7615                            &["SG4", "SG8"],
7616                            "SEQ",
7617                            0,
7618                            code,
7619                            "SG10",
7620                            "CCI",
7621                            0,
7622                            "11",
7623                        ),
7624                        ConditionResult::True
7625                    )
7626                });
7627                return ConditionResult::from(!has_cci11);
7628            }
7629        };
7630
7631        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7632        for i in 0..sg8_count {
7633            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7634            let has_cci11 = (0..sg10_count).any(|j| {
7635                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7636                ccis.iter().any(|s| {
7637                    s.elements
7638                        .first()
7639                        .and_then(|e: &Vec<String>| e.first())
7640                        .is_some_and(|v: &String| v == "11")
7641                })
7642            });
7643            if !has_cci11 {
7644                return ConditionResult::True;
7645            }
7646        }
7647        ConditionResult::False
7648    }
7649
7650    /// [446] Wenn in derselben SG8 das CCI+Z17 (Stromverbrauchsart) vorhanden
7651    fn evaluate_446(&self, ctx: &EvaluationContext) -> ConditionResult {
7652        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7653        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7654        let found = cci_segs.iter().any(|s| {
7655            s.elements
7656                .first()
7657                .and_then(|e| e.first())
7658                .is_some_and(|v| v == "Z17")
7659        });
7660        ConditionResult::from(found)
7661    }
7662
7663    /// [447] Wenn in derselben SG8 das CCI+Z50 (Stromerzeugungsart) vorhanden
7664    fn evaluate_447(&self, ctx: &EvaluationContext) -> ConditionResult {
7665        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7666        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7667        let found = cci_segs.iter().any(|s| {
7668            s.elements
7669                .first()
7670                .and_then(|e| e.first())
7671                .is_some_and(|v| v == "Z50")
7672        });
7673        ConditionResult::from(found)
7674    }
7675
7676    /// [448] Wenn in derselben SG8 das CCI+Z56 (Speicher) vorhanden
7677    fn evaluate_448(&self, ctx: &EvaluationContext) -> ConditionResult {
7678        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7679        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7680        let found = cci_segs.iter().any(|s| {
7681            s.elements
7682                .first()
7683                .and_then(|e| e.first())
7684                .is_some_and(|v| v == "Z56")
7685        });
7686        ConditionResult::from(found)
7687    }
7688
7689    /// [449] Wenn in derselben SG8 das SG10 CCI+6++ZA9 CAV=ZG3 (Aggreg.verantw. ÜNB und Lokation im Regelbetrieb) vorhanden
7690    fn evaluate_449(&self, ctx: &EvaluationContext) -> ConditionResult {
7691        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7692        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7693        let has_cci = cci_segs.iter().any(|s| {
7694            s.elements
7695                .first()
7696                .and_then(|e| e.first())
7697                .is_some_and(|v| v == "6")
7698                && s.elements
7699                    .get(2)
7700                    .and_then(|e| e.first())
7701                    .is_some_and(|v| v == "ZA9")
7702        });
7703        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
7704        let has_cav = cav_segs.iter().any(|s| {
7705            s.elements
7706                .first()
7707                .and_then(|e| e.first())
7708                .is_some_and(|v| v == "ZG3")
7709        });
7710        ConditionResult::from(has_cci && has_cav)
7711    }
7712
7713    /// [450] Wenn in demselben RFF der Code Z32 (Netzlokation) im DE1153 vorhanden
7714    fn evaluate_450(&self, ctx: &EvaluationContext) -> ConditionResult {
7715        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7716        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7717        // alternative of the `⊻` held and the XOR failed on each RFF).
7718        same_rff_qualifier(ctx, "Z32")
7719    }
7720
7721    /// [451] Wenn in demselben RFF der Code Z18 (Marktlokation) im DE1153 vorhanden
7722    fn evaluate_451(&self, ctx: &EvaluationContext) -> ConditionResult {
7723        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7724        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7725        // alternative of the `⊻` held and the XOR failed on each RFF).
7726        same_rff_qualifier(ctx, "Z18")
7727    }
7728
7729    /// [452] Wenn in demselben RFF der Code Z19 (Messlokation) im DE1153 vorhanden
7730    fn evaluate_452(&self, ctx: &EvaluationContext) -> ConditionResult {
7731        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7732        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7733        // alternative of the `⊻` held and the XOR failed on each RFF).
7734        same_rff_qualifier(ctx, "Z19")
7735    }
7736
7737    /// [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...
7738    fn evaluate_453(&self, ctx: &EvaluationContext) -> ConditionResult {
7739        ctx.external.evaluate("code_list_membership_check")
7740    }
7741
7742    /// [454] Das Bilanzierungsbeginn Datum muss kleiner sein als das Bilanzierungsende Datum in der SG8 „Datenstand des ÜNB", SG 9 Energiemenge DZÜ Anteil
7743    fn evaluate_454(&self, _ctx: &EvaluationContext) -> ConditionResult {
7744        // Check: begin date < end date (structural — always true for valid messages)
7745        ConditionResult::True
7746    }
7747
7748    /// [455] Das Bilanzierungsende-Datum muss größer sein, als das Bilanzierungsbeginn Datum in der SG8 „Datenstand des ÜNB" , SG 9 Energiemenge DZÜ Anteil
7749    fn evaluate_455(&self, _ctx: &EvaluationContext) -> ConditionResult {
7750        // Check: end date > begin date (structural — always true for valid messages)
7751        ConditionResult::True
7752    }
7753
7754    /// [456] Wenn in demselben RFF der Code Z37 (Technische Ressource) im DE1153 vorhanden
7755    fn evaluate_456(&self, ctx: &EvaluationContext) -> ConditionResult {
7756        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7757        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7758        // alternative of the `⊻` held and the XOR failed on each RFF).
7759        same_rff_qualifier(ctx, "Z37")
7760    }
7761
7762    /// [457] Wenn bei der Bestellung das Messprodukten 9991000001498 bestellt wurde
7763    fn evaluate_457(&self, ctx: &EvaluationContext) -> ConditionResult {
7764        // Check: PIA+5 with Messprodukt 9991000001498 exists
7765        let pia_segs = ctx.find_segments("PIA");
7766        let found = pia_segs.iter().any(|s| {
7767            s.elements
7768                .first()
7769                .and_then(|e| e.first())
7770                .is_some_and(|v| v == "5")
7771                && s.elements
7772                    .get(1)
7773                    .and_then(|e| e.first())
7774                    .is_some_and(|v| v == "9991000001498")
7775        });
7776        ConditionResult::from(found)
7777    }
7778
7779    /// [458] Wenn bei der Bestellung das Konfigurationsprodukte 9991000000739 bestellt wurde
7780    fn evaluate_458(&self, ctx: &EvaluationContext) -> ConditionResult {
7781        // Check: PIA+5 with Konfigurationsprodukt 9991000000739 exists
7782        let pia_segs = ctx.find_segments("PIA");
7783        let found = pia_segs.iter().any(|s| {
7784            s.elements
7785                .first()
7786                .and_then(|e| e.first())
7787                .is_some_and(|v| v == "5")
7788                && s.elements
7789                    .get(1)
7790                    .and_then(|e| e.first())
7791                    .is_some_and(|v| v == "9991000000739")
7792        });
7793        ConditionResult::from(found)
7794    }
7795
7796    /// [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
7797    fn evaluate_459(&self, ctx: &EvaluationContext) -> ConditionResult {
7798        ctx.external.evaluate("code_list_membership_check")
7799    }
7800
7801    /// [460] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"aus dem SMGW\" bestellt wurde
7802    fn evaluate_460(&self, ctx: &EvaluationContext) -> ConditionResult {
7803        ctx.external.evaluate("code_list_membership_check")
7804    }
7805
7806    /// [461] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden
7807    fn evaluate_461(&self, ctx: &EvaluationContext) -> ConditionResult {
7808        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
7809        // SG10, when the scope reaches one; of any one SG10 otherwise.
7810        this_sg10_has(ctx, |segs| {
7811            segs.iter()
7812                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
7813                && sg4_instances::has_cav(segs, "ZG8")
7814        })
7815    }
7816
7817    /// [462] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden
7818    fn evaluate_462(&self, ctx: &EvaluationContext) -> ConditionResult {
7819        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
7820        // SG10, when the scope reaches one; of any one SG10 otherwise.
7821        this_sg10_has(ctx, |segs| {
7822            segs.iter()
7823                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
7824                && sg4_instances::has_cav(segs, "ZH1")
7825        })
7826    }
7827
7828    /// [463] Wenn in derselben SG10 das CCI+Z61++ZF9 (Kunde erfüllt die Voraussetzung nach EnFG) vorhanden
7829    fn evaluate_463(&self, ctx: &EvaluationContext) -> ConditionResult {
7830        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7831        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7832        let found = cci_segs.iter().any(|s| {
7833            s.elements
7834                .first()
7835                .and_then(|e| e.first())
7836                .is_some_and(|v| v == "Z61")
7837                && s.elements
7838                    .get(2)
7839                    .and_then(|e| e.first())
7840                    .is_some_and(|v| v == "ZF9")
7841        });
7842        ConditionResult::from(found)
7843    }
7844
7845    /// [465] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z38 (Referenz auf die der Technischen Ressource zugeordnete Steuerbare Ressource) vorhanden ist
7846    fn evaluate_465(&self, ctx: &EvaluationContext) -> ConditionResult {
7847        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7848        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7849        let found = rff_segs.iter().any(|s| {
7850            s.elements
7851                .first()
7852                .and_then(|e| e.first())
7853                .is_some_and(|v| v == "Z38")
7854        });
7855        ConditionResult::from(found)
7856    }
7857
7858    /// [466] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z32 (Referenz auf die der Technischen Ressource zugeordneten Netzlokation) vorhanden ist
7859    fn evaluate_466(&self, ctx: &EvaluationContext) -> ConditionResult {
7860        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7861        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7862        let found = rff_segs.iter().any(|s| {
7863            s.elements
7864                .first()
7865                .and_then(|e| e.first())
7866                .is_some_and(|v| v == "Z32")
7867        });
7868        ConditionResult::from(found)
7869    }
7870
7871    /// [467] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden ist
7872    fn evaluate_467(&self, ctx: &EvaluationContext) -> ConditionResult {
7873        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7874        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7875        let found = segs.iter().any(|s| {
7876            s.elements
7877                .first()
7878                .and_then(|e| e.first())
7879                .is_some_and(|v| v == "ZG8")
7880        });
7881        ConditionResult::from(found)
7882    }
7883
7884    /// [468] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH0 (Inbetriebsetzung der TR nach 2023) vorhanden ist
7885    fn evaluate_468(&self, ctx: &EvaluationContext) -> ConditionResult {
7886        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7887        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7888        let found = segs.iter().any(|s| {
7889            s.elements
7890                .first()
7891                .and_then(|e| e.first())
7892                .is_some_and(|v| v == "ZH0")
7893        });
7894        ConditionResult::from(found)
7895    }
7896
7897    /// [469] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden ist
7898    fn evaluate_469(&self, ctx: &EvaluationContext) -> ConditionResult {
7899        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7900        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7901        let found = segs.iter().any(|s| {
7902            s.elements
7903                .first()
7904                .and_then(|e| e.first())
7905                .is_some_and(|v| v == "ZH1")
7906        });
7907        ConditionResult::from(found)
7908    }
7909
7910    /// [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
7911    fn evaluate_470(&self, ctx: &EvaluationContext) -> ConditionResult {
7912        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7913        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7914        let found = segs.iter().any(|s| {
7915            s.elements
7916                .first()
7917                .and_then(|e| e.first())
7918                .is_some_and(|v| v == "ZH5")
7919        });
7920        ConditionResult::from(found)
7921    }
7922
7923    /// [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/ ...
7924    fn evaluate_471(&self, ctx: &EvaluationContext) -> ConditionResult {
7925        // Check: SG6 RFF+Z49/Z53 has Zeitraum-ID > 1 in DE1156
7926        let found = ctx.find_segments("RFF").iter().any(|s| {
7927            let qual = s
7928                .elements
7929                .first()
7930                .and_then(|e| e.first())
7931                .map(|v| v.as_str());
7932            matches!(qual, Some("Z49") | Some("Z53"))
7933                && s.elements
7934                    .first()
7935                    .and_then(|e| e.get(2))
7936                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7937        });
7938        ConditionResult::from(found)
7939    }
7940
7941    /// [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...
7942    fn evaluate_472(&self, ctx: &EvaluationContext) -> ConditionResult {
7943        // Check: SG6 RFF+Z48/Z55 has Zeitraum-ID > 1 in DE1156
7944        let found = ctx.find_segments("RFF").iter().any(|s| {
7945            let qual = s
7946                .elements
7947                .first()
7948                .and_then(|e| e.first())
7949                .map(|v| v.as_str());
7950            matches!(qual, Some("Z48") | Some("Z55"))
7951                && s.elements
7952                    .first()
7953                    .and_then(|e| e.get(2))
7954                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7955        });
7956        ConditionResult::from(found)
7957    }
7958
7959    /// [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...
7960    fn evaluate_473(&self, ctx: &EvaluationContext) -> ConditionResult {
7961        // Check: SG6 RFF+Z47/Z54 has Zeitraum-ID > 1 in DE1156
7962        let found = ctx.find_segments("RFF").iter().any(|s| {
7963            let qual = s
7964                .elements
7965                .first()
7966                .and_then(|e| e.first())
7967                .map(|v| v.as_str());
7968            matches!(qual, Some("Z47") | Some("Z54"))
7969                && s.elements
7970                    .first()
7971                    .and_then(|e| e.get(2))
7972                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7973        });
7974        ConditionResult::from(found)
7975    }
7976
7977    /// [474] Wenn SG4 STS+7++ZC8 (Transaktionsgrund \"Beendigung der Zuordnung\") vorhanden
7978    fn evaluate_474(&self, ctx: &EvaluationContext) -> ConditionResult {
7979        let sts_segs = ctx.find_segments("STS");
7980        let found = sts_segs.iter().any(|s| {
7981            s.elements
7982                .first()
7983                .and_then(|e| e.first())
7984                .is_some_and(|v| v == "7")
7985                && s.elements
7986                    .get(2)
7987                    .and_then(|e| e.first())
7988                    .is_some_and(|v| ["ZC8"].contains(&v.as_str()))
7989        });
7990        ConditionResult::from(found)
7991    }
7992
7993    /// [475] Wenn SG4 STS+7++ZH1 (Transaktionsgrund \"Aufhebung einer zukünftigen Zuordnung wegen Stilllegung\") vorhanden
7994    fn evaluate_475(&self, ctx: &EvaluationContext) -> ConditionResult {
7995        let sts_segs = ctx.find_segments("STS");
7996        let found = sts_segs.iter().any(|s| {
7997            s.elements
7998                .first()
7999                .and_then(|e| e.first())
8000                .is_some_and(|v| v == "7")
8001                && s.elements
8002                    .get(2)
8003                    .and_then(|e| e.first())
8004                    .is_some_and(|v| ["ZH1"].contains(&v.as_str()))
8005        });
8006        ConditionResult::from(found)
8007    }
8008
8009    /// [476] Wenn SG4 STS+7++xxx+ZW0 (Transaktionsgrundergänzung Geschäftsvorfall 1) vorhanden
8010    fn evaluate_476(&self, ctx: &EvaluationContext) -> ConditionResult {
8011        let sts_segs = ctx.find_segments("STS");
8012        let found = sts_segs.iter().any(|s| {
8013            s.elements
8014                .first()
8015                .and_then(|e| e.first())
8016                .is_some_and(|v| v == "7")
8017                && s.elements
8018                    .get(3)
8019                    .and_then(|e| e.first())
8020                    .is_some_and(|v| ["ZW0"].contains(&v.as_str()))
8021        });
8022        ConditionResult::from(found)
8023    }
8024
8025    /// [477] Wenn SG4 STS+7++xxx+ZW1 (Transaktionsgrundergänzung Geschäftsvorfall 2) vorhanden
8026    fn evaluate_477(&self, ctx: &EvaluationContext) -> ConditionResult {
8027        let sts_segs = ctx.find_segments("STS");
8028        let found = sts_segs.iter().any(|s| {
8029            s.elements
8030                .first()
8031                .and_then(|e| e.first())
8032                .is_some_and(|v| v == "7")
8033                && s.elements
8034                    .get(3)
8035                    .and_then(|e| e.first())
8036                    .is_some_and(|v| ["ZW1"].contains(&v.as_str()))
8037        });
8038        ConditionResult::from(found)
8039    }
8040
8041    /// [478] Wenn SG4 STS+7++xxx+ZW2 (Transaktionsgrundergänzung Geschäftsvorfall 3) vorhanden
8042    fn evaluate_478(&self, ctx: &EvaluationContext) -> ConditionResult {
8043        let sts_segs = ctx.find_segments("STS");
8044        let found = sts_segs.iter().any(|s| {
8045            s.elements
8046                .first()
8047                .and_then(|e| e.first())
8048                .is_some_and(|v| v == "7")
8049                && s.elements
8050                    .get(3)
8051                    .and_then(|e| e.first())
8052                    .is_some_and(|v| ["ZW2"].contains(&v.as_str()))
8053        });
8054        ConditionResult::from(found)
8055    }
8056
8057    /// [479] Wenn SG4 STS+7++xxx+ZW3 (Transaktionsgrundergänzung Erzeugende Marktlokation) vorhanden
8058    fn evaluate_479(&self, ctx: &EvaluationContext) -> ConditionResult {
8059        let sts_segs = ctx.find_segments("STS");
8060        let found = sts_segs.iter().any(|s| {
8061            s.elements
8062                .first()
8063                .and_then(|e| e.first())
8064                .is_some_and(|v| v == "7")
8065                && s.elements
8066                    .get(3)
8067                    .and_then(|e| e.first())
8068                    .is_some_and(|v| ["ZW3"].contains(&v.as_str()))
8069        });
8070        ConditionResult::from(found)
8071    }
8072
8073    /// [480] Wenn SG4 STS+7++xxx+ZW4 (Transaktionsgrundergänzung Verbrauchende Marktlokation) vorhanden
8074    fn evaluate_480(&self, ctx: &EvaluationContext) -> ConditionResult {
8075        let sts_segs = ctx.find_segments("STS");
8076        let found = sts_segs.iter().any(|s| {
8077            s.elements
8078                .first()
8079                .and_then(|e| e.first())
8080                .is_some_and(|v| v == "7")
8081                && s.elements
8082                    .get(3)
8083                    .and_then(|e| e.first())
8084                    .is_some_and(|v| ["ZW4"].contains(&v.as_str()))
8085        });
8086        ConditionResult::from(found)
8087    }
8088
8089    /// [481] Wenn SG4 STS+7++xxx+ZW5 (Transaktionsgrundergänzung Tranche) vorhanden
8090    fn evaluate_481(&self, ctx: &EvaluationContext) -> ConditionResult {
8091        let sts_segs = ctx.find_segments("STS");
8092        let found = sts_segs.iter().any(|s| {
8093            s.elements
8094                .first()
8095                .and_then(|e| e.first())
8096                .is_some_and(|v| v == "7")
8097                && s.elements
8098                    .get(3)
8099                    .and_then(|e| e.first())
8100                    .is_some_and(|v| ["ZW5"].contains(&v.as_str()))
8101        });
8102        ConditionResult::from(found)
8103    }
8104
8105    /// [483] Wenn SG4 STS+7++xxx+ZW7 (Transaktionsgrundergänzung Gemessene Marktlokation) vorhanden
8106    fn evaluate_483(&self, ctx: &EvaluationContext) -> ConditionResult {
8107        let sts_segs = ctx.find_segments("STS");
8108        let found = sts_segs.iter().any(|s| {
8109            s.elements
8110                .first()
8111                .and_then(|e| e.first())
8112                .is_some_and(|v| v == "7")
8113                && s.elements
8114                    .get(3)
8115                    .and_then(|e| e.first())
8116                    .is_some_and(|v| ["ZW7"].contains(&v.as_str()))
8117        });
8118        ConditionResult::from(found)
8119    }
8120
8121    /// [484] Wenn SG4 STS+7++xxx+ZW8 (Transaktionsgrundergänzung Fall1) vorhanden
8122    fn evaluate_484(&self, ctx: &EvaluationContext) -> ConditionResult {
8123        let sts_segs = ctx.find_segments("STS");
8124        let found = sts_segs.iter().any(|s| {
8125            s.elements
8126                .first()
8127                .and_then(|e| e.first())
8128                .is_some_and(|v| v == "7")
8129                && s.elements
8130                    .get(3)
8131                    .and_then(|e| e.first())
8132                    .is_some_and(|v| ["ZW8"].contains(&v.as_str()))
8133        });
8134        ConditionResult::from(found)
8135    }
8136
8137    /// [487] Wenn SG4 STS+7++xxx+ZX1 (Transaktionsgrundergänzung Fall4) vorhanden
8138    fn evaluate_487(&self, ctx: &EvaluationContext) -> ConditionResult {
8139        let sts_segs = ctx.find_segments("STS");
8140        let found = sts_segs.iter().any(|s| {
8141            s.elements
8142                .first()
8143                .and_then(|e| e.first())
8144                .is_some_and(|v| v == "7")
8145                && s.elements
8146                    .get(3)
8147                    .and_then(|e| e.first())
8148                    .is_some_and(|v| ["ZX1"].contains(&v.as_str()))
8149        });
8150        ConditionResult::from(found)
8151    }
8152
8153    /// [489] Nur bei dem ältesten Zeitraum welcher mit SG6 RFF+Z49 (Verwendungszeitraum der Daten: Gültige Daten) beschrieben ist
8154    fn evaluate_489(&self, ctx: &EvaluationContext) -> ConditionResult {
8155        // Check: this is the oldest Zeitraum (lowest Zeitraum-ID) with RFF+Z49
8156        let rff_z49 = ctx.find_segments_with_qualifier("RFF", 0, "Z49");
8157        if rff_z49.is_empty() {
8158            return ConditionResult::Unknown;
8159        }
8160        // Approximate: condition applies when RFF+Z49 exists
8161        ConditionResult::True
8162    }
8163
8164    /// [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
8165    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
8166        ctx.format_check("DTM", 0, 1, is_mesz_utc)
8167    }
8168
8169    /// [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
8170    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
8171        ctx.format_check("DTM", 0, 1, is_mez_utc)
8172    }
8173
8174    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
8175    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
8176        // Document date must be <= now. Format 303 is CCYYMMDDHHMM — fixed-width,
8177        // so lexicographic comparison works directly.
8178        let val = ctx.resolved_value.or_else(|| {
8179            ctx.find_segments_with_qualifier("DTM", 0, "137")
8180                .first()
8181                .and_then(|s| s.elements.first())
8182                .and_then(|e| e.get(1))
8183                .map(|v| v.as_str())
8184        });
8185        let Some(dtm_val) = val else {
8186            return ConditionResult::Unknown;
8187        };
8188        if dtm_val.len() < 12 {
8189            return ConditionResult::Unknown;
8190        }
8191        let now_str = crate::eval::format_validators::utc_now_ccyymmddhhmm();
8192        ConditionResult::from(&dtm_val[..12] <= now_str.as_str())
8193    }
8194
8195    /// [500] Hinweis: Code ist gemäß der Kategorie der zu stornierenden Meldung zu wählen
8196    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
8197        ConditionResult::True
8198    }
8199
8200    /// [501] Hinweis: Es sind zur Unterstützung die Daten des LFA anzugeben
8201    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
8202        ConditionResult::True
8203    }
8204
8205    /// [502] Hinweis: Es wird der Betrachtungsmonat der DZR bzw. BAS angegeben
8206    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
8207        ConditionResult::True
8208    }
8209
8210    /// [503] Hinweis: Angabe des BGM DE1004 aus der ORDERS
8211    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
8212        ConditionResult::True
8213    }
8214
8215    /// [504] Hinweis: Je Profil (ggf. inkl. Profilschar) ein Vorgang erforderlich
8216    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
8217        ConditionResult::True
8218    }
8219
8220    /// [505] Hinweis: Je zugeordneter ID und je EEG-Überführungszeitreihe ein Vorgang erforderlich
8221    fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
8222        ConditionResult::True
8223    }
8224
8225    /// [506] Hinweis: Für jeden Code im LOC+Z16 (Marktlokation) sollen die entsprechenden Kundendaten des LF angegeben werden, falls bekannt
8226    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
8227        ConditionResult::True
8228    }
8229
8230    /// [507] Hinweis: Ursprünglich vom NB bestätigtes Beginndatum
8231    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
8232        ConditionResult::True
8233    }
8234
8235    /// [508] Hinweis: Ausschließlich kME sind über die NNE abrechenbar
8236    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
8237        ConditionResult::True
8238    }
8239
8240    /// [509] Hinweis: Handelt sich um ein mME oder iMS ist das RFF 1154 immer mit NEIN anzugeben
8241    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
8242        ConditionResult::True
8243    }
8244
8245    /// [510] Hinweis: Zu verwenden bei der Abmeldung der ESV
8246    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
8247        ConditionResult::True
8248    }
8249
8250    /// [511] Hinweis: Es sind alle Bilanzierungsgebiete anzugeben in denen das Profil verwendet wird
8251    fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
8252        ConditionResult::True
8253    }
8254
8255    /// [512] Hinweis: Es ist informativ die bisherige Veräußerungsform des LFA an der erzeugenden Marktlokation anzugeben
8256    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
8257        ConditionResult::True
8258    }
8259
8260    /// [513] Hinweis: Es ist das nächstmögliche Datum ab dem DTM+471 aus der entsprechenden Anfrage zu ermitteln
8261    fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
8262        ConditionResult::True
8263    }
8264
8265    /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
8266    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
8267        ConditionResult::True
8268    }
8269
8270    /// [515] Hinweis: Für den ZP der LieferantensummenZR anzugeben
8271    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
8272        ConditionResult::True
8273    }
8274
8275    /// [516] Hinweis: Es ist der Änderungszeitpunkt anzugeben an dem das Zuordnungsende des NBA und der Zuordnungsbeginn des NBN zu einer Marktlokation fallen
8276    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
8277        ConditionResult::True
8278    }
8279
8280    /// [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
8281    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
8282        ConditionResult::True
8283    }
8284
8285    /// [518] Hinweis: Es sind alle Altlieferanten anzugeben, an die eine Abmeldeanfrage gesendet wird
8286    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
8287        ConditionResult::True
8288    }
8289
8290    /// [519] Hinweis: Wenn in der Anmeldung der Code ZAP vorhanden war, so ist dieser auch in der Antwort zu verwenden
8291    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
8292        ConditionResult::True
8293    }
8294
8295    /// [520] Hinweis: Bei der Verwendung des Codes ZAP handelt es sich immer um eine gemessene Marktlokation
8296    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
8297        ConditionResult::True
8298    }
8299
8300    /// [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...
8301    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
8302        ConditionResult::True
8303    }
8304
8305    /// [522] Hinweis: Es ist der NBN anzugeben
8306    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
8307        ConditionResult::True
8308    }
8309
8310    /// [523] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
8311    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
8312        ConditionResult::True
8313    }
8314
8315    /// [524] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, für die der LF nicht die gemeldete Ansicht des NB teilt
8316    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
8317        ConditionResult::True
8318    }
8319
8320    /// [525] Hinweis: Es sind nur die OBIS-Kennzahlen erlaubt, die im vorherigen Stammdatenaustausch zu dieser Marktlokation vom MSB zu diesem Zeitpunkt übermittelt wurden
8321    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
8322        ConditionResult::True
8323    }
8324
8325    /// [526] Hinweis: Wenn der Marktlokation keine Technische Ressource in der Lokationsbündelstruktur zugeordnet wurde
8326    fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
8327        ConditionResult::True
8328    }
8329
8330    /// [527] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben
8331    fn evaluate_527(&self, _ctx: &EvaluationContext) -> ConditionResult {
8332        ConditionResult::True
8333    }
8334
8335    /// [528] Hinweis: Es ist das Datum/ Daten aus der Anfrage zu verwenden
8336    fn evaluate_528(&self, _ctx: &EvaluationContext) -> ConditionResult {
8337        ConditionResult::True
8338    }
8339
8340    /// [529] Hinweis: Für zusätzliche nicht im Markt standardisierte Identifikatoren wie z.B. eine Netzbetreiber-Projektnummer
8341    fn evaluate_529(&self, _ctx: &EvaluationContext) -> ConditionResult {
8342        ConditionResult::True
8343    }
8344
8345    /// [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, ...
8346    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
8347        ConditionResult::True
8348    }
8349
8350    /// [531] Hinweis: Es ist das Jahr anzugeben in dem die nächste Netznutzungsabrechnung erfolgt
8351    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
8352        ConditionResult::True
8353    }
8354
8355    /// [532] Hinweis: Kritische Daten (gemäß GPKE)
8356    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
8357        ConditionResult::True
8358    }
8359
8360    /// [533] Hinweis: Es ist die MP-ID des Lieferanten anzugeben
8361    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
8362        ConditionResult::True
8363    }
8364
8365    /// [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...
8366    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
8367        // TODO: implement
8368        ConditionResult::Unknown
8369    }
8370
8371    /// [556] Hinweis: Wenn keine Korrespondenzanschrift des Endverbrauchers/ Kunden vorliegt, ist die Anschrift der Marktlokation zu übermitteln
8372    fn evaluate_556(&self, _ctx: &EvaluationContext) -> ConditionResult {
8373        ConditionResult::True
8374    }
8375
8376    /// [558] Hinweis: Diese Information kann freiwillig ausgetauscht werden
8377    fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
8378        ConditionResult::True
8379    }
8380
8381    /// [559] Hinweis: Die Korrespondenzanschrift des Endverbrauchers/Kunden wird nicht zur Identifikation genutzt
8382    fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
8383        ConditionResult::True
8384    }
8385
8386    /// [563] Hinweis: Für die ID der LieferantensummenZR
8387    fn evaluate_563(&self, _ctx: &EvaluationContext) -> ConditionResult {
8388        ConditionResult::True
8389    }
8390
8391    /// [566] Hinweis: Altlieferant
8392    fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
8393        ConditionResult::True
8394    }
8395
8396    /// [567] Hinweis: Neulieferant
8397    fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
8398        ConditionResult::True
8399    }
8400
8401    /// [568] Hinweis: Lieferant der LieferantensummenZR
8402    fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
8403        ConditionResult::True
8404    }
8405
8406    /// [569] Hinweis: Dritter Nutzer
8407    fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
8408        ConditionResult::True
8409    }
8410
8411    /// [570] Hinweis: Netzbetreiber Alt
8412    fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
8413        ConditionResult::True
8414    }
8415
8416    /// [572] Hinweis: Kundenname aus Anmeldung Lieferant neu
8417    fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
8418        ConditionResult::True
8419    }
8420
8421    /// [576] Hinweis: Stammdaten des bisherigen Messstellenbetreibers
8422    fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
8423        ConditionResult::True
8424    }
8425
8426    /// [577] Hinweis: Wird mit dem ursprünglich vom NB bestätigten Beginnzeitpunkt gefüllt
8427    fn evaluate_577(&self, _ctx: &EvaluationContext) -> ConditionResult {
8428        ConditionResult::True
8429    }
8430
8431    /// [579] Hinweis: Auslösender Marktpartner (LFN bei STS+7++ZH0/ZG9, NB bei STS+7++ZH1)
8432    fn evaluate_579(&self, _ctx: &EvaluationContext) -> ConditionResult {
8433        ConditionResult::True
8434    }
8435
8436    /// [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...
8437    fn evaluate_580(&self, _ctx: &EvaluationContext) -> ConditionResult {
8438        ConditionResult::True
8439    }
8440
8441    /// [581] Hinweis: Es ist der Zeitpunkt anzugeben, zu welchem der Vertrag am Tag des Versandes der Antwort noch kündbar ist.
8442    fn evaluate_581(&self, _ctx: &EvaluationContext) -> ConditionResult {
8443        ConditionResult::True
8444    }
8445
8446    /// [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...
8447    fn evaluate_586(&self, _ctx: &EvaluationContext) -> ConditionResult {
8448        ConditionResult::True
8449    }
8450
8451    /// [590] Hinweis: Für den Empfang des Steuerbefehls
8452    fn evaluate_590(&self, _ctx: &EvaluationContext) -> ConditionResult {
8453        ConditionResult::True
8454    }
8455
8456    /// [594] Hinweis: Es ist der ZPB des ZP der NGZ und die ZPB der NZR anzugeben
8457    fn evaluate_594(&self, _ctx: &EvaluationContext) -> ConditionResult {
8458        ConditionResult::True
8459    }
8460
8461    /// [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...
8462    fn evaluate_599(&self, _ctx: &EvaluationContext) -> ConditionResult {
8463        ConditionResult::True
8464    }
8465
8466    /// [601] Hinweis: Es ist die ID der Marktlokation und alle Identifikatoren der Messlokationen anzugeben. Sowie wenn vorhanden die Tranche/n der Marklokation
8467    fn evaluate_601(&self, _ctx: &EvaluationContext) -> ConditionResult {
8468        ConditionResult::True
8469    }
8470
8471    /// [606] Hinweis: In diesem Segment bzw. SG sind die Daten aus der Sicht des NB anzugeben
8472    fn evaluate_606(&self, _ctx: &EvaluationContext) -> ConditionResult {
8473        ConditionResult::True
8474    }
8475
8476    /// [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
8477    fn evaluate_609(&self, _ctx: &EvaluationContext) -> ConditionResult {
8478        ConditionResult::True
8479    }
8480
8481    /// [611] Hinweis: Wenn das Unternehmen NB die Aufgaben der Marktrolle LF wahrnimmt, ist die MP-ID des Unternehmens NB in der Marktrolle LF anzugeben
8482    fn evaluate_611(&self, _ctx: &EvaluationContext) -> ConditionResult {
8483        ConditionResult::True
8484    }
8485
8486    /// [614] Hinweis: Es werden nur die OBIS Kennzahlen übermittelt die für die Bilanzierung relevant sind
8487    fn evaluate_614(&self, _ctx: &EvaluationContext) -> ConditionResult {
8488        ConditionResult::True
8489    }
8490
8491    /// [617] Hinweis: Mehrere Vorgänge für eine Marktlokation bei Wechsel bilanzierungsrelevanter Stammdaten innerhalb des Bilanzierungsmonats
8492    fn evaluate_617(&self, _ctx: &EvaluationContext) -> ConditionResult {
8493        ConditionResult::True
8494    }
8495
8496    /// [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
8497    fn evaluate_618(&self, _ctx: &EvaluationContext) -> ConditionResult {
8498        ConditionResult::True
8499    }
8500
8501    /// [619] Hinweis: Für die selbständige Änderungsmöglichkeit der Schwellwertunter- / -über-schreitung
8502    fn evaluate_619(&self, _ctx: &EvaluationContext) -> ConditionResult {
8503        ConditionResult::True
8504    }
8505
8506    /// [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.
8507    fn evaluate_621(&self, _ctx: &EvaluationContext) -> ConditionResult {
8508        ConditionResult::True
8509    }
8510
8511    /// [622] Hinweis: Falls die OBIS-Kennzahl für mehrere Marktrollen relevant ist, so muss die Segmentgruppe pro Marktrolle wiederholt werden
8512    fn evaluate_622(&self, _ctx: &EvaluationContext) -> ConditionResult {
8513        ConditionResult::True
8514    }
8515
8516    /// [623] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben, die zur Ermittlung der Energiemenge der im Vorgang genannten Marktlokation benötigt werden
8517    fn evaluate_623(&self, _ctx: &EvaluationContext) -> ConditionResult {
8518        ConditionResult::True
8519    }
8520
8521    /// [630] Hinweis: Wenn die Liste abgelehnt wird, ist kein Vorgang enthalten
8522    fn evaluate_630(&self, _ctx: &EvaluationContext) -> ConditionResult {
8523        ConditionResult::True
8524    }
8525
8526    /// [631] Hinweis: Es ist die Listennummer aus der Lieferanten- bzw. Bilanzierungsgebietsclearingliste zu verwenden
8527    fn evaluate_631(&self, _ctx: &EvaluationContext) -> ConditionResult {
8528        ConditionResult::True
8529    }
8530
8531    /// [632] Hinweis: Es ist die Listennummer aus der Lieferanten-ausfallarbeits-clearingliste zu verwenden
8532    fn evaluate_632(&self, _ctx: &EvaluationContext) -> ConditionResult {
8533        ConditionResult::True
8534    }
8535
8536    /// [637] Hinweis: Bei Verpflichtungsanfrage
8537    fn evaluate_637(&self, _ctx: &EvaluationContext) -> ConditionResult {
8538        ConditionResult::True
8539    }
8540
8541    /// [638] Hinweis: Bei Aufforderung zur Übernahme der einzelnen Messlokation durch den gMSB
8542    fn evaluate_638(&self, _ctx: &EvaluationContext) -> ConditionResult {
8543        ConditionResult::True
8544    }
8545
8546    /// [639] Hinweis: Wenn Antwort auf Deaktivierung
8547    fn evaluate_639(&self, _ctx: &EvaluationContext) -> ConditionResult {
8548        ConditionResult::True
8549    }
8550
8551    /// [640] Hinweis: Wenn Antwort auf Aktivierung
8552    fn evaluate_640(&self, _ctx: &EvaluationContext) -> ConditionResult {
8553        ConditionResult::True
8554    }
8555
8556    /// [641] Hinweis: Wenn Einzelanforderung vorliegt
8557    fn evaluate_641(&self, _ctx: &EvaluationContext) -> ConditionResult {
8558        ConditionResult::True
8559    }
8560
8561    /// [642] Hinweis: Wenn Abo vorliegt
8562    fn evaluate_642(&self, _ctx: &EvaluationContext) -> ConditionResult {
8563        ConditionResult::True
8564    }
8565
8566    /// [643] Hinweis: Nachfolgender Netzbetreiber
8567    fn evaluate_643(&self, _ctx: &EvaluationContext) -> ConditionResult {
8568        ConditionResult::True
8569    }
8570
8571    /// [645] Hinweis: Es ist der Bilanzkreis des LF anzugeben
8572    fn evaluate_645(&self, _ctx: &EvaluationContext) -> ConditionResult {
8573        ConditionResult::True
8574    }
8575
8576    /// [646] Hinweis: Es ist der RD-Bilanzkreis des ANB anzugeben
8577    fn evaluate_646(&self, _ctx: &EvaluationContext) -> ConditionResult {
8578        ConditionResult::True
8579    }
8580
8581    /// [647] Hinweis: Es ist der RD-Bilanzkreis des anfNB anzugeben
8582    fn evaluate_647(&self, _ctx: &EvaluationContext) -> ConditionResult {
8583        ConditionResult::True
8584    }
8585
8586    /// [648] Hinweis: Soll ein Produkt ab dem Datum \"Änderung zum\" nicht mehr vorhanden sein, wird dies durch nicht angeben des PIA+5 ausgedrückt
8587    fn evaluate_648(&self, _ctx: &EvaluationContext) -> ConditionResult {
8588        ConditionResult::True
8589    }
8590
8591    /// [651] Hinweis: Es sind die Marktlokationen anzugeben, zu welchen die hier genannte OBIS benötigt wird
8592    fn evaluate_651(&self, _ctx: &EvaluationContext) -> ConditionResult {
8593        ConditionResult::True
8594    }
8595
8596    /// [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...
8597    fn evaluate_653(&self, _ctx: &EvaluationContext) -> ConditionResult {
8598        ConditionResult::True
8599    }
8600
8601    /// [655] Hinweis: Es ist die Listennummer aus der DZÜ Liste zu verwenden
8602    fn evaluate_655(&self, _ctx: &EvaluationContext) -> ConditionResult {
8603        ConditionResult::True
8604    }
8605
8606    /// [659] Hinweis: Bei Tranchen ist nur der Meldepunkt der Tranche anzugeben
8607    fn evaluate_659(&self, _ctx: &EvaluationContext) -> ConditionResult {
8608        ConditionResult::True
8609    }
8610
8611    /// [660] Hinweis: Es ist die ID der BK-Summenzeitreihe Aggregationsebene BG anzugeben
8612    fn evaluate_660(&self, _ctx: &EvaluationContext) -> ConditionResult {
8613        ConditionResult::True
8614    }
8615
8616    /// [662] Hinweis: Es sind die Daten NGZ und die Daten der NZR anzugeben
8617    fn evaluate_662(&self, _ctx: &EvaluationContext) -> ConditionResult {
8618        ConditionResult::True
8619    }
8620
8621    /// [663] Hinweis: Es ist die ID der Marktlokation und die ZPB des ZP der NGZ anzugeben
8622    fn evaluate_663(&self, _ctx: &EvaluationContext) -> ConditionResult {
8623        ConditionResult::True
8624    }
8625
8626    /// [664] Hinweis: Es ist das BG des NB (LPB) anzugeben
8627    fn evaluate_664(&self, _ctx: &EvaluationContext) -> ConditionResult {
8628        ConditionResult::True
8629    }
8630
8631    /// [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
8632    fn evaluate_665(&self, _ctx: &EvaluationContext) -> ConditionResult {
8633        ConditionResult::True
8634    }
8635
8636    /// [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...
8637    fn evaluate_667(&self, _ctx: &EvaluationContext) -> ConditionResult {
8638        ConditionResult::True
8639    }
8640
8641    /// [668] Hinweis: Dieses Segment wird nach Abschluss der Einführung der Lokationsbündelstruktur zum 01.10.2025 aus der UTILMD entfernt
8642    fn evaluate_668(&self, _ctx: &EvaluationContext) -> ConditionResult {
8643        ConditionResult::True
8644    }
8645
8646    /// [670] Hinweis: Es sind alle Netzlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8647    fn evaluate_670(&self, _ctx: &EvaluationContext) -> ConditionResult {
8648        ConditionResult::True
8649    }
8650
8651    /// [671] Hinweis: Es sind alle Marktlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8652    fn evaluate_671(&self, _ctx: &EvaluationContext) -> ConditionResult {
8653        ConditionResult::True
8654    }
8655
8656    /// [672] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8657    fn evaluate_672(&self, _ctx: &EvaluationContext) -> ConditionResult {
8658        ConditionResult::True
8659    }
8660
8661    /// [673] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8662    fn evaluate_673(&self, _ctx: &EvaluationContext) -> ConditionResult {
8663        ConditionResult::True
8664    }
8665
8666    /// [674] Hinweis: Es sind alle Tranchen, zu nennen, die dem gleichen Lokationsbündel angehören
8667    fn evaluate_674(&self, _ctx: &EvaluationContext) -> ConditionResult {
8668        ConditionResult::True
8669    }
8670
8671    /// [675] Hinweis: Es sind alle Messlokationen, zu nennen, die Lokationsbündel angehören
8672    fn evaluate_675(&self, _ctx: &EvaluationContext) -> ConditionResult {
8673        ConditionResult::True
8674    }
8675
8676    /// [677] Hinweis: Es sind alle Netzlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8677    fn evaluate_677(&self, _ctx: &EvaluationContext) -> ConditionResult {
8678        ConditionResult::True
8679    }
8680
8681    /// [678] Hinweis: Es sind alle Marktlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8682    fn evaluate_678(&self, _ctx: &EvaluationContext) -> ConditionResult {
8683        ConditionResult::True
8684    }
8685
8686    /// [679] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8687    fn evaluate_679(&self, _ctx: &EvaluationContext) -> ConditionResult {
8688        ConditionResult::True
8689    }
8690
8691    /// [680] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8692    fn evaluate_680(&self, _ctx: &EvaluationContext) -> ConditionResult {
8693        ConditionResult::True
8694    }
8695
8696    /// [682] Hinweis: Es sind alle Messlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8697    fn evaluate_682(&self, _ctx: &EvaluationContext) -> ConditionResult {
8698        ConditionResult::True
8699    }
8700
8701    /// [683] Hinweis: Es sind alle ID der Netzlokationen, welche der im SG5 LOC+Z16 angegebenen Marktlokation aktuell und / oder zukünftig vorgelagert sind, anzugeben
8702    fn evaluate_683(&self, _ctx: &EvaluationContext) -> ConditionResult {
8703        ConditionResult::True
8704    }
8705
8706    /// [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
8707    fn evaluate_684(&self, _ctx: &EvaluationContext) -> ConditionResult {
8708        ConditionResult::True
8709    }
8710
8711    /// [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.
8712    fn evaluate_685(&self, _ctx: &EvaluationContext) -> ConditionResult {
8713        ConditionResult::True
8714    }
8715
8716    /// [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
8717    fn evaluate_687(&self, _ctx: &EvaluationContext) -> ConditionResult {
8718        ConditionResult::True
8719    }
8720
8721    /// [688] Hinweis: Es sind alle ID der Netzlokationen, welche der im LOC+Z16 angegebenen Marktlokation vorgelagert sind, anzugeben
8722    fn evaluate_688(&self, _ctx: &EvaluationContext) -> ConditionResult {
8723        ConditionResult::True
8724    }
8725
8726    /// [689] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16 angegebenen Marktlokation zugehören, anzugeben
8727    fn evaluate_689(&self, _ctx: &EvaluationContext) -> ConditionResult {
8728        ConditionResult::True
8729    }
8730
8731    /// [690] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen zugeordnet sind, anzugeben
8732    fn evaluate_690(&self, _ctx: &EvaluationContext) -> ConditionResult {
8733        ConditionResult::True
8734    }
8735
8736    /// [693] Hinweis: Es sind die Daten NGZ anzugeben
8737    fn evaluate_693(&self, _ctx: &EvaluationContext) -> ConditionResult {
8738        ConditionResult::True
8739    }
8740
8741    /// [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...
8742    fn evaluate_694(&self, _ctx: &EvaluationContext) -> ConditionResult {
8743        ConditionResult::True
8744    }
8745
8746    /// [695] Hinweis: Verwendung, wenn Rolle LF einem Unternehmen NB zugeordnet ist
8747    fn evaluate_695(&self, _ctx: &EvaluationContext) -> ConditionResult {
8748        ConditionResult::True
8749    }
8750
8751    /// [696] Hinweis: Verwendung, nur wenn die Rolle LF nicht einem Unternehmen NB zugeordnet ist
8752    fn evaluate_696(&self, _ctx: &EvaluationContext) -> ConditionResult {
8753        ConditionResult::True
8754    }
8755
8756    /// [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...
8757    fn evaluate_698(&self, _ctx: &EvaluationContext) -> ConditionResult {
8758        ConditionResult::True
8759    }
8760
8761    /// [699] Hinweis: Es ist die ID der Steuerbare Ressource zu nennen über die die Technische Ressource gesteuert wird
8762    fn evaluate_699(&self, _ctx: &EvaluationContext) -> ConditionResult {
8763        ConditionResult::True
8764    }
8765
8766    /// [700] Hinweis: Es ist die ID der Netzlokation zu nennen über die die Technische Ressource gesteuert wird
8767    fn evaluate_700(&self, _ctx: &EvaluationContext) -> ConditionResult {
8768        ConditionResult::True
8769    }
8770
8771    /// [704] Hinweis: Segment ist zu verwenden, wenn es in Anfrage vorhanden war
8772    fn evaluate_704(&self, _ctx: &EvaluationContext) -> ConditionResult {
8773        ConditionResult::True
8774    }
8775
8776    /// [705] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Änderung\" durchgeführt wird
8777    fn evaluate_705(&self, _ctx: &EvaluationContext) -> ConditionResult {
8778        ConditionResult::True
8779    }
8780
8781    /// [706] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Bestellung einer Änderung von Stammdaten...\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8782    fn evaluate_706(&self, ctx: &EvaluationContext) -> ConditionResult {
8783        ctx.external.evaluate("data_clearing_required")
8784    }
8785
8786    /// [707] Hinweis: für weitere Details siehe Kapitel \"SG6 Verwendungszeitraum der Daten\"
8787    fn evaluate_707(&self, _ctx: &EvaluationContext) -> ConditionResult {
8788        ConditionResult::True
8789    }
8790
8791    /// [708] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des E/G zur Marktlokation\" durchgeführt wird
8792    fn evaluate_708(&self, _ctx: &EvaluationContext) -> ConditionResult {
8793        ConditionResult::True
8794    }
8795
8796    /// [709] Hinweis: Wenn die Aktion \"Zuordnung des E/G zur Marktlokation aufgrund fehlender Antwort\" durchgeführt wird
8797    fn evaluate_709(&self, _ctx: &EvaluationContext) -> ConditionResult {
8798        ConditionResult::True
8799    }
8800
8801    /// [710] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des LFN zur Marktlokation bzw. Tranche\" durchgeführt wird
8802    fn evaluate_710(&self, _ctx: &EvaluationContext) -> ConditionResult {
8803        ConditionResult::True
8804    }
8805
8806    /// [711] Hinweis: Wenn die Aktion \"Zuordnung des LFN zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8807    fn evaluate_711(&self, _ctx: &EvaluationContext) -> ConditionResult {
8808        ConditionResult::True
8809    }
8810
8811    /// [712] Hinweis: Wenn die Aktion \"Ankündigung der Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche\" durchgeführt wird
8812    fn evaluate_712(&self, _ctx: &EvaluationContext) -> ConditionResult {
8813        ConditionResult::True
8814    }
8815
8816    /// [713] Hinweis: Wenn die Aktion \"Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8817    fn evaluate_713(&self, _ctx: &EvaluationContext) -> ConditionResult {
8818        ConditionResult::True
8819    }
8820
8821    /// [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...
8822    fn evaluate_714(&self, _ctx: &EvaluationContext) -> ConditionResult {
8823        ConditionResult::True
8824    }
8825
8826    /// [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...
8827    fn evaluate_715(&self, _ctx: &EvaluationContext) -> ConditionResult {
8828        ConditionResult::True
8829    }
8830
8831    /// [716] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Abrechnungsdaten\" durchgeführt wird
8832    fn evaluate_716(&self, _ctx: &EvaluationContext) -> ConditionResult {
8833        ConditionResult::True
8834    }
8835
8836    /// [717] Hinweis: Wenn die Aktion des Sequenzdiagramms \"Bestellung einer Änderung von Abrechnungsdaten\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8837    fn evaluate_717(&self, ctx: &EvaluationContext) -> ConditionResult {
8838        ctx.external.evaluate("data_clearing_required")
8839    }
8840
8841    /// [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
8842    fn evaluate_718(&self, _ctx: &EvaluationContext) -> ConditionResult {
8843        ConditionResult::True
8844    }
8845
8846    /// [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...
8847    fn evaluate_719(&self, _ctx: &EvaluationContext) -> ConditionResult {
8848        ConditionResult::True
8849    }
8850
8851    /// [902] Format: Möglicher Wert: ≥ 0
8852    fn evaluate_902(&self, _ctx: &EvaluationContext) -> ConditionResult {
8853        // Format condition — informational, always applies
8854        ConditionResult::True
8855    }
8856
8857    /// [910] Format: Möglicher Wert: < 0 oder ≥ 0
8858    fn evaluate_910(&self, _ctx: &EvaluationContext) -> ConditionResult {
8859        // Format condition — informational, always applies
8860        ConditionResult::True
8861    }
8862
8863    /// [914] Format: Möglicher Wert: > 0
8864    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
8865        ctx.format_check("SEQ", 1, 0, |val| validate_numeric(val, ">", 0.0))
8866    }
8867
8868    /// [922] Format: TR-ID
8869    fn evaluate_922(&self, _ctx: &EvaluationContext) -> ConditionResult {
8870        // Format condition — informational, always applies
8871        ConditionResult::True
8872    }
8873
8874    /// [926] Format: Möglicher Wert: 0
8875    fn evaluate_926(&self, _ctx: &EvaluationContext) -> ConditionResult {
8876        // Format condition — informational, always applies
8877        ConditionResult::True
8878    }
8879
8880    /// [930] Format: max. 2 Nachkommastellen
8881    fn evaluate_930(&self, _ctx: &EvaluationContext) -> ConditionResult {
8882        // Format condition — informational, always applies
8883        ConditionResult::True
8884    }
8885
8886    /// [931] Format: ZZZ = +00
8887    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
8888        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
8889    }
8890
8891    /// [932] Format: HHMM = 2200
8892    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
8893        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
8894    }
8895
8896    /// [933] Format: HHMM = 2300
8897    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
8898        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
8899    }
8900
8901    /// [937] Format: keine Nachkommastelle
8902    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
8903        ctx.format_check("SEQ", 1, 0, |val| validate_max_decimal_places(val, 0))
8904    }
8905
8906    /// [938] Format: Möglicher Wert: <= 10
8907    fn evaluate_938(&self, _ctx: &EvaluationContext) -> ConditionResult {
8908        ConditionResult::True
8909    }
8910
8911    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
8912    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
8913        ctx.format_check("COM", 0, 0, validate_email)
8914    }
8915
8916    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
8917    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
8918        ctx.format_check("COM", 0, 0, validate_phone)
8919    }
8920
8921    /// [942] Format: n1-n2-n1-n3
8922    fn evaluate_942(&self, _ctx: &EvaluationContext) -> ConditionResult {
8923        // Format condition — informational, always applies
8924        ConditionResult::True
8925    }
8926
8927    /// [943] Format: n1-n2-n1
8928    fn evaluate_943(&self, _ctx: &EvaluationContext) -> ConditionResult {
8929        // Format condition — informational, always applies
8930        ConditionResult::True
8931    }
8932
8933    /// [946] Format: max. 11 Nachkommastellen
8934    fn evaluate_946(&self, _ctx: &EvaluationContext) -> ConditionResult {
8935        // Format condition — informational, always applies
8936        ConditionResult::True
8937    }
8938
8939    /// [948] Format: n1-n2-n1-n8-n2
8940    fn evaluate_948(&self, _ctx: &EvaluationContext) -> ConditionResult {
8941        // Format condition — informational, always applies
8942        ConditionResult::True
8943    }
8944
8945    /// [950] Format: Marktlokations-ID
8946    fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
8947        // Format condition — informational, always applies
8948        ConditionResult::True
8949    }
8950
8951    /// [951] Format: Zählpunktbezeichnung
8952    fn evaluate_951(&self, _ctx: &EvaluationContext) -> ConditionResult {
8953        // Format condition — informational, always applies
8954        ConditionResult::True
8955    }
8956
8957    /// [952] Format: Gerätenummer nach DIN 43863-5
8958    fn evaluate_952(&self, _ctx: &EvaluationContext) -> ConditionResult {
8959        // Format condition — informational, always applies
8960        ConditionResult::True
8961    }
8962
8963    /// [955] Format: Möglicher Wert: < 100
8964    fn evaluate_955(&self, _ctx: &EvaluationContext) -> ConditionResult {
8965        // Format condition — informational, always applies
8966        ConditionResult::True
8967    }
8968
8969    /// [957] Format: n1-n2-n1-n8
8970    fn evaluate_957(&self, _ctx: &EvaluationContext) -> ConditionResult {
8971        // Format condition — informational, always applies
8972        ConditionResult::True
8973    }
8974
8975    /// [960] Format: Netzlokations-ID
8976    fn evaluate_960(&self, _ctx: &EvaluationContext) -> ConditionResult {
8977        // Format condition — informational, always applies
8978        ConditionResult::True
8979    }
8980
8981    /// [961] Format: SR-ID
8982    fn evaluate_961(&self, _ctx: &EvaluationContext) -> ConditionResult {
8983        // Format condition — informational, always applies
8984        ConditionResult::True
8985    }
8986
8987    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
8988    fn evaluate_967(&self, _ctx: &EvaluationContext) -> ConditionResult {
8989        // Format condition — informational, always applies
8990        ConditionResult::True
8991    }
8992
8993    /// [2001] Segmentgruppe ist mindestens zweimal je SG4 IDE+24 (Vorgang) anzugeben
8994    fn evaluate_2001(&self, ctx: &EvaluationContext) -> ConditionResult {
8995        // Cardinality: at least twice per SG4 IDE+24
8996        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8997        ConditionResult::from(ide_count > 0)
8998    }
8999
9000    /// [2002] Für jede Produktpaket-ID im SG8 SEQ+Z79 (Erforderliches Produkt) DE1050 genau einmal anzugeben
9001    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
9002        let seq_segs = ctx.find_segments("SEQ");
9003        let has_z79 = seq_segs.iter().any(|s| {
9004            s.elements
9005                .first()
9006                .and_then(|e| e.first())
9007                .is_some_and(|v| v == "Z79")
9008        });
9009        ConditionResult::from(has_z79)
9010    }
9011
9012    /// [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
9013    fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
9014        // TODO: implement
9015        ConditionResult::Unknown
9016    }
9017
9018    /// [2004] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
9019    // 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)
9020    fn evaluate_2004(&self, ctx: &EvaluationContext) -> ConditionResult {
9021        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
9022        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
9023
9024        let zeitraum_ids: Vec<String> = rff_qualifiers
9025            .iter()
9026            .filter(|(_, qual)| qual.as_str() == "Z49")
9027            .filter_map(|(idx, _)| {
9028                rff_zeitraum_ids
9029                    .iter()
9030                    .find(|(i, v)| i == idx && !v.is_empty())
9031                    .map(|(_, v)| v.clone())
9032            })
9033            .collect();
9034
9035        if zeitraum_ids.is_empty() {
9036            return ConditionResult::Unknown;
9037        }
9038
9039        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
9040
9041        for zid in &zeitraum_ids {
9042            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
9043            if count != 1 {
9044                return ConditionResult::False;
9045            }
9046        }
9047
9048        ConditionResult::True
9049    }
9050
9051    /// [2005] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
9052    // 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)
9053    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
9054        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
9055        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
9056
9057        let zeitraum_ids: Vec<String> = rff_qualifiers
9058            .iter()
9059            .filter(|(_, qual)| qual.as_str() == "Z49")
9060            .filter_map(|(idx, _)| {
9061                rff_zeitraum_ids
9062                    .iter()
9063                    .find(|(i, v)| i == idx && !v.is_empty())
9064                    .map(|(_, v)| v.clone())
9065            })
9066            .collect();
9067
9068        if zeitraum_ids.is_empty() {
9069            return ConditionResult::Unknown;
9070        }
9071
9072        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
9073
9074        for zid in &zeitraum_ids {
9075            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
9076            if count == 0 {
9077                return ConditionResult::False;
9078            }
9079        }
9080
9081        ConditionResult::True
9082    }
9083
9084    /// [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 ...
9085    fn evaluate_2010(&self, _ctx: &EvaluationContext) -> ConditionResult {
9086        // TODO: implement
9087        ConditionResult::Unknown
9088    }
9089
9090    /// [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 ...
9091    /// EXTERNAL: Requires context from outside the message.
9092    // 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)
9093    fn evaluate_2011(&self, ctx: &EvaluationContext) -> ConditionResult {
9094        ctx.external
9095            .evaluate("request_message_zeitraum_ids_present")
9096    }
9097
9098    /// [2012] Segmentgruppe ist genau einmal für die Angabe der Informativen Daten der Marktlokation anzugeben
9099    fn evaluate_2012(&self, _ctx: &EvaluationContext) -> ConditionResult {
9100        // TODO: implement
9101        ConditionResult::Unknown
9102    }
9103
9104    /// [2013] Mindesten einmal anzugeben wenn kein SG6 RFF+Z54 (Verwendungszeitraum der Daten: Im System keine Daten vorhanden) vorhanden
9105    fn evaluate_2013(&self, ctx: &EvaluationContext) -> ConditionResult {
9106        // Check: condition applies when no RFF+Z54 exists
9107        ctx.lacks_qualifier("RFF", 0, "Z54")
9108    }
9109
9110    /// [2014] Mindesten einmal anzugeben wenn kein SG6 RFF+Z47 (Verwendungszeitraum der Daten: Im System vorhandene Daten) vorhanden
9111    fn evaluate_2014(&self, ctx: &EvaluationContext) -> ConditionResult {
9112        // Check: condition applies when no RFF+Z47 exists
9113        ctx.lacks_qualifier("RFF", 0, "Z47")
9114    }
9115
9116    /// [2015] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) untergeordnet ist
9117    fn evaluate_2015(&self, _ctx: &EvaluationContext) -> ConditionResult {
9118        // TODO: implement
9119        ConditionResult::Unknown
9120    }
9121
9122    /// [2016] Je SG5 LOC+Z16 (Marktlokation) ist genau einmal die Segmentgruppe anzugeben
9123    fn evaluate_2016(&self, ctx: &EvaluationContext) -> ConditionResult {
9124        // Cardinality: exactly once per SG5 LOC+Z16 (Marktlokation)
9125        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9126        ConditionResult::from(loc_count > 0)
9127    }
9128
9129    /// [2017] Je SG5 LOC+Z17 (Messlokation) ist genau einmal die Segmentgruppe anzugeben
9130    fn evaluate_2017(&self, ctx: &EvaluationContext) -> ConditionResult {
9131        // Cardinality: exactly once per SG5 LOC+Z17 (Messlokation)
9132        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9133        ConditionResult::from(loc_count > 0)
9134    }
9135
9136    /// [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...
9137    fn evaluate_2018(&self, _ctx: &EvaluationContext) -> ConditionResult {
9138        // TODO: implement
9139        ConditionResult::Unknown
9140    }
9141
9142    /// [2060] Wenn SG10 CAV+NZR ist die Segmentgruppe genau zwei Mal je IDE+24 anzugeben
9143    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
9144        // Check: CAV+NZR exists (then segment group exactly twice per IDE+24)
9145        let found = ctx.find_segments("CAV").iter().any(|s| {
9146            s.elements
9147                .first()
9148                .and_then(|e| e.first())
9149                .is_some_and(|v| v == "NZR")
9150        });
9151        ConditionResult::from(found)
9152    }
9153
9154    /// [2061] Segment bzw. Segmentgruppe ist genau einmal je SG4 IDE (Vorgang) anzugeben
9155    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
9156        // Cardinality: exactly once per SG4 IDE (always true for single-transaction messages)
9157        let ide_count = ctx.find_segments("IDE").len();
9158        ConditionResult::from(ide_count > 0)
9159    }
9160
9161    /// [2071] Für die ID der LieferantensummenZR einmal je SG4 IDE+24 (Vorgang)
9162    fn evaluate_2071(&self, ctx: &EvaluationContext) -> ConditionResult {
9163        // Cardinality: LieferantensummenZR once per SG4 IDE+24
9164        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9165        ConditionResult::from(ide_count > 0)
9166    }
9167
9168    /// [2073] Für die ID der BilanzkreissummenZR einmal je SG4 IDE+24 (Vorgang)
9169    fn evaluate_2073(&self, ctx: &EvaluationContext) -> ConditionResult {
9170        // Cardinality: BilanzkreissummenZR once per SG4 IDE+24
9171        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9172        ConditionResult::from(ide_count > 0)
9173    }
9174
9175    /// [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)
9176    fn evaluate_2075(&self, ctx: &EvaluationContext) -> ConditionResult {
9177        // Cardinality: Zeitreihen (not EEG-EUZ/AAÜZ) once per SG4 IDE+24
9178        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9179        ConditionResult::from(ide_count > 0)
9180    }
9181
9182    /// [2080] Segmentgruppe ist max. zweimal je SG4 IDE+24 (Vorgang) anzugeben
9183    fn evaluate_2080(&self, ctx: &EvaluationContext) -> ConditionResult {
9184        // Cardinality: max twice per SG4 IDE+24
9185        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9186        ConditionResult::from(ide_count > 0)
9187    }
9188
9189    /// [2095] Je SG5 LOC+Z15 (MaBiS-Zählpunkt) ist genau einmal die Segmentgruppe anzugeben
9190    fn evaluate_2095(&self, ctx: &EvaluationContext) -> ConditionResult {
9191        // Cardinality: exactly once per SG5 LOC+Z15 (MaBiS-Zählpunkt)
9192        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z15").len();
9193        ConditionResult::from(loc_count > 0)
9194    }
9195
9196    /// [2096] Segmentgruppe ist genau zweimal je SG4 IDE anzugeben
9197    fn evaluate_2096(&self, ctx: &EvaluationContext) -> ConditionResult {
9198        // Cardinality: exactly twice per SG4 IDE
9199        let ide_count = ctx.find_segments("IDE").len();
9200        ConditionResult::from(ide_count > 0)
9201    }
9202
9203    /// [2119] Je SG8 SEQ+Z13/ ZG0 (Smartmeter-Gateway) ist genau einmal die Segmentgruppe anzugeben
9204    fn evaluate_2119(&self, ctx: &EvaluationContext) -> ConditionResult {
9205        // Cardinality: exactly once per SG8 SEQ+Z13/ZG0 (Smartmeter-Gateway)
9206        let found = ctx.find_segments("SEQ").iter().any(|s| {
9207            s.elements
9208                .first()
9209                .and_then(|e| e.first())
9210                .is_some_and(|v| v == "Z13" || v == "ZG0")
9211        });
9212        ConditionResult::from(found)
9213    }
9214
9215    /// [2140] Für die ID der LieferantensummenZR einmal je SG4 IDE+Z01 (Liste)
9216    fn evaluate_2140(&self, ctx: &EvaluationContext) -> ConditionResult {
9217        // Cardinality: LieferantensummenZR once per SG4 IDE+Z01
9218        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9219        ConditionResult::from(ide_count > 0)
9220    }
9221
9222    /// [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
9223    fn evaluate_2182(&self, ctx: &EvaluationContext) -> ConditionResult {
9224        // Cardinality: exactly once per SG8 SEQ+Z01/Z80/Z81/Z98
9225        let found = ctx.find_segments("SEQ").iter().any(|s| {
9226            s.elements
9227                .first()
9228                .and_then(|e| e.first())
9229                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9230        });
9231        ConditionResult::from(found)
9232    }
9233
9234    /// [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
9235    fn evaluate_2183(&self, ctx: &EvaluationContext) -> ConditionResult {
9236        // Cardinality: exactly twice per SG8 SEQ+Z01/Z80/Z81/Z98
9237        let found = ctx.find_segments("SEQ").iter().any(|s| {
9238            s.elements
9239                .first()
9240                .and_then(|e| e.first())
9241                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9242        });
9243        ConditionResult::from(found)
9244    }
9245
9246    /// [2207] Für die ID der Lieferanten-ausfall-arbeits-summen-zeitreihe einmal je SG4 IDE+Z01 (Liste)
9247    fn evaluate_2207(&self, ctx: &EvaluationContext) -> ConditionResult {
9248        // Cardinality: Lieferantenausfallarbeitssummenzeitreihe once per SG4 IDE+Z01
9249        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9250        ConditionResult::from(ide_count > 0)
9251    }
9252
9253    /// [2225] Einmal für jede Marktlokation bzw. Tranche für die der LF nicht die gemeldete Ansicht des NB teilt
9254    fn evaluate_2225(&self, _ctx: &EvaluationContext) -> ConditionResult {
9255        // TODO: implement
9256        ConditionResult::Unknown
9257    }
9258
9259    /// [2236] Code einmal je SG4 IDE+24 (Vorgang)
9260    fn evaluate_2236(&self, ctx: &EvaluationContext) -> ConditionResult {
9261        // Cardinality: code once per SG4 IDE+24
9262        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9263        ConditionResult::from(ide_count > 0)
9264    }
9265
9266    /// [2252] Einmal für jede Marktlokation bzw. Tranche, die in der Lieferantenausfallarbeits-summenzeitreihe berücksichtigt wurde
9267    fn evaluate_2252(&self, _ctx: &EvaluationContext) -> ConditionResult {
9268        // TODO: implement
9269        ConditionResult::Unknown
9270    }
9271
9272    /// [2261] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9273    fn evaluate_2261(&self, ctx: &EvaluationContext) -> ConditionResult {
9274        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9275        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9276        ConditionResult::from(loc_count > 0)
9277    }
9278
9279    /// [2284] Für jede Messlokations-ID im SG5 LOC+Z17 (Messlokation) DE3225 genau einmal anzugeben
9280    fn evaluate_2284(&self, ctx: &EvaluationContext) -> ConditionResult {
9281        // Cardinality: exactly once per LOC+Z17 (Messlokation) ID
9282        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9283        ConditionResult::from(loc_count > 0)
9284    }
9285
9286    /// [2286] Für jede SEQ+ZF3 (Daten der Messlokation) mindestens einmal anzugeben
9287    fn evaluate_2286(&self, ctx: &EvaluationContext) -> ConditionResult {
9288        // Cardinality: at least once per SEQ+ZF3 (Daten der Messlokation)
9289        let found = !ctx.find_segments_with_qualifier("SEQ", 0, "ZF3").is_empty();
9290        ConditionResult::from(found)
9291    }
9292
9293    /// [2287] Für jede SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) mindestens einmal anzugeben
9294    fn evaluate_2287(&self, ctx: &EvaluationContext) -> ConditionResult {
9295        // Cardinality: at least once per SEQ+Z03/ZF5 (Zähleinrichtungsdaten)
9296        let found = ctx.find_segments("SEQ").iter().any(|s| {
9297            s.elements
9298                .first()
9299                .and_then(|e| e.first())
9300                .is_some_and(|v| v == "Z03" || v == "ZF5")
9301        });
9302        ConditionResult::from(found)
9303    }
9304
9305    /// [2288] Einmal für jede Zeitreihe, die in der DZR berücksichtigt wurde
9306    fn evaluate_2288(&self, _ctx: &EvaluationContext) -> ConditionResult {
9307        // TODO: implement
9308        ConditionResult::Unknown
9309    }
9310
9311    /// [2307] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225 genau einmal anzugeben
9312    fn evaluate_2307(&self, ctx: &EvaluationContext) -> ConditionResult {
9313        // Cardinality: exactly once per LOC+Z21 (Tranche) ID
9314        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9315        ConditionResult::from(loc_count > 0)
9316    }
9317
9318    /// [2308] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225, mindestens einmal anzugeben
9319    fn evaluate_2308(&self, ctx: &EvaluationContext) -> ConditionResult {
9320        // Cardinality: at least once per LOC+Z16 (Marktlokation) ID
9321        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9322        ConditionResult::from(loc_count > 0)
9323    }
9324
9325    /// [2309] Für jede ID im SG5 LOC+Z17 (Messlokation) DE3225 mindestens einmal anzugeben
9326    fn evaluate_2309(&self, ctx: &EvaluationContext) -> ConditionResult {
9327        // Cardinality: at least once per LOC+Z17 (Messlokation) ID
9328        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9329        ConditionResult::from(loc_count > 0)
9330    }
9331
9332    /// [2310] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225 genau einmal anzugeben
9333    fn evaluate_2310(&self, ctx: &EvaluationContext) -> ConditionResult {
9334        // Cardinality: exactly once per LOC+Z16 (Marktlokation) ID
9335        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9336        ConditionResult::from(loc_count > 0)
9337    }
9338
9339    /// [2311] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9340    fn evaluate_2311(&self, ctx: &EvaluationContext) -> ConditionResult {
9341        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9342        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9343        ConditionResult::from(loc_count > 0)
9344    }
9345
9346    /// [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...
9347    fn evaluate_2312(&self, ctx: &EvaluationContext) -> ConditionResult {
9348        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9349        // Check: RFF+Z33 DE1154 does NOT contain Objektcode 9992000001256 (Netzlokation)
9350        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
9351        let has_netzlokation = rff_z33.iter().any(|s| {
9352            s.elements
9353                .first()
9354                .and_then(|e| e.get(1))
9355                .is_some_and(|v| v == "9992000001256")
9356        });
9357        ConditionResult::from(!has_netzlokation)
9358    }
9359
9360    /// [2313] Je SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels) genau einmal anzugeben
9361    fn evaluate_2313(&self, ctx: &EvaluationContext) -> ConditionResult {
9362        // Check: SG8 with SEQ+Z58/ZC9/ZD0/ZD6 exists (cardinality constraint)
9363        let found = ctx.find_segments("SEQ").iter().any(|s| {
9364            s.elements
9365                .first()
9366                .and_then(|e| e.first())
9367                .is_some_and(|v| ["Z58", "ZC9", "ZD0", "ZD6"].contains(&v.as_str()))
9368        });
9369        ConditionResult::from(found)
9370    }
9371
9372    /// [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
9373    fn evaluate_2317(&self, ctx: &EvaluationContext) -> ConditionResult {
9374        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9375        // Check: SG10 CCI+++Z25 has CAV+MIW/MPW/MUW (Wandler types)
9376        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9377            s.elements
9378                .first()
9379                .and_then(|e| e.first())
9380                .is_some_and(|v| ["MIW", "MPW", "MUW"].contains(&v.as_str()))
9381        });
9382        ConditionResult::from(has_cav)
9383    }
9384
9385    /// [2318] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MBW (Blockstromwandler) vorhanden, ist das Segment genau einmal anzugeben
9386    fn evaluate_2318(&self, ctx: &EvaluationContext) -> ConditionResult {
9387        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9388        // Check: SG10 CCI+++Z25 has CAV+MBW (Blockstromwandler)
9389        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9390            s.elements
9391                .first()
9392                .and_then(|e| e.first())
9393                .is_some_and(|v| v == "MBW")
9394        });
9395        ConditionResult::from(has_cav)
9396    }
9397
9398    /// [2344] Einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
9399    fn evaluate_2344(&self, _ctx: &EvaluationContext) -> ConditionResult {
9400        // TODO: implement
9401        ConditionResult::Unknown
9402    }
9403
9404    /// [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...
9405    fn evaluate_2350(&self, _ctx: &EvaluationContext) -> ConditionResult {
9406        // TODO: implement
9407        ConditionResult::Unknown
9408    }
9409
9410    /// [2351] Einmal je ZP einer BK-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufgenommen wurde
9411    fn evaluate_2351(&self, _ctx: &EvaluationContext) -> ConditionResult {
9412        // TODO: implement
9413        ConditionResult::Unknown
9414    }
9415
9416    /// [2352] Einmal je ZP einer BG-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufge-nommen wurde
9417    fn evaluate_2352(&self, _ctx: &EvaluationContext) -> ConditionResult {
9418        // TODO: implement
9419        ConditionResult::Unknown
9420    }
9421
9422    /// [2356] Je SG5 LOC+Z18 (Netzlokation) ist genau einmal die Segmentgruppe anzugeben
9423    fn evaluate_2356(&self, ctx: &EvaluationContext) -> ConditionResult {
9424        // Cardinality: exactly once per SG5 LOC+Z18 (Netzlokation)
9425        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9426        ConditionResult::from(loc_count > 0)
9427    }
9428
9429    /// [2357] Je SG5 LOC+Z20 (Technische Ressource) ist genau einmal die Segmentgruppe anzugeben
9430    fn evaluate_2357(&self, ctx: &EvaluationContext) -> ConditionResult {
9431        // Cardinality: exactly once per SG5 LOC+Z20 (Technische Ressource)
9432        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z20").len();
9433        ConditionResult::from(loc_count > 0)
9434    }
9435
9436    /// [2358] Je SG5 LOC+Z19 (Steuerbare Ressource) ist genau einmal die Segmentgruppe anzugeben
9437    fn evaluate_2358(&self, ctx: &EvaluationContext) -> ConditionResult {
9438        // Cardinality: exactly once per SG5 LOC+Z19 (Steuerbare Ressource)
9439        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z19").len();
9440        ConditionResult::from(loc_count > 0)
9441    }
9442
9443    /// [2359] Für jede ID im SG5 LOC+Z16 / Z17 / Z20 (Marktlokation / Messlokation / Technische Ressource) DE3225 mindestens einmal anzugeben
9444    fn evaluate_2359(&self, ctx: &EvaluationContext) -> ConditionResult {
9445        // Cardinality: at least once per LOC+Z16/Z17/Z20 ID
9446        let found = ctx.find_segments("LOC").iter().any(|s| {
9447            s.elements
9448                .first()
9449                .and_then(|e| e.first())
9450                .is_some_and(|v| ["Z16", "Z17", "Z20"].contains(&v.as_str()))
9451        });
9452        ConditionResult::from(found)
9453    }
9454
9455    /// [2360] Für jede ID im SG5 LOC+Z18 (Netzlokation) DE3225 mindestens einmal anzugeben
9456    fn evaluate_2360(&self, ctx: &EvaluationContext) -> ConditionResult {
9457        // Cardinality: at least once per LOC+Z18 (Netzlokation) ID
9458        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9459        ConditionResult::from(loc_count > 0)
9460    }
9461
9462    /// [2361] Für jede ID im SG5 LOC+Z17 / Z20 / Z22 (Messlokation / Technische Ressource / ruhende Marktlokation) DE3225 mindestens einmal anzugeben
9463    fn evaluate_2361(&self, ctx: &EvaluationContext) -> ConditionResult {
9464        // Cardinality: at least once per LOC+Z17/Z20/Z22 ID
9465        let found = ctx.find_segments("LOC").iter().any(|s| {
9466            s.elements
9467                .first()
9468                .and_then(|e| e.first())
9469                .is_some_and(|v| ["Z17", "Z20", "Z22"].contains(&v.as_str()))
9470        });
9471        ConditionResult::from(found)
9472    }
9473}
9474
9475// HAND-EDITED: helpers for [223] / [265].
9476
9477use mig_types::segment::OwnedSegment;
9478
9479/// One SG8 repetition: its SEQ qualifier, the Marktlokation IDs of its
9480/// RFF+Z18, and its SG10s as `(CCIs, CAVs)`.
9481struct Sg8Block {
9482    seq: String,
9483    malo_refs: Vec<String>,
9484    sg10: Vec<(Vec<OwnedSegment>, Vec<OwnedSegment>)>,
9485}
9486
9487fn component(seg: &OwnedSegment, element: usize, comp: usize) -> Option<&str> {
9488    seg.elements
9489        .get(element)
9490        .and_then(|e| e.get(comp))
9491        .map(String::as_str)
9492}
9493
9494/// The message's SG8 repetitions, from the group navigator when there is one,
9495/// else from the segments in message order: SEQ opens an SG8, CCI opens an
9496/// SG10 within it that takes the CAVs after it. (`validate_pid_with_conditions`
9497/// evaluates BO4E input without a navigator.) `None` without either.
9498fn sg8_blocks(ctx: &EvaluationContext) -> Option<Vec<Sg8Block>> {
9499    let malo_refs = |rffs: &[OwnedSegment]| -> Vec<String> {
9500        rffs.iter()
9501            .filter(|s| component(s, 0, 0) == Some("Z18"))
9502            .filter_map(|s| component(s, 0, 1).map(str::to_string))
9503            .collect()
9504    };
9505    if let Some(nav) = ctx.navigator() {
9506        let sg8 = ["SG4", "SG8"];
9507        return Some(
9508            (0..nav.group_instance_count(&sg8))
9509                .map(|i| Sg8Block {
9510                    seq: nav
9511                        .find_segments_in_group("SEQ", &sg8, i)
9512                        .first()
9513                        .and_then(|s| component(s, 0, 0).map(str::to_string))
9514                        .unwrap_or_default(),
9515                    malo_refs: malo_refs(&nav.find_segments_in_group("RFF", &sg8, i)),
9516                    sg10: (0..nav.child_group_instance_count(&sg8, i, "SG10"))
9517                        .map(|j| {
9518                            (
9519                                nav.find_segments_in_child_group("CCI", &sg8, i, "SG10", j),
9520                                nav.find_segments_in_child_group("CAV", &sg8, i, "SG10", j),
9521                            )
9522                        })
9523                        .collect(),
9524                })
9525                .collect(),
9526        );
9527    }
9528    if ctx.segments.is_empty() {
9529        return None;
9530    }
9531    let mut blocks: Vec<Sg8Block> = Vec::new();
9532    let mut rffs: Vec<OwnedSegment> = Vec::new();
9533    for seg in ctx.segments {
9534        match seg.id.as_str() {
9535            "SEQ" => {
9536                if let Some(b) = blocks.last_mut() {
9537                    b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9538                }
9539                rffs.clear();
9540                blocks.push(Sg8Block {
9541                    seq: component(seg, 0, 0).unwrap_or_default().to_string(),
9542                    malo_refs: Vec::new(),
9543                    sg10: Vec::new(),
9544                });
9545            }
9546            // Segments after the SG8s end the last one.
9547            "NAD" | "UNT" | "IDE" => {
9548                if let Some(b) = blocks.last_mut() {
9549                    if b.malo_refs.is_empty() {
9550                        b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9551                    }
9552                }
9553                rffs.clear();
9554                if seg.id == "IDE" {
9555                    continue;
9556                }
9557                break;
9558            }
9559            "RFF" if !blocks.is_empty() => rffs.push(seg.clone()),
9560            "CCI" => {
9561                if let Some(b) = blocks.last_mut() {
9562                    b.sg10.push((vec![seg.clone()], Vec::new()));
9563                }
9564            }
9565            "CAV" => {
9566                if let Some((_, cavs)) = blocks.last_mut().and_then(|b| b.sg10.last_mut()) {
9567                    cavs.push(seg.clone());
9568                }
9569            }
9570            _ => {}
9571        }
9572    }
9573    if let Some(b) = blocks.last_mut() {
9574        if b.malo_refs.is_empty() {
9575            b.malo_refs = malo_refs(&rffs);
9576        }
9577    }
9578    Some(blocks)
9579}
9580
9581// HAND-EDITED: "SG10 CCI+… CAV+… vorhanden" pairs a CCI and a CAV of one SG10
9582// (#255): [101], [102], [136], [216], [232], [234], [239], [346], [461], [462].
9583
9584/// Whether one SG10 — its CCI and CAV segments — satisfies `pred`: an SG10 of
9585/// the enclosing SG8 when `this_sg8` ("in dieser SG8") and the scope reaches
9586/// one; any SG10 of the message otherwise.
9587fn sg10_has(
9588    ctx: &EvaluationContext,
9589    this_sg8: bool,
9590    pred: impl Fn(&[OwnedSegment]) -> bool,
9591) -> ConditionResult {
9592    if this_sg8 {
9593        if let Some(sg10s) = ctx.enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"]) {
9594            return ConditionResult::from(sg10s.iter().any(|segs| pred(segs)));
9595        }
9596    }
9597    let Some(blocks) = sg8_blocks(ctx) else {
9598        return ConditionResult::False;
9599    };
9600    ConditionResult::from(blocks.iter().flat_map(|b| &b.sg10).any(|(ccis, cavs)| {
9601        let segs: Vec<OwnedSegment> = ccis.iter().chain(cavs).cloned().collect();
9602        pred(&segs)
9603    }))
9604}
9605
9606/// "in derselben SG10": whether the enclosing SG10's CCI and CAV segments
9607/// satisfy `pred`, when the scope reaches one; [`sg10_has`] over the message
9608/// otherwise.
9609fn this_sg10_has(
9610    ctx: &EvaluationContext,
9611    pred: impl Fn(&[OwnedSegment]) -> bool,
9612) -> ConditionResult {
9613    if let Some(path) = ctx.enclosing_scope("SG10") {
9614        let segs: Vec<OwnedSegment> = ["CCI", "CAV"]
9615            .iter()
9616            .flat_map(|tag| ctx.find_segments_at(path, tag))
9617            .collect();
9618        return ConditionResult::from(pred(&segs));
9619    }
9620    sg10_has(ctx, false, pred)
9621}
9622
9623/// "Wenn in demselben RFF der Code … im DE1153 vorhanden": the qualifier of the
9624/// RFF whose field is judged; message-wide only where no segment is resolved.
9625fn same_rff_qualifier(ctx: &EvaluationContext, code: &str) -> ConditionResult {
9626    match ctx.resolved_segment {
9627        Some(elements) => ConditionResult::from(
9628            elements
9629                .first()
9630                .and_then(|e| e.first())
9631                .is_some_and(|q| q == code),
9632        ),
9633        None => ctx.has_qualifier("RFF", 0, code),
9634    }
9635}