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        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4359        let found = seq_segs.iter().any(|s| {
4360            s.elements
4361                .first()
4362                .and_then(|e| e.first())
4363                .is_some_and(|v| ["Z18"].contains(&v.as_str()))
4364        });
4365        ConditionResult::from(found)
4366    }
4367
4368    /// [190] Wenn in derselben SG8 SEQ+Z60/ ZG8/ ZG9/ ZE0  (Produkt-Daten der Netzlokation) PIA+5+9991000000721:Z12 vorhanden
4369    fn evaluate_190(&self, ctx: &EvaluationContext) -> ConditionResult {
4370        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4371        // Check: SG8 with SEQ+Z60/ZG8/ZG9/ZE0 has PIA+5 with code 9991000000721
4372        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4373        let found = pia_segs.iter().any(|s| {
4374            s.elements
4375                .first()
4376                .and_then(|e| e.first())
4377                .is_some_and(|v| v == "5")
4378                && s.elements
4379                    .get(1)
4380                    .and_then(|e| e.first())
4381                    .is_some_and(|v| v == "9991000000721")
4382        });
4383        ConditionResult::from(found)
4384    }
4385
4386    /// [191] Wenn im SG4 IDE+Z01 (Identifikation einer Liste) STS+E01 (Status der Antwort der Liste) nicht vorhanden
4387    fn evaluate_191(&self, ctx: &EvaluationContext) -> ConditionResult {
4388        let sts_segs = ctx.find_segments("STS");
4389        let found = sts_segs.iter().any(|s| {
4390            s.elements
4391                .first()
4392                .and_then(|e| e.first())
4393                .is_some_and(|v| v == "E01")
4394        });
4395        ConditionResult::from(!found)
4396    }
4397
4398    /// [192] Wenn SG4 STS+7++XXX+ZW4/ZAP (Transaktionsgrund / Ergänzung: Verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
4399    fn evaluate_192(&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 == "7")
4406                && s.elements
4407                    .get(3)
4408                    .and_then(|e| e.first())
4409                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
4410        });
4411        ConditionResult::from(found)
4412    }
4413
4414    /// [193] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E06 (Niederspannung) in dieser SG vorhanden
4415    fn evaluate_193(&self, ctx: &EvaluationContext) -> ConditionResult {
4416        let nav = match ctx.navigator {
4417            Some(n) => n,
4418            None => {
4419                let cavs = ctx.find_segments("CAV");
4420                return ConditionResult::from(cavs.iter().any(|s| {
4421                    s.elements
4422                        .first()
4423                        .and_then(|e: &Vec<String>| e.first())
4424                        .is_some_and(|v: &String| v == "E06")
4425                }));
4426            }
4427        };
4428        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4429        for i in 0..sg8_count {
4430            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4431            for j in 0..sg10_count {
4432                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4433                let has_cci_zb5 = ccis.iter().any(|s| {
4434                    s.elements
4435                        .get(2)
4436                        .and_then(|e: &Vec<String>| e.first())
4437                        .is_some_and(|v: &String| v == "ZB5")
4438                });
4439                if !has_cci_zb5 {
4440                    continue;
4441                }
4442                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4443                if cavs.iter().any(|s| {
4444                    s.elements
4445                        .first()
4446                        .and_then(|e: &Vec<String>| e.first())
4447                        .is_some_and(|v: &String| v == "E06")
4448                }) {
4449                    return ConditionResult::True;
4450                }
4451            }
4452        }
4453        ConditionResult::False
4454    }
4455
4456    /// [194] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E05 (Mittelspannung) in dieser SG vorhanden
4457    fn evaluate_194(&self, ctx: &EvaluationContext) -> ConditionResult {
4458        let cci_segs = ctx.find_segments("CCI");
4459        let has_cci = cci_segs.iter().any(|s| {
4460            s.elements
4461                .iter()
4462                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4463        });
4464        let cav_segs = ctx.find_segments("CAV");
4465        let has_cav = cav_segs.iter().any(|s| {
4466            s.elements
4467                .first()
4468                .and_then(|e| e.first())
4469                .is_some_and(|v| v == "E05")
4470        });
4471        ConditionResult::from(has_cci && has_cav)
4472    }
4473
4474    /// [195] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E04 (Hochspannung) in dieser SG vorhanden
4475    fn evaluate_195(&self, ctx: &EvaluationContext) -> ConditionResult {
4476        let cci_segs = ctx.find_segments("CCI");
4477        let has_cci = cci_segs.iter().any(|s| {
4478            s.elements
4479                .iter()
4480                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4481        });
4482        let cav_segs = ctx.find_segments("CAV");
4483        let has_cav = cav_segs.iter().any(|s| {
4484            s.elements
4485                .first()
4486                .and_then(|e| e.first())
4487                .is_some_and(|v| v == "E04")
4488        });
4489        ConditionResult::from(has_cci && has_cav)
4490    }
4491
4492    /// [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...
4493    // 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)
4494    fn evaluate_196(&self, ctx: &EvaluationContext) -> ConditionResult {
4495        // HAND-EDITED: "in einem weiteren SG8 … Nummer, Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
4496        // reaches an SG8; the message as before otherwise.
4497        if let Some(scope) = ctx.enclosing_scope("SG8") {
4498            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
4499                ctx,
4500                scope,
4501                &["Z58", "ZC9", "ZD0"],
4502                true,
4503                true,
4504            ));
4505        }
4506        let nav = match ctx.navigator {
4507            Some(n) => n,
4508            None => return ConditionResult::Unknown,
4509        };
4510        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4511        let mut entries: Vec<(usize, String, String, String, String)> = Vec::new();
4512        for i in 0..sg8_count {
4513            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4514            let seq_opt = seqs.iter().find(|s| {
4515                s.elements
4516                    .first()
4517                    .and_then(|e: &Vec<String>| e.first())
4518                    .is_some_and(|v: &String| matches!(v.as_str(), "Z58" | "ZC9" | "ZD0"))
4519            });
4520            let seq = match seq_opt {
4521                Some(s) => s,
4522                None => continue,
4523            };
4524            let seq_code = seq
4525                .elements
4526                .first()
4527                .and_then(|e: &Vec<String>| e.first())
4528                .cloned()
4529                .unwrap_or_default();
4530            let zeitraum_id = seq
4531                .elements
4532                .get(1)
4533                .and_then(|e: &Vec<String>| e.first())
4534                .cloned()
4535                .unwrap_or_default();
4536            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4537            for rff in &rffs {
4538                let elems = match rff.elements.first() {
4539                    Some(e) => e,
4540                    None => continue,
4541                };
4542                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4543                    continue;
4544                }
4545                let objektcode = elems.get(1).cloned().unwrap_or_default();
4546                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4547                if !objektcode.is_empty() {
4548                    entries.push((
4549                        i,
4550                        seq_code.clone(),
4551                        zeitraum_id.clone(),
4552                        objektcode,
4553                        fort_nr,
4554                    ));
4555                }
4556            }
4557        }
4558        for a in 0..entries.len() {
4559            for b in (a + 1)..entries.len() {
4560                if entries[a].0 != entries[b].0
4561                    && entries[a].1 == entries[b].1
4562                    && entries[a].2 == entries[b].2
4563                    && entries[a].3 == entries[b].3
4564                    && entries[a].4 == entries[b].4
4565                {
4566                    return ConditionResult::True;
4567                }
4568            }
4569        }
4570        ConditionResult::False
4571    }
4572
4573    /// [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 (...
4574    // 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)
4575    fn evaluate_197(&self, ctx: &EvaluationContext) -> ConditionResult {
4576        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (SLP/SEP) — compared within the enclosing SG4 when the scope
4577        // reaches an SG8; the message as before otherwise.
4578        if let Some(scope) = ctx.enclosing_scope("SG8") {
4579            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
4580            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
4581                sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "E02")
4582            }));
4583        }
4584        let nav = match ctx.navigator {
4585            Some(n) => n,
4586            None => return ConditionResult::Unknown,
4587        };
4588        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4589        let mut qualifying_malo_ids: Vec<String> = Vec::new();
4590        for i in 0..sg8_count {
4591            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4592            if !seqs.iter().any(|s| {
4593                s.elements
4594                    .first()
4595                    .and_then(|e: &Vec<String>| e.first())
4596                    .is_some_and(|v: &String| v == "Z01")
4597            }) {
4598                continue;
4599            }
4600            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4601            let has_prog = (0..sg10_count).any(|j| {
4602                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4603                let has_za6 = ccis.iter().any(|s| {
4604                    s.elements
4605                        .get(2)
4606                        .and_then(|e: &Vec<String>| e.first())
4607                        .is_some_and(|v: &String| v == "ZA6")
4608                });
4609                if !has_za6 {
4610                    return false;
4611                }
4612                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4613                cavs.iter().any(|s| {
4614                    s.elements
4615                        .first()
4616                        .and_then(|e: &Vec<String>| e.first())
4617                        .is_some_and(|v: &String| v == "E02")
4618                })
4619            });
4620            if !has_prog {
4621                continue;
4622            }
4623            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4624            for rff in &rffs {
4625                let elems = match rff.elements.first() {
4626                    Some(e) => e,
4627                    None => continue,
4628                };
4629                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4630                    continue;
4631                }
4632                if let Some(malo_id) = elems.get(1) {
4633                    if !malo_id.is_empty() {
4634                        qualifying_malo_ids.push(malo_id.clone());
4635                    }
4636                }
4637            }
4638        }
4639        if qualifying_malo_ids.is_empty() {
4640            return ConditionResult::False;
4641        }
4642        for i in 0..sg8_count {
4643            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4644            for rff in &rffs {
4645                let elems = match rff.elements.first() {
4646                    Some(e) => e,
4647                    None => continue,
4648                };
4649                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4650                    continue;
4651                }
4652                if let Some(malo_id) = elems.get(1) {
4653                    if qualifying_malo_ids.contains(malo_id) {
4654                        return ConditionResult::True;
4655                    }
4656                }
4657            }
4658        }
4659        ConditionResult::False
4660    }
4661
4662    /// [198] Wenn SG4 STS+7++E03(Transaktionsgrund: Wechsel) vorhanden
4663    fn evaluate_198(&self, ctx: &EvaluationContext) -> ConditionResult {
4664        let sts_segs = ctx.find_segments("STS");
4665        let found = sts_segs.iter().any(|s| {
4666            s.elements
4667                .first()
4668                .and_then(|e| e.first())
4669                .is_some_and(|v| v == "7")
4670                && s.elements
4671                    .get(2)
4672                    .and_then(|e| e.first())
4673                    .is_some_and(|v| ["E03"].contains(&v.as_str()))
4674        });
4675        ConditionResult::from(found)
4676    }
4677
4678    /// [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
4679    // 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)
4680    fn evaluate_199(&self, ctx: &EvaluationContext) -> ConditionResult {
4681        let nav = match ctx.navigator {
4682            Some(n) => n,
4683            None => {
4684                return ctx.filtered_parent_child_has_qualifier(
4685                    &["SG4", "SG8"],
4686                    "SEQ",
4687                    0,
4688                    "Z01",
4689                    "SG10",
4690                    "CCI",
4691                    2,
4692                    "ZA6",
4693                )
4694            }
4695        };
4696        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4697        for i in 0..sg8_count {
4698            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4699            if !seqs.iter().any(|s| {
4700                s.elements
4701                    .first()
4702                    .and_then(|e: &Vec<String>| e.first())
4703                    .is_some_and(|v: &String| v == "Z01")
4704            }) {
4705                continue;
4706            }
4707            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4708            for j in 0..sg10_count {
4709                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4710                if !ccis.iter().any(|s| {
4711                    s.elements
4712                        .get(2)
4713                        .and_then(|e: &Vec<String>| e.first())
4714                        .is_some_and(|v: &String| v == "ZA6")
4715                }) {
4716                    continue;
4717                }
4718                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4719                if cavs.iter().any(|s| {
4720                    s.elements
4721                        .first()
4722                        .and_then(|e: &Vec<String>| e.first())
4723                        .is_some_and(|v: &String| v == "E02")
4724                }) {
4725                    return ConditionResult::True;
4726                }
4727            }
4728        }
4729        ConditionResult::False
4730    }
4731
4732    /// [201] Wenn die Marktlokation / Tranche nicht den ganzen Bilanzierungsmonat dem gleichen Tupel aus Bilanzkreis, Zeitreihentyp, Spannungsebene und Lieferant zugeordnet ist
4733    fn evaluate_201(&self, _ctx: &EvaluationContext) -> ConditionResult {
4734        // TODO: implement
4735        ConditionResult::Unknown
4736    }
4737
4738    /// [202] Wenn SG8 SEQ+Z78 RFF+Z39 (Keine standardisierte Lokationsbündelstruktur vorhanden), mit identischer Zeitraum-ID im DE1050 wie im DE3224 dieses Segments
4739    // 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)
4740    fn evaluate_202(&self, ctx: &EvaluationContext) -> ConditionResult {
4741        use std::collections::HashSet;
4742        // Collect Zeitraum-IDs from all SG5 LOC segments (DE3224 at elements[1][3])
4743        let loc_zeitraum_ids: HashSet<String> = ctx
4744            .find_segments("LOC")
4745            .into_iter()
4746            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
4747            .filter(|v| !v.is_empty())
4748            .collect();
4749        if loc_zeitraum_ids.is_empty() {
4750            return ConditionResult::False;
4751        }
4752        // Check if any SEQ+Z78 (SG8 Lokationsbündelstruktur) has DE1050 matching a LOC DE3224
4753        let matching_seq = ctx
4754            .find_segments_with_qualifier("SEQ", 0, "Z78")
4755            .into_iter()
4756            .any(|s| {
4757                s.elements
4758                    .get(1)
4759                    .and_then(|e: &Vec<String>| e.first())
4760                    .is_some_and(|v: &String| {
4761                        !v.is_empty() && loc_zeitraum_ids.contains(v.as_str())
4762                    })
4763            });
4764        if !matching_seq {
4765            return ConditionResult::False;
4766        }
4767        // Verify RFF+Z39 also exists (co-occurrence in same SG8 not verified without navigator)
4768        ctx.has_qualifier("RFF", 0, "Z39")
4769    }
4770
4771    /// [203] Wenn STS+7++E06 / Z39 / ZC6 / ZC7/ ZT6/ ZT7 vorhanden
4772    fn evaluate_203(&self, ctx: &EvaluationContext) -> ConditionResult {
4773        let sts_segs = ctx.find_segments("STS");
4774        let found = sts_segs.iter().any(|s| {
4775            s.elements
4776                .first()
4777                .and_then(|e| e.first())
4778                .is_some_and(|v| v == "7")
4779                && s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
4780                    ["E06", "Z39", "ZC6", "ZC7", "ZT6", "ZT7"].contains(&v.as_str())
4781                })
4782        });
4783        ConditionResult::from(found)
4784    }
4785
4786    /// [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...
4787    // 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)
4788    fn evaluate_204(&self, ctx: &EvaluationContext) -> ConditionResult {
4789        // True if any SG8 with Zähleinrichtungsdaten (SEQ+Z03/ZA3/ZA4) lacks RFF+Z14 (Smartmeter-Gateway)
4790        for qual in &["Z03", "ZA3", "ZA4"] {
4791            if matches!(
4792                ctx.any_group_has_qualifier_without(
4793                    "SEQ",
4794                    0,
4795                    qual,
4796                    "RFF",
4797                    0,
4798                    "Z14",
4799                    &["SG4", "SG8"]
4800                ),
4801                ConditionResult::True
4802            ) {
4803                return ConditionResult::True;
4804            }
4805        }
4806        ConditionResult::False
4807    }
4808
4809    /// [205] Wenn SG5 LOC+Z19 (Steuerbare Ressource) vorhanden
4810    fn evaluate_205(&self, ctx: &EvaluationContext) -> ConditionResult {
4811        let loc_segs = ctx.find_segments("LOC");
4812        let found = loc_segs.iter().any(|s| {
4813            s.elements
4814                .first()
4815                .and_then(|e| e.first())
4816                .is_some_and(|v| v == "Z19")
4817        });
4818        ConditionResult::from(found)
4819    }
4820
4821    /// [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
4822    fn evaluate_206(&self, ctx: &EvaluationContext) -> ConditionResult {
4823        let sts_segs = ctx.find_segments("STS");
4824        let found = sts_segs.iter().any(|s| {
4825            s.elements
4826                .first()
4827                .and_then(|e| e.first())
4828                .is_some_and(|v| v == "7")
4829                && s.elements
4830                    .get(2)
4831                    .and_then(|e| e.first())
4832                    .is_some_and(|v| ["ZG5"].contains(&v.as_str()))
4833        });
4834        ConditionResult::from(!found)
4835    }
4836
4837    /// [209] Wenn im selben Segment im DE2379 der Code 303 vorhanden ist
4838    fn evaluate_209(&self, ctx: &EvaluationContext) -> ConditionResult {
4839        ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "303"))
4840    }
4841
4842    /// [210] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
4843    fn evaluate_210(&self, ctx: &EvaluationContext) -> ConditionResult {
4844        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4845        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4846        let found = cci_segs.iter().any(|s| {
4847            s.elements
4848                .get(2)
4849                .and_then(|e| e.first())
4850                .is_some_and(|v| v == "ZA6")
4851        });
4852        ConditionResult::from(found)
4853    }
4854
4855    /// [212] Wenn im selben SG12 NAD DE3124 nicht vorhanden
4856    fn evaluate_212(&self, ctx: &EvaluationContext) -> ConditionResult {
4857        let nad_segs = ctx.find_segments("NAD");
4858        let found = nad_segs.iter().any(|s| {
4859            s.elements
4860                .get(3)
4861                .and_then(|e| e.first())
4862                .is_some_and(|v| !v.is_empty())
4863        });
4864        ConditionResult::from(!found)
4865    }
4866
4867    /// [213] Wenn SG12 NAD+Z09 (Kunde des Lieferanten) vorhanden
4868    fn evaluate_213(&self, ctx: &EvaluationContext) -> ConditionResult {
4869        let nad_segs = ctx.find_segments("NAD");
4870        let found = nad_segs.iter().any(|s| {
4871            s.elements
4872                .first()
4873                .and_then(|e| e.first())
4874                .is_some_and(|v| v == "Z09")
4875        });
4876        ConditionResult::from(found)
4877    }
4878
4879    /// [215] Wenn in derselben SG8 (Zähleinrichtungsdaten) SG10 CCI+++E13 CAV+MME (Zählertyp: mME) vorhanden
4880    fn evaluate_215(&self, ctx: &EvaluationContext) -> ConditionResult {
4881        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8 (its
4882        // SG10s included). Both segments are looked for anywhere in that SG8,
4883        // not within one SG10.
4884        let has_cci_e13 = ctx.scoped_find_segments_in("SG8", "CCI").iter().any(|s| {
4885            s.elements
4886                .get(2)
4887                .and_then(|e| e.first())
4888                .is_some_and(|v| v == "E13")
4889        });
4890        let has_cav_mme = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
4891            s.elements
4892                .first()
4893                .and_then(|e| e.first())
4894                .is_some_and(|v| v == "MME")
4895        });
4896        ConditionResult::from(has_cci_e13 && has_cav_mme)
4897    }
4898
4899    /// [216] Wenn CCI+++Z88 (Netznutzung) CAV+Z74:::Z08 (Netznutzungsvertrag: Direkter Vertrag zwischen Kunden und NB) vorhanden
4900    fn evaluate_216(&self, ctx: &EvaluationContext) -> ConditionResult {
4901        // HAND-EDITED: the CCI and the CAV of one SG10; Z08 is DE7110 of the CAV.
4902        sg10_has(ctx, false, |segs| {
4903            sg4_instances::has_cci(segs, "", "Z88")
4904                && segs.iter().any(|s| {
4905                    s.id == "CAV" && s.get_element(0) == "Z74" && s.get_component(0, 3) == "Z08"
4906                })
4907        })
4908    }
4909
4910    /// [217] Wenn SG6 RFF+Z49 (Verwendungszeitraum der Daten: \"Gültige Daten\") vorhanden
4911    fn evaluate_217(&self, ctx: &EvaluationContext) -> ConditionResult {
4912        let rff_segs = ctx.find_segments("RFF");
4913        let found = rff_segs.iter().any(|s| {
4914            s.elements
4915                .first()
4916                .and_then(|e| e.first())
4917                .is_some_and(|v| v == "Z49")
4918        });
4919        ConditionResult::from(found)
4920    }
4921
4922    /// [219] Wenn SG12 NAD+Z65 (Informativer Kunde des Lieferanten) vorhanden
4923    fn evaluate_219(&self, ctx: &EvaluationContext) -> ConditionResult {
4924        let nad_segs = ctx.find_segments("NAD");
4925        let found = nad_segs.iter().any(|s| {
4926            s.elements
4927                .first()
4928                .and_then(|e| e.first())
4929                .is_some_and(|v| v == "Z65")
4930        });
4931        ConditionResult::from(found)
4932    }
4933
4934    /// [220] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) in dieser SG8 vorhanden
4935    fn evaluate_220(&self, ctx: &EvaluationContext) -> ConditionResult {
4936        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4937        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4938        let has_seq = seq_segs.iter().any(|s| {
4939            s.elements
4940                .first()
4941                .and_then(|e| e.first())
4942                .is_some_and(|v| ["Z01", "Z98"].contains(&v.as_str()))
4943        });
4944        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4945        let has_cci = cci_segs.iter().any(|s| {
4946            s.elements
4947                .first()
4948                .and_then(|e| e.first())
4949                .is_some_and(|v| v == "Z30")
4950                && s.elements
4951                    .get(2)
4952                    .and_then(|e| e.first())
4953                    .is_some_and(|v| v == "Z06")
4954        });
4955        ConditionResult::from(has_seq && has_cci)
4956    }
4957
4958    /// [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)
4959    fn evaluate_221(&self, ctx: &EvaluationContext) -> ConditionResult {
4960        // Check: no SG8 with SEQ+Z15 has RFF+Z18 referencing same Marktlokation
4961        let has_seq_z15 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z15").is_empty();
4962        if !has_seq_z15 {
4963            return ConditionResult::True;
4964        }
4965        // Approximate: SEQ+Z15 exists, check if it references via RFF+Z18
4966        ctx.any_group_has_qualifier_without("SEQ", 0, "Z15", "RFF", 0, "Z18", &["SG4", "SG8"])
4967    }
4968
4969    /// [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...
4970    // HAND-EDITED (mako-twin, 55043): the generated version only checked that
4971    // some RFF+Z18 names some LOC+Z16 and dropped the Erzeugung clause, so it
4972    // held for every message with a Marktlokation. The scope never narrows to
4973    // one SG8 (only top-level groups are scoped), so "this SG8" is any SG8
4974    // other than SEQ+Z01, as in the neighbouring conditions.
4975    fn evaluate_223(&self, ctx: &EvaluationContext) -> ConditionResult {
4976        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (Erzeugung) — compared within the enclosing SG4 when the scope
4977        // reaches an SG8; the message as before otherwise.
4978        if let Some(scope) = ctx.enclosing_scope("SG8") {
4979            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
4980            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
4981                sg4_instances::has_cci(s, "Z30", "Z06")
4982            }));
4983        }
4984        let Some(blocks) = sg8_blocks(ctx) else {
4985            return ConditionResult::Unknown;
4986        };
4987        let loc_ids: Vec<String> = ctx
4988            .find_segments_with_qualifier("LOC", 0, "Z16")
4989            .iter()
4990            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
4991            .collect();
4992        let malo_refs = |b: &Sg8Block| -> Vec<String> {
4993            b.malo_refs
4994                .iter()
4995                .filter(|id| loc_ids.contains(id))
4996                .cloned()
4997                .collect()
4998        };
4999        // Marktlokationen whose SEQ+Z01 carries SG10 CCI+Z30++Z06 (Erzeugung).
5000        let erzeugung: Vec<String> = blocks
5001            .iter()
5002            .filter(|b| b.seq == "Z01")
5003            .filter(|b| {
5004                b.sg10.iter().any(|(ccis, _)| {
5005                    ccis.iter().any(|c| {
5006                        component(c, 0, 0) == Some("Z30") && component(c, 2, 0) == Some("Z06")
5007                    })
5008                })
5009            })
5010            .flat_map(|b| malo_refs(b))
5011            .collect();
5012        let referenced = blocks
5013            .iter()
5014            .filter(|b| b.seq != "Z01")
5015            .any(|b| malo_refs(b).iter().any(|id| erzeugung.contains(id)));
5016        ConditionResult::from(referenced)
5017    }
5018
5019    /// [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)
5020    fn evaluate_224(&self, ctx: &EvaluationContext) -> ConditionResult {
5021        // Check: no SG8 with SEQ+ZE7 has RFF+Z18 referencing same Marktlokation
5022        let has_seq_ze7 = !ctx.find_segments_with_qualifier("SEQ", 0, "ZE7").is_empty();
5023        if !has_seq_ze7 {
5024            return ConditionResult::True;
5025        }
5026        ctx.any_group_has_qualifier_without("SEQ", 0, "ZE7", "RFF", 0, "Z18", &["SG4", "SG8"])
5027    }
5028
5029    /// [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
5030    fn evaluate_227(&self, ctx: &EvaluationContext) -> ConditionResult {
5031        // Check: in SG8 with SEQ+Z98, SG10 has CCI+++ZA6 and CAV+E14 or ETK
5032        let r1 = ctx.filtered_parent_child_has_qualifier(
5033            &["SG4", "SG8"],
5034            "SEQ",
5035            0,
5036            "Z98",
5037            "SG10",
5038            "CAV",
5039            0,
5040            "E14",
5041        );
5042        if r1 == ConditionResult::True {
5043            return ConditionResult::True;
5044        }
5045        ctx.filtered_parent_child_has_qualifier(
5046            &["SG4", "SG8"],
5047            "SEQ",
5048            0,
5049            "Z98",
5050            "SG10",
5051            "CAV",
5052            0,
5053            "ETK",
5054        )
5055    }
5056
5057    /// [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)...
5058    // 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)
5059    fn evaluate_229(&self, ctx: &EvaluationContext) -> ConditionResult {
5060        // HAND-EDITED: "in einem weiteren SG8 … Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
5061        // reaches an SG8; the message as before otherwise.
5062        if let Some(scope) = ctx.enclosing_scope("SG8") {
5063            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
5064                ctx,
5065                scope,
5066                &["Z58", "ZC9", "ZD0", "ZD6"],
5067                false,
5068                true,
5069            ));
5070        }
5071        use std::collections::HashSet;
5072        let target_quals: HashSet<&str> = ["Z58", "ZC9", "ZD0", "ZD6"].iter().cloned().collect();
5073        // Collect per-SG8-instance: SEQ qualifier and Zeitraum-ID
5074        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
5075        let seq_zeitraums = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
5076        // Collect per-SG8-instance: RFF qualifier and DE1154 value
5077        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
5078        let rff_codes = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
5079        // Detect duplicate (seq_qual, zeitraum_id, objektcode) combinations across SG8 instances
5080        let mut seen: HashSet<(String, String, String)> = HashSet::new();
5081        for (idx, seq_qual) in &seq_quals {
5082            if seq_qual.is_empty() || !target_quals.contains(seq_qual.as_str()) {
5083                continue;
5084            }
5085            let zeitraum_id = seq_zeitraums
5086                .iter()
5087                .find(|(i, _)| i == idx)
5088                .map(|(_, v)| v.as_str())
5089                .unwrap_or("");
5090            // Find RFF+Z33 in this SG8 instance (correlation by instance index)
5091            for (ridx, rff_qual) in rff_quals.iter() {
5092                if ridx != idx || rff_qual != "Z33" {
5093                    continue;
5094                }
5095                if let Some((_, obj_code)) = rff_codes.iter().find(|(i, _)| i == ridx) {
5096                    if obj_code.is_empty() {
5097                        continue;
5098                    }
5099                    let key = (seq_qual.clone(), zeitraum_id.to_string(), obj_code.clone());
5100                    if seen.contains(&key) {
5101                        return ConditionResult::True;
5102                    }
5103                    seen.insert(key);
5104                }
5105            }
5106        }
5107        ConditionResult::False
5108    }
5109
5110    /// [232] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z52 (iMS) vorhanden
5111    fn evaluate_232(&self, ctx: &EvaluationContext) -> ConditionResult {
5112        // HAND-EDITED: the CCI and the CAV of one SG10.
5113        sg10_has(ctx, false, |segs| {
5114            sg4_instances::has_cci(segs, "", "Z83") && sg4_instances::has_cav(segs, "Z52")
5115        })
5116    }
5117
5118    /// [233] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle NB
5119    fn evaluate_233(&self, ctx: &EvaluationContext) -> ConditionResult {
5120        ctx.external.evaluate("recipient_role_check")
5121    }
5122
5123    /// [234] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z53 (kME/ mME) vorhanden
5124    fn evaluate_234(&self, ctx: &EvaluationContext) -> ConditionResult {
5125        // HAND-EDITED: the CCI and the CAV of one SG10.
5126        sg10_has(ctx, false, |segs| {
5127            sg4_instances::has_cci(segs, "", "Z83") && sg4_instances::has_cav(segs, "Z53")
5128        })
5129    }
5130
5131    /// [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
5132    fn evaluate_237(&self, ctx: &EvaluationContext) -> ConditionResult {
5133        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5134        // Check: PIA+Z02 with code starting with 1-08 and containing AGS
5135        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5136        let found = pia_segs.iter().any(|s| {
5137            s.elements
5138                .first()
5139                .and_then(|e| e.first())
5140                .is_some_and(|v| v == "Z02")
5141                && s.elements
5142                    .get(1)
5143                    .and_then(|e| e.first())
5144                    .is_some_and(|v| v.starts_with("1-08") && v.contains("AGS"))
5145        });
5146        ConditionResult::from(found)
5147    }
5148
5149    /// [238] Wenn SG4 IDE+24 (Vorgang) nicht vorhanden
5150    fn evaluate_238(&self, ctx: &EvaluationContext) -> ConditionResult {
5151        let ide_segs = ctx.find_segments("IDE");
5152        let found = ide_segs.iter().any(|s| {
5153            s.elements
5154                .first()
5155                .and_then(|e| e.first())
5156                .is_some_and(|v| v == "24")
5157        });
5158        ConditionResult::from(!found)
5159    }
5160
5161    /// [239] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+Z65 (Wärme/Kälte) vorhanden
5162    fn evaluate_239(&self, ctx: &EvaluationContext) -> ConditionResult {
5163        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
5164        // SG10, when the scope reaches one; of any one SG10 otherwise.
5165        this_sg10_has(ctx, |segs| {
5166            segs.iter()
5167                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
5168                && sg4_instances::has_cav(segs, "Z65")
5169        })
5170    }
5171
5172    /// [240] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) CCI+Z22++Z91 (Veräußerungsform der erzeugenden Marktlokation:: Marktprämie) vorhanden
5173    fn evaluate_240(&self, ctx: &EvaluationContext) -> ConditionResult {
5174        let seq_segs = ctx.find_segments("SEQ");
5175        let found = seq_segs.iter().any(|s| {
5176            s.elements
5177                .first()
5178                .and_then(|e| e.first())
5179                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
5180        });
5181        ConditionResult::from(found)
5182    }
5183
5184    /// [241] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle MSB
5185    fn evaluate_241(&self, ctx: &EvaluationContext) -> ConditionResult {
5186        ctx.external.evaluate("recipient_role_check")
5187    }
5188
5189    /// [243] Wenn SG10 CCI+6++ZA8 (Aggreg.verantw. NB) in dieser SG8 vorhanden
5190    fn evaluate_243(&self, ctx: &EvaluationContext) -> ConditionResult {
5191        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5192        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5193        let found = cci_segs.iter().any(|s| {
5194            s.elements
5195                .first()
5196                .and_then(|e| e.first())
5197                .is_some_and(|v| v == "6")
5198                && s.elements
5199                    .get(2)
5200                    .and_then(|e| e.first())
5201                    .is_some_and(|v| v == "ZA8")
5202        });
5203        ConditionResult::from(found)
5204    }
5205
5206    /// [244] Wenn SG10 CCI+6++ZA9 (Aggreg.verantw. ÜNB) in dieser SG8 vorhanden
5207    fn evaluate_244(&self, ctx: &EvaluationContext) -> ConditionResult {
5208        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5209        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5210        let found = cci_segs.iter().any(|s| {
5211            s.elements
5212                .first()
5213                .and_then(|e| e.first())
5214                .is_some_and(|v| v == "6")
5215                && s.elements
5216                    .get(2)
5217                    .and_then(|e| e.first())
5218                    .is_some_and(|v| v == "ZA9")
5219        });
5220        ConditionResult::from(found)
5221    }
5222
5223    /// [248] Wenn die Inbetriebnahme der verbrauchenden Marktlokation innerhalb der letzten drei Jahre war und die Energie der Marktlokation mit iMS ausgestatteten Messlokationen erfolgt
5224    fn evaluate_248(&self, _ctx: &EvaluationContext) -> ConditionResult {
5225        // TODO: implement
5226        ConditionResult::Unknown
5227    }
5228
5229    /// [249] Innerhalb eines SG4 IDE müssen alle DE1131 der SG4 STS+E01 (Status der Antwort) den identischen Wert enthalten
5230    fn evaluate_249(&self, ctx: &EvaluationContext) -> ConditionResult {
5231        // Check: all STS+E01 DE1131 values are identical
5232        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
5233        if sts_segs.len() <= 1 {
5234            return ConditionResult::True;
5235        }
5236        let first_val = sts_segs[0].elements.first().and_then(|e| e.get(1));
5237        let all_same = sts_segs
5238            .iter()
5239            .all(|s| s.elements.first().and_then(|e| e.get(1)) == first_val);
5240        ConditionResult::from(all_same)
5241    }
5242
5243    /// [251] Wenn MP-ID in SG2 NAD+MS (Nachrichtensender) in der Rolle BIKO
5244    fn evaluate_251(&self, ctx: &EvaluationContext) -> ConditionResult {
5245        ctx.external.evaluate("sender_role_check")
5246    }
5247
5248    /// [252] Wenn DE0068 vorhanden
5249    fn evaluate_252(&self, ctx: &EvaluationContext) -> ConditionResult {
5250        let unb_segs = ctx.find_segments("UNB");
5251        let found = unb_segs.iter().any(|s| {
5252            s.elements
5253                .get(4)
5254                .and_then(|e| e.first())
5255                .is_some_and(|v| !v.is_empty())
5256        });
5257        ConditionResult::from(found)
5258    }
5259
5260    /// [254] Es ist der Code \"AUA\" einzutragen
5261    fn evaluate_254(&self, _ctx: &EvaluationContext) -> ConditionResult {
5262        // Check: the code "AUA" is present
5263        ConditionResult::True // AUA is always the required code when this condition applies
5264    }
5265
5266    /// [255] Wenn Marktlokation am Redispatch 2.0 teilnimmt
5267    fn evaluate_255(&self, _ctx: &EvaluationContext) -> ConditionResult {
5268        // TODO: implement
5269        ConditionResult::Unknown
5270    }
5271
5272    /// [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
5273    fn evaluate_256(&self, ctx: &EvaluationContext) -> ConditionResult {
5274        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5275        // Check: PIA+5 has OBIS code matching 1-?:1.8.? or 1-?:2.8.? pattern
5276        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5277        let found = pia_segs.iter().any(|s| {
5278            s.elements
5279                .first()
5280                .and_then(|e| e.first())
5281                .is_some_and(|v| v == "5")
5282                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5283                    (v.contains(":1.8.") || v.contains(":2.8.")) && v.starts_with("1-")
5284                })
5285        });
5286        ConditionResult::from(found)
5287    }
5288
5289    /// [257] Wenn in derselben SG8 SEQ (OBIS-Daten der Marktlokation) das PIA+5+1-b?:1.9.e vorhanden
5290    fn evaluate_257(&self, ctx: &EvaluationContext) -> ConditionResult {
5291        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5292        // Check: PIA+5 has OBIS code matching 1-?:1.9.? pattern
5293        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5294        let found = pia_segs.iter().any(|s| {
5295            s.elements
5296                .first()
5297                .and_then(|e| e.first())
5298                .is_some_and(|v| v == "5")
5299                && s.elements
5300                    .get(1)
5301                    .and_then(|e| e.first())
5302                    .is_some_and(|v| v.contains(":1.9.") && v.starts_with("1-"))
5303        });
5304        ConditionResult::from(found)
5305    }
5306
5307    /// [259] Wenn in diesem PIA der Code im DE7140 mit 1-01, 1-03, 1-05 enthalten ist
5308    fn evaluate_259(&self, ctx: &EvaluationContext) -> ConditionResult {
5309        let pia_segs = ctx.find_segments("PIA");
5310        let found = pia_segs.iter().any(|s| {
5311            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5312                let prefixes = ["1-01", "1-03", "1-05"];
5313                prefixes.iter().any(|p| v.starts_with(p))
5314            })
5315        });
5316        ConditionResult::from(found)
5317    }
5318
5319    /// [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
5320    fn evaluate_261(&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 = [
5325                    "1-07-1", "2", "1-10-1", "2", "4", "5", "6", "7", "8", "9", "1-11-1",
5326                ];
5327                prefixes.iter().any(|p| v.starts_with(p))
5328            })
5329        });
5330        ConditionResult::from(found)
5331    }
5332
5333    /// [262] Wenn Produkt an der Marktlokation vorhanden ist
5334    fn evaluate_262(&self, _ctx: &EvaluationContext) -> ConditionResult {
5335        // TODO: implement
5336        ConditionResult::Unknown
5337    }
5338
5339    /// [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
5340    // HAND-EDITED (mako-twin, 55043): the generated version looked for
5341    // CAV+E14/ETK in any SG10 under SEQ+Z01/Z98, ignoring CCI+++ZA6; the AHB
5342    // asks for CAV+Z36 (TEP m. Referenzmessung) in the SG10 CCI+++ZA6.
5343    fn evaluate_265(&self, ctx: &EvaluationContext) -> ConditionResult {
5344        let Some(blocks) = sg8_blocks(ctx) else {
5345            return ConditionResult::Unknown;
5346        };
5347        let found = blocks
5348            .iter()
5349            .filter(|b| b.seq == "Z01" || b.seq == "Z98")
5350            .any(|b| {
5351                b.sg10.iter().any(|(ccis, cavs)| {
5352                    ccis.iter().any(|c| component(c, 2, 0) == Some("ZA6"))
5353                        && cavs.iter().any(|c| component(c, 0, 0) == Some("Z36"))
5354                })
5355            });
5356        ConditionResult::from(found)
5357    }
5358
5359    /// [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...
5360    // 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)
5361    fn evaluate_266(&self, ctx: &EvaluationContext) -> ConditionResult {
5362        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8" — compared within the enclosing SG4 when the scope
5363        // reaches an SG8; the message as before otherwise.
5364        if let Some(scope) = ctx.enclosing_scope("SG8") {
5365            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
5366            return ConditionResult::from(z01s.iter().any(|p| {
5367                sg4_instances::sg10_at(ctx, p, |s| {
5368                    sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "Z36")
5369                })
5370            }));
5371        }
5372        let nav = match ctx.navigator {
5373            Some(n) => n,
5374            None => {
5375                // Fallback: check any SG8+SEQ+Z01 child SG10 has CAV+Z36
5376                return ctx.filtered_parent_child_has_qualifier(
5377                    &["SG4", "SG8"],
5378                    "SEQ",
5379                    0,
5380                    "Z01",
5381                    "SG10",
5382                    "CAV",
5383                    0,
5384                    "Z36",
5385                );
5386            }
5387        };
5388        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5389        for i in 0..sg8_count {
5390            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
5391            for j in 0..sg10_count {
5392                // CCI+++ZA6 means elements[2][0] == "ZA6"
5393                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
5394                let has_cci_za6 = ccis.iter().any(|s| {
5395                    s.elements
5396                        .get(2)
5397                        .and_then(|e: &Vec<String>| e.first())
5398                        .is_some_and(|v: &String| v == "ZA6")
5399                });
5400                if has_cci_za6 {
5401                    let cavs =
5402                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
5403                    if cavs.iter().any(|s| {
5404                        s.elements
5405                            .first()
5406                            .and_then(|e: &Vec<String>| e.first())
5407                            .is_some_and(|v: &String| v == "Z36")
5408                    }) {
5409                        return ConditionResult::True;
5410                    }
5411                }
5412            }
5413        }
5414        ConditionResult::False
5415    }
5416
5417    /// [267] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
5418    fn evaluate_267(&self, ctx: &EvaluationContext) -> ConditionResult {
5419        let seq_segs = ctx.find_segments("SEQ");
5420        let found = seq_segs.iter().any(|s| {
5421            s.elements
5422                .first()
5423                .and_then(|e| e.first())
5424                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
5425        });
5426        ConditionResult::from(!found)
5427    }
5428
5429    /// [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
5430    fn evaluate_268(&self, _ctx: &EvaluationContext) -> ConditionResult {
5431        // Most European countries have postal codes — default to true
5432        ConditionResult::True
5433    }
5434
5435    /// [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
5436    fn evaluate_269(&self, _ctx: &EvaluationContext) -> ConditionResult {
5437        // TODO: implement
5438        ConditionResult::Unknown
5439    }
5440
5441    /// [270] Wenn in diesem PIA der Code im DE7140 mit 1-02, 1-04, 1-06, 1-09 beginnt
5442    fn evaluate_270(&self, ctx: &EvaluationContext) -> ConditionResult {
5443        let pia_segs = ctx.find_segments("PIA");
5444        let found = pia_segs.iter().any(|s| {
5445            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5446                let prefixes = ["1-02", "1-04", "1-06", "1-09"];
5447                prefixes.iter().any(|p| v.starts_with(p))
5448            })
5449        });
5450        ConditionResult::from(found)
5451    }
5452
5453    /// [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 ...
5454    fn evaluate_273(&self, ctx: &EvaluationContext) -> ConditionResult {
5455        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5456        // Check: PIA+5 has OBIS code matching energy quantity patterns in SG8 SEQ+Z20
5457        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5458        let found = pia_segs.iter().any(|s| {
5459            s.elements
5460                .first()
5461                .and_then(|e| e.first())
5462                .is_some_and(|v| v == "5")
5463                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5464                    v.starts_with("1-")
5465                        && (v.contains(":1.8.") || v.contains(":2.8.") || v.contains(":1.29."))
5466                })
5467        });
5468        ConditionResult::from(found)
5469    }
5470
5471    /// [279] Wenn STS+7++xxx+ZW4 / ZAP (Transaktionsgrundergänzung  verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
5472    fn evaluate_279(&self, ctx: &EvaluationContext) -> ConditionResult {
5473        let sts_segs = ctx.find_segments("STS");
5474        let found = sts_segs.iter().any(|s| {
5475            s.elements
5476                .first()
5477                .and_then(|e| e.first())
5478                .is_some_and(|v| v == "7")
5479                && s.elements
5480                    .get(3)
5481                    .and_then(|e| e.first())
5482                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
5483        });
5484        ConditionResult::from(found)
5485    }
5486
5487    /// [280] Nur MP-ID aus Sparte Strom
5488    fn evaluate_280(&self, ctx: &EvaluationContext) -> ConditionResult {
5489        // Check: Marktpartner-ID is from Sparte Strom
5490        // Approximate: check NAD+MS C082 for Strom MP-ID format
5491        ctx.external.evaluate("sector_check")
5492    }
5493
5494    /// [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
5495    fn evaluate_282(&self, ctx: &EvaluationContext) -> ConditionResult {
5496        // Check: PIA DE7140 has one of the specific codes
5497        let pia_segs = ctx.find_segments("PIA");
5498        let found = pia_segs.iter().any(|s| {
5499            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5500                v.starts_with("1-01-6-005")
5501                    || v.starts_with("1-01-9-001")
5502                    || v.starts_with("1-01-9-002")
5503                    || v.starts_with("1-07-3-001")
5504                    || v.starts_with("1-08-1-001")
5505                    || v.starts_with("1-08-3-001")
5506                    || v.starts_with("1-08-4-001")
5507                    || v.starts_with("1-08-4-002")
5508                    || v.starts_with("1-08-4-003")
5509                    || v.starts_with("1-08-5")
5510            })
5511        });
5512        ConditionResult::from(found)
5513    }
5514
5515    /// [284] Erlaubte Codes aus der Codeliste der Gruppenartikel- und Artikel-ID sind in der Spalte UTILMD/Codeverwendung mit X gekennzeichnet
5516    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
5517    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
5518    // Regenerating this function should preserve the shape, but re-verify
5519    // against the committed body if generate-conditions is run.
5520    fn evaluate_284(&self, ctx: &EvaluationContext) -> ConditionResult {
5521        // [284] Erlaubte Codes aus der Codeliste der Gruppenartikel-
5522        // und Artikel-ID mit X in der Spalte UTILMD/Codeverwendung.
5523        //
5524        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
5525        // narrows to the enclosing SG8 instance; otherwise falls back to
5526        // message-wide find_segments. Matches the AHB wording
5527        // "in derselben SG8 im PIA DE7140 ein Code...".
5528        use crate::eval::CodelistColumn;
5529        let any_match = ctx.scoped_find_segments("PIA").iter().any(|s| {
5530            let is_z02 = s
5531                .elements
5532                .first()
5533                .and_then(|e| e.first())
5534                .is_some_and(|v| v == "Z02");
5535            if !is_z02 {
5536                return false;
5537            }
5538            let code = match s.elements.get(1).and_then(|e| e.first()) {
5539                Some(c) if !c.is_empty() => c,
5540                _ => return false,
5541            };
5542            ctx.codelist
5543                .flag(code, CodelistColumn::UtilmdCodeverwendung)
5544                .unwrap_or(false)
5545        });
5546        ConditionResult::from(any_match)
5547    }
5548
5549    /// [285] Wenn in diesem PIA der Code im DE7140 mit 1-08-3-AGS angegeben ist
5550    fn evaluate_285(&self, ctx: &EvaluationContext) -> ConditionResult {
5551        // Check: PIA DE7140 starts with 1-08-3-AGS
5552        let pia_segs = ctx.find_segments("PIA");
5553        let found = pia_segs.iter().any(|s| {
5554            s.elements
5555                .get(1)
5556                .and_then(|e| e.first())
5557                .is_some_and(|v| v.contains("1-08-3") && v.contains("AGS"))
5558        });
5559        ConditionResult::from(found)
5560    }
5561
5562    /// [286] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/2/4/5-AGS-KG
5563    fn evaluate_286(&self, ctx: &EvaluationContext) -> ConditionResult {
5564        // Check: PIA DE7140 matches 1-08-1/2/4/5-AGS-KG pattern
5565        let pia_segs = ctx.find_segments("PIA");
5566        let found = pia_segs.iter().any(|s| {
5567            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5568                (v.starts_with("1-08-1")
5569                    || v.starts_with("1-08-2")
5570                    || v.starts_with("1-08-4")
5571                    || v.starts_with("1-08-5"))
5572                    && v.contains("AGS")
5573                    && v.contains("KG")
5574            })
5575        });
5576        ConditionResult::from(found)
5577    }
5578
5579    /// [287] Erlaubte Codes aus der PRICAT BGM+ Z70 (Preisblatt Netznutzung) des verantwortlichen NB
5580    fn evaluate_287(&self, _ctx: &EvaluationContext) -> ConditionResult {
5581        // TODO: implement
5582        ConditionResult::Unknown
5583    }
5584
5585    /// [288] Wenn in derselben SG8 (OBIS-Daten der Marktlokation) das PIA+5+1-b?:2.9.e vorhanden
5586    fn evaluate_288(&self, ctx: &EvaluationContext) -> ConditionResult {
5587        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5588        // Check: PIA+5 has OBIS code matching 1-?:2.9.? pattern
5589        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5590        let found = pia_segs.iter().any(|s| {
5591            s.elements
5592                .first()
5593                .and_then(|e| e.first())
5594                .is_some_and(|v| v == "5")
5595                && s.elements
5596                    .get(1)
5597                    .and_then(|e| e.first())
5598                    .is_some_and(|v| v.contains(":2.9.") && v.starts_with("1-"))
5599        });
5600        ConditionResult::from(found)
5601    }
5602
5603    /// [291] Wenn in derselben SG8 (Zuordnung Lokation zum Objektcode des Lokationsbündels) das RFF+Z37 vorhanden
5604    fn evaluate_291(&self, ctx: &EvaluationContext) -> ConditionResult {
5605        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5606        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
5607        let found = rff_segs.iter().any(|s| {
5608            s.elements
5609                .first()
5610                .and_then(|e| e.first())
5611                .is_some_and(|v| v == "Z37")
5612        });
5613        ConditionResult::from(found)
5614    }
5615
5616    /// [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...
5617    fn evaluate_292(&self, ctx: &EvaluationContext) -> ConditionResult {
5618        ctx.external.evaluate("code_list_membership_check")
5619    }
5620
5621    /// [293] Wenn SG5 LOC+Z22 (ruhende Marktlokation) vorhanden
5622    fn evaluate_293(&self, ctx: &EvaluationContext) -> ConditionResult {
5623        let loc_segs = ctx.find_segments("LOC");
5624        let found = loc_segs.iter().any(|s| {
5625            s.elements
5626                .first()
5627                .and_then(|e| e.first())
5628                .is_some_and(|v| v == "Z22")
5629        });
5630        ConditionResult::from(found)
5631    }
5632
5633    /// [294] Wenn SG5 LOC+Z18 (Netzlokation) vorhanden
5634    fn evaluate_294(&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 == "Z18")
5641        });
5642        ConditionResult::from(found)
5643    }
5644
5645    /// [295] Wenn SG5 LOC+Z15 (MaBiS-Zählpunkt) nicht vorhanden
5646    fn evaluate_295(&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 == "Z15")
5653        });
5654        ConditionResult::from(!found)
5655    }
5656
5657    /// [296] Wenn SG5 LOC+Z20 (Technische Ressource) vorhanden
5658    fn evaluate_296(&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 == "Z20")
5665        });
5666        ConditionResult::from(found)
5667    }
5668
5669    /// [297] #kv# Wenn SG5 LOC+Z18 (Netzlokation) nicht vorhanden
5670    fn evaluate_297(&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 == "Z18")
5677        });
5678        ConditionResult::from(!found)
5679    }
5680
5681    /// [298] Wenn SG5 LOC+Z20 (Technische Ressource) nicht vorhanden
5682    fn evaluate_298(&self, ctx: &EvaluationContext) -> ConditionResult {
5683        let loc_segs = ctx.find_segments("LOC");
5684        let found = loc_segs.iter().any(|s| {
5685            s.elements
5686                .first()
5687                .and_then(|e| e.first())
5688                .is_some_and(|v| v == "Z20")
5689        });
5690        ConditionResult::from(!found)
5691    }
5692
5693    /// [299] #kv# Wenn SG5 LOC+Z19 (Steuerbare Ressource) nicht vorhanden
5694    fn evaluate_299(&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 == "Z19")
5701        });
5702        ConditionResult::from(!found)
5703    }
5704
5705    /// [300] Wenn SG8 SEQ+Z15/ ZE7 (Daten der Tranche) nicht vorhanden
5706    fn evaluate_300(&self, ctx: &EvaluationContext) -> ConditionResult {
5707        let seq_segs = ctx.find_segments("SEQ");
5708        let found = seq_segs.iter().any(|s| {
5709            s.elements
5710                .first()
5711                .and_then(|e| e.first())
5712                .is_some_and(|v| ["Z15", "ZE7"].contains(&v.as_str()))
5713        });
5714        ConditionResult::from(!found)
5715    }
5716
5717    /// [301] Wenn BGM+E03 (Änderungsmeldung) vorhanden
5718    fn evaluate_301(&self, ctx: &EvaluationContext) -> ConditionResult {
5719        let bgm_segs = ctx.find_segments("BGM");
5720        let found = bgm_segs.iter().any(|s| {
5721            s.elements
5722                .first()
5723                .and_then(|e| e.first())
5724                .is_some_and(|v| v == "E03")
5725        });
5726        ConditionResult::from(found)
5727    }
5728
5729    /// [302] Wenn SG8 SEQ+Z08 (Profilschardaten) nicht vorhanden
5730    fn evaluate_302(&self, ctx: &EvaluationContext) -> ConditionResult {
5731        let seq_segs = ctx.find_segments("SEQ");
5732        let found = seq_segs.iter().any(|s| {
5733            s.elements
5734                .first()
5735                .and_then(|e| e.first())
5736                .is_some_and(|v| ["Z08"].contains(&v.as_str()))
5737        });
5738        ConditionResult::from(!found)
5739    }
5740
5741    /// [303] Wenn SG8 SEQ+Z21 (Profildaten) nicht vorhanden
5742    fn evaluate_303(&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| ["Z21"].contains(&v.as_str()))
5749        });
5750        ConditionResult::from(!found)
5751    }
5752
5753    /// [304] Wenn SG8 SEQ+Z22 (Daten der Summenzeitreihe) vorhanden
5754    fn evaluate_304(&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| ["Z22"].contains(&v.as_str()))
5761        });
5762        ConditionResult::from(found)
5763    }
5764
5765    /// [305] Wenn SG8 SEQ+Z24 (Daten der Überführungszeitreihe) vorhanden
5766    fn evaluate_305(&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| ["Z24"].contains(&v.as_str()))
5773        });
5774        ConditionResult::from(found)
5775    }
5776
5777    /// [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
5778    fn evaluate_306(&self, ctx: &EvaluationContext) -> ConditionResult {
5779        // HAND-EDITED: "SG5 LOC+Z22 mit derselben Zeitraum-ID wie in dieser SG8 … des SEQ+Z58" — compared within the enclosing SG4 when the scope
5780        // reaches an SG8; the message as before otherwise.
5781        if let Some(scope) = ctx.enclosing_scope("SG8") {
5782            return ConditionResult::from(
5783                sg4_instances::seq_at(ctx, scope)
5784                    .filter(|(code, zeitraum)| code == "Z58" && !zeitraum.is_empty())
5785                    .is_some_and(|(_, zeitraum)| {
5786                        sg4_instances::ruhende_malo_with_zeitraum(ctx, &zeitraum)
5787                    }),
5788            );
5789        }
5790        use std::collections::HashSet;
5791        // Collect Zeitraum-IDs from SG5 LOC+Z22 (Ruhende Marktlokation) — DE3224 at elements[1][3]
5792        let loc_z22_ids: HashSet<String> = ctx
5793            .find_segments_with_qualifier("LOC", 0, "Z22")
5794            .into_iter()
5795            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
5796            .filter(|v| !v.is_empty())
5797            .collect();
5798        if loc_z22_ids.is_empty() {
5799            return ConditionResult::False;
5800        }
5801        // Check if any SG8 SEQ+Z58 has DE1050 (elements[1][0]) matching a LOC+Z22 DE3224
5802        let has_match = ctx
5803            .find_segments_with_qualifier("SEQ", 0, "Z58")
5804            .iter()
5805            .any(|s| {
5806                s.elements
5807                    .get(1)
5808                    .and_then(|e: &Vec<String>| e.first())
5809                    .is_some_and(|v: &String| !v.is_empty() && loc_z22_ids.contains(v.as_str()))
5810            });
5811        ConditionResult::from(has_match)
5812    }
5813
5814    /// [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
5815    // 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)
5816    fn evaluate_307(&self, ctx: &EvaluationContext) -> ConditionResult {
5817        // 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
5818        // reaches an SG8; the message as before otherwise.
5819        if let Some(scope) = ctx.enclosing_scope("SG8") {
5820            return ConditionResult::from(
5821                sg4_instances::seq_at(ctx, scope)
5822                    .filter(|(code, zeitraum)| code == "Z58" && !zeitraum.is_empty())
5823                    .is_some_and(|(_, zeitraum)| {
5824                        !sg4_instances::ruhende_malo_with_zeitraum(ctx, &zeitraum)
5825                    }),
5826            );
5827        }
5828        let seq_segs = ctx.find_segments_with_qualifier("SEQ", 0, "Z58");
5829        if seq_segs.is_empty() {
5830            return ConditionResult::Unknown;
5831        }
5832        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z22");
5833        let loc_z22_zeitraum_ids: Vec<&str> = loc_segs
5834            .iter()
5835            .filter_map(|s| s.elements.get(1).and_then(|e| e.get(3)).map(|v| v.as_str()))
5836            .filter(|v| !v.is_empty())
5837            .collect();
5838        ConditionResult::from(seq_segs.iter().any(|seq| {
5839            let zid = seq
5840                .elements
5841                .get(1)
5842                .and_then(|e: &Vec<String>| e.first())
5843                .map(|v| v.as_str())
5844                .unwrap_or("");
5845            !zid.is_empty() && !loc_z22_zeitraum_ids.contains(&zid)
5846        }))
5847    }
5848
5849    /// [309] Wenn SG5 LOC+Z22 (ruhende Marktlokation) nicht vorhanden
5850    fn evaluate_309(&self, ctx: &EvaluationContext) -> ConditionResult {
5851        let loc_segs = ctx.find_segments("LOC");
5852        let found = loc_segs.iter().any(|s| {
5853            s.elements
5854                .first()
5855                .and_then(|e| e.first())
5856                .is_some_and(|v| v == "Z22")
5857        });
5858        ConditionResult::from(!found)
5859    }
5860
5861    /// [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...
5862    fn evaluate_314(&self, ctx: &EvaluationContext) -> ConditionResult {
5863        ctx.external.evaluate("code_list_membership_check")
5864    }
5865
5866    /// [315] Wenn BGM+Z88 (Datenclearing) vorhanden
5867    fn evaluate_315(&self, ctx: &EvaluationContext) -> ConditionResult {
5868        ctx.external.evaluate("data_clearing_required")
5869    }
5870
5871    /// [316] Wenn in derselben SG8 das RFF+Z14 (Smartmeter-Gateway) nicht vorhanden
5872    fn evaluate_316(&self, ctx: &EvaluationContext) -> ConditionResult {
5873        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5874        ctx.this_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
5875    }
5876
5877    /// [317] Es ist derselbe Wert wie im DE2380 von DTM+92 (Datum Vertragsbeginn) einzutragen
5878    fn evaluate_317(&self, ctx: &EvaluationContext) -> ConditionResult {
5879        // Check: DTM+92 (Datum Vertragsbeginn) exists — value identity is structural
5880        let found = !ctx.find_segments_with_qualifier("DTM", 0, "92").is_empty();
5881        if found {
5882            ConditionResult::True
5883        } else {
5884            ConditionResult::Unknown
5885        }
5886    }
5887
5888    /// [318] Es ist derselbe Wert wie im DE2380 von DTM+93 (Datum Vertragende) einzutragen
5889    fn evaluate_318(&self, ctx: &EvaluationContext) -> ConditionResult {
5890        // Check: DTM+93 (Datum Vertragende) exists — value identity is structural
5891        let found = !ctx.find_segments_with_qualifier("DTM", 0, "93").is_empty();
5892        if found {
5893            ConditionResult::True
5894        } else {
5895            ConditionResult::Unknown
5896        }
5897    }
5898
5899    /// [321] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
5900    fn evaluate_321(&self, ctx: &EvaluationContext) -> ConditionResult {
5901        let com_segs = ctx.find_segments("COM");
5902        let has_em = com_segs.iter().any(|s| {
5903            s.elements
5904                .first()
5905                .and_then(|e| e.get(1))
5906                .is_some_and(|v| v == "EM")
5907        });
5908        ConditionResult::from(has_em)
5909    }
5910
5911    /// [322] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
5912    fn evaluate_322(&self, ctx: &EvaluationContext) -> ConditionResult {
5913        let com_segs = ctx.find_segments("COM");
5914        let has_phone = com_segs.iter().any(|s| {
5915            s.elements
5916                .first()
5917                .and_then(|e| e.get(1))
5918                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
5919        });
5920        ConditionResult::from(has_phone)
5921    }
5922
5923    /// [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...
5924    fn evaluate_323(&self, ctx: &EvaluationContext) -> ConditionResult {
5925        ctx.external.evaluate("code_list_membership_check")
5926    }
5927
5928    /// [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...
5929    // 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)
5930    fn evaluate_327(&self, ctx: &EvaluationContext) -> ConditionResult {
5931        // HAND-EDITED: "die Zeitraum-ID im DE1050 dieser SG8 SEQ … einer SG6 RFF+Z47/Z48/Z49" — compared within the enclosing SG4 when the scope
5932        // reaches an SG8; the message as before otherwise.
5933        if let Some(scope) = ctx.enclosing_scope("SG8") {
5934            let Some((_, zeitraum)) =
5935                sg4_instances::seq_at(ctx, scope).filter(|(_, z)| !z.is_empty())
5936            else {
5937                return ConditionResult::False;
5938            };
5939            let sg6s = ctx.scopes_under("SG4", "SG6").unwrap_or_default();
5940            return ConditionResult::from(sg6s.iter().any(|p| {
5941                ctx.find_segments_at(p, "RFF").iter().any(|s| {
5942                    matches!(s.get_element(0), "Z47" | "Z48" | "Z49")
5943                        && s.get_component(0, 2) == zeitraum
5944                }) && ctx.find_segments_at(p, "DTM").iter().any(|s| {
5945                    s.get_element(0) == "Z25"
5946                        && s.get_component(0, 1).replace("?+", "+").as_str() >= "202212312300+00"
5947                })
5948            }));
5949        }
5950        const THRESHOLD: &str = "202212312300+00";
5951        let seq_zeitraum_ids: Vec<String> = ctx
5952            .find_segments("SEQ")
5953            .iter()
5954            .filter_map(|s| {
5955                s.elements
5956                    .get(1)
5957                    .and_then(|e: &Vec<String>| e.first())
5958                    .cloned()
5959            })
5960            .filter(|v| !v.is_empty())
5961            .collect();
5962        if seq_zeitraum_ids.is_empty() {
5963            return ConditionResult::Unknown;
5964        }
5965        let rff_segs = ctx.find_segments("RFF");
5966        let matching_rff = rff_segs.iter().any(|rff| {
5967            let qual = rff
5968                .elements
5969                .first()
5970                .and_then(|e: &Vec<String>| e.first())
5971                .map(|v| v.as_str())
5972                .unwrap_or("");
5973            if !["Z47", "Z48", "Z49"].contains(&qual) {
5974                return false;
5975            }
5976            let zid = rff
5977                .elements
5978                .first()
5979                .and_then(|e| e.get(2))
5980                .map(|v| v.as_str())
5981                .unwrap_or("");
5982            !zid.is_empty() && seq_zeitraum_ids.iter().any(|id| id.as_str() == zid)
5983        });
5984        if !matching_rff {
5985            return ConditionResult::False;
5986        }
5987        for dtm in ctx.find_segments_with_qualifier("DTM", 0, "Z25") {
5988            let val = dtm
5989                .elements
5990                .first()
5991                .and_then(|e| e.get(1))
5992                .map(|v| v.replace("?+", "+"))
5993                .unwrap_or_default();
5994            if !val.is_empty() && val.as_str() >= THRESHOLD {
5995                return ConditionResult::True;
5996            }
5997        }
5998        ConditionResult::False
5999    }
6000
6001    /// [329] Wenn in dieser SG4 IDE+24 das STS+E01++A03: E_0096 bzw. A04:E_0097 (Status der Antwort) nicht vorhanden
6002    fn evaluate_329(&self, ctx: &EvaluationContext) -> ConditionResult {
6003        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6004        // Check: STS+E01 does NOT have A03 with E_0096 or A04 with E_0097
6005        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6006        let has_a03_e0096 = sts_segs.iter().any(|s| {
6007            s.elements
6008                .get(2)
6009                .and_then(|e| e.first())
6010                .is_some_and(|v| v == "A03")
6011                && s.elements
6012                    .get(2)
6013                    .and_then(|e| e.get(1))
6014                    .is_some_and(|v| v == "E_0096")
6015        });
6016        let has_a04_e0097 = sts_segs.iter().any(|s| {
6017            s.elements
6018                .get(2)
6019                .and_then(|e| e.first())
6020                .is_some_and(|v| v == "A04")
6021                && s.elements
6022                    .get(2)
6023                    .and_then(|e| e.get(1))
6024                    .is_some_and(|v| v == "E_0097")
6025        });
6026        ConditionResult::from(!has_a03_e0096 && !has_a04_e0097)
6027    }
6028
6029    /// [331] Wenn im SG8 SEQ+Z98 (Daten der Marktlokation) das SG10 CCI+6++ZA9 (Aggreg. verantw. ÜNB) nicht vorhanden
6030    fn evaluate_331(&self, ctx: &EvaluationContext) -> ConditionResult {
6031        let seq_segs = ctx.find_segments("SEQ");
6032        let has_seq = seq_segs.iter().any(|s| {
6033            s.elements
6034                .first()
6035                .and_then(|e| e.first())
6036                .is_some_and(|v| v == "Z98")
6037        });
6038        let cci_segs = ctx.find_segments("CCI");
6039        let has_cci = cci_segs.iter().any(|s| {
6040            s.elements
6041                .first()
6042                .and_then(|e| e.first())
6043                .is_some_and(|v| v == "6")
6044                && s.elements
6045                    .get(2)
6046                    .and_then(|e| e.first())
6047                    .is_some_and(|v| v == "ZA9")
6048        });
6049        ConditionResult::from(!(has_seq && has_cci))
6050    }
6051
6052    /// [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
6053    // 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)
6054    fn evaluate_332(&self, ctx: &EvaluationContext) -> ConditionResult {
6055        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8 … nicht vorhanden" — compared within the enclosing SG4 when the scope
6056        // reaches an SG8; the message as before otherwise.
6057        if let Some(scope) = ctx.enclosing_scope("SG8") {
6058            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
6059            return ConditionResult::from(z01s.iter().any(|p| {
6060                !sg4_instances::sg10_at(ctx, p, |s| sg4_instances::has_cci(s, "6", "ZA9"))
6061            }));
6062        }
6063        let nav = match ctx.navigator {
6064            Some(n) => n,
6065            None => {
6066                if ctx.find_segments_with_qualifier("SEQ", 0, "Z01").is_empty() {
6067                    return ConditionResult::Unknown;
6068                }
6069                let has_cci_za9 = ctx
6070                    .find_segments_with_qualifier("CCI", 0, "6")
6071                    .iter()
6072                    .any(|s| {
6073                        s.elements
6074                            .get(2)
6075                            .and_then(|e: &Vec<String>| e.first())
6076                            .map(|v| v.as_str())
6077                            == Some("ZA9")
6078                    });
6079                return ConditionResult::from(!has_cci_za9);
6080            }
6081        };
6082        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6083        if sg8_count == 0 {
6084            return ConditionResult::Unknown;
6085        }
6086        for sg8_idx in 0..sg8_count {
6087            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], sg8_idx, "SG10");
6088            let mut has_cci_za9 = false;
6089            for sg10_idx in 0..sg10_count {
6090                let ccis = nav.find_segments_in_child_group(
6091                    "CCI",
6092                    &["SG4", "SG8"],
6093                    sg8_idx,
6094                    "SG10",
6095                    sg10_idx,
6096                );
6097                if ccis.iter().any(|s| {
6098                    s.elements
6099                        .first()
6100                        .and_then(|e: &Vec<String>| e.first())
6101                        .map(|v| v.as_str())
6102                        == Some("6")
6103                        && s.elements
6104                            .get(2)
6105                            .and_then(|e: &Vec<String>| e.first())
6106                            .map(|v| v.as_str())
6107                            == Some("ZA9")
6108                }) {
6109                    has_cci_za9 = true;
6110                    break;
6111                }
6112            }
6113            if !has_cci_za9 {
6114                return ConditionResult::True;
6115            }
6116        }
6117        ConditionResult::False
6118    }
6119
6120    /// [333] Wenn in diesem RFF \"Referenz auf die Lokationsbündelstruktur\" im DE1153 der Code Z31 \"Lokationsbündelstruktur\" vorhanden ist
6121    fn evaluate_333(&self, ctx: &EvaluationContext) -> ConditionResult {
6122        let rff_segs = ctx.find_segments("RFF");
6123        let found = rff_segs.iter().any(|s| {
6124            s.elements
6125                .first()
6126                .and_then(|e| e.first())
6127                .is_some_and(|v| v == "Z31")
6128        });
6129        ConditionResult::from(found)
6130    }
6131
6132    /// [334] Nur MP-ID aus Sparte Gas
6133    fn evaluate_334(&self, ctx: &EvaluationContext) -> ConditionResult {
6134        // Check: Marktpartner-ID is from Sparte Gas
6135        ctx.external.evaluate("sector_check")
6136    }
6137
6138    /// [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...
6139    // 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)
6140    fn evaluate_335(&self, ctx: &EvaluationContext) -> ConditionResult {
6141        // HAND-EDITED: the Zeitraum-ID of *this* SG8's SEQ+Z58 (DE1050), looked
6142        // for in the RFF+Z31 of any SG8 SEQ+Z78 of the message.
6143        let z58_ids: Vec<String> = ctx
6144            .scoped_find_segments_in_with_qualifier("SG8", "SEQ", 0, "Z58")
6145            .iter()
6146            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
6147            .filter(|v| !v.is_empty())
6148            .collect();
6149        if z58_ids.is_empty() {
6150            return ConditionResult::Unknown;
6151        }
6152        if ctx.find_segments_with_qualifier("SEQ", 0, "Z78").is_empty() {
6153            return ConditionResult::False;
6154        }
6155        let rff_z31_ids: Vec<String> = ctx
6156            .find_segments_with_qualifier("RFF", 0, "Z31")
6157            .iter()
6158            .filter_map(|rff| rff.elements.first().and_then(|e| e.get(1)).cloned())
6159            .filter(|v| !v.is_empty())
6160            .collect();
6161        ConditionResult::from(z58_ids.iter().any(|id| rff_z31_ids.contains(id)))
6162    }
6163
6164    /// [336] Wenn in Änderungsmeldung gefüllt
6165    fn evaluate_336(&self, _ctx: &EvaluationContext) -> ConditionResult {
6166        // TODO: implement
6167        ConditionResult::Unknown
6168    }
6169
6170    /// [337] Wenn SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96 vorhanden
6171    fn evaluate_337(&self, ctx: &EvaluationContext) -> ConditionResult {
6172        let cci_segs = ctx.find_segments("CCI");
6173        let has_cci = cci_segs.iter().any(|s| {
6174            s.elements
6175                .get(2)
6176                .and_then(|e| e.first())
6177                .is_some_and(|v| v == "ZB4")
6178        });
6179        let cav_segs = ctx.find_segments("CAV");
6180        let has_cav = cav_segs.iter().any(|s| {
6181            s.elements
6182                .first()
6183                .and_then(|e| e.first())
6184                .is_some_and(|v| ["Z95", "Z96"].contains(&v.as_str()))
6185        });
6186        ConditionResult::from(has_cci && has_cav)
6187    }
6188
6189    /// [338] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96/ Z97/ Z99/ ZA1/ ZA3/ ZA4/ZG7 vorhanden
6190    fn evaluate_338(&self, ctx: &EvaluationContext) -> ConditionResult {
6191        let cci_segs = ctx.find_segments("CCI");
6192        let has_cci = cci_segs.iter().any(|s| {
6193            s.elements
6194                .get(2)
6195                .and_then(|e| e.first())
6196                .is_some_and(|v| v == "ZB4")
6197        });
6198        let cav_segs = ctx.find_segments("CAV");
6199        let has_cav = cav_segs.iter().any(|s| {
6200            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6201                ["Z95", "Z96", "Z97", "Z99", "ZA1", "ZA3", "ZA4", "ZG7"].contains(&v.as_str())
6202            })
6203        });
6204        ConditionResult::from(has_cci && has_cav)
6205    }
6206
6207    /// [339] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z97/ Z98/ Z99/ ZA0/ ZA1/ ZA2/ ZA3/ ZA6/ ZG7 vorhanden
6208    fn evaluate_339(&self, ctx: &EvaluationContext) -> ConditionResult {
6209        let cci_segs = ctx.find_segments("CCI");
6210        let has_cci = cci_segs.iter().any(|s| {
6211            s.elements
6212                .get(2)
6213                .and_then(|e| e.first())
6214                .is_some_and(|v| v == "ZB4")
6215        });
6216        let cav_segs = ctx.find_segments("CAV");
6217        let has_cav = cav_segs.iter().any(|s| {
6218            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6219                [
6220                    "Z97", "Z98", "Z99", "ZA0", "ZA1", "ZA2", "ZA3", "ZA6", "ZG7",
6221                ]
6222                .contains(&v.as_str())
6223            })
6224        });
6225        ConditionResult::from(has_cci && has_cav)
6226    }
6227
6228    /// [340] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z97/ Z98/ Z99/ ZA1/ ZA2/ ZA3/ ZA4/ ZA5/ ZA6 vorhanden
6229    fn evaluate_340(&self, ctx: &EvaluationContext) -> ConditionResult {
6230        let cci_segs = ctx.find_segments("CCI");
6231        let has_cci = cci_segs.iter().any(|s| {
6232            s.elements
6233                .get(2)
6234                .and_then(|e| e.first())
6235                .is_some_and(|v| v == "ZB4")
6236        });
6237        let cav_segs = ctx.find_segments("CAV");
6238        let has_cav = cav_segs.iter().any(|s| {
6239            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6240                [
6241                    "Z95", "Z97", "Z98", "Z99", "ZA1", "ZA2", "ZA3", "ZA4", "ZA5", "ZA6",
6242                ]
6243                .contains(&v.as_str())
6244            })
6245        });
6246        ConditionResult::from(has_cci && has_cav)
6247    }
6248
6249    /// [341] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z96/ ZA0/ ZG7 vorhanden
6250    fn evaluate_341(&self, ctx: &EvaluationContext) -> ConditionResult {
6251        let cci_segs = ctx.find_segments("CCI");
6252        let has_cci = cci_segs.iter().any(|s| {
6253            s.elements
6254                .get(2)
6255                .and_then(|e| e.first())
6256                .is_some_and(|v| v == "ZB4")
6257        });
6258        let cav_segs = ctx.find_segments("CAV");
6259        let has_cav = cav_segs.iter().any(|s| {
6260            s.elements
6261                .first()
6262                .and_then(|e| e.first())
6263                .is_some_and(|v| ["Z96", "ZA0", "ZG7"].contains(&v.as_str()))
6264        });
6265        ConditionResult::from(has_cci && has_cav)
6266    }
6267
6268    /// [342] Wenn aktiver MaBiS-ZP für den Betrachtungszeitraum vorhanden
6269    fn evaluate_342(&self, _ctx: &EvaluationContext) -> ConditionResult {
6270        // TODO: implement
6271        ConditionResult::Unknown
6272    }
6273
6274    /// [344] Wenn in dieser SG8 das SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+ZF1 (Netzgangzeitreihe (NGZ)) vorhanden
6275    fn evaluate_344(&self, ctx: &EvaluationContext) -> ConditionResult {
6276        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6277        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6278        let has_cci = cci_segs.iter().any(|s| {
6279            s.elements
6280                .get(2)
6281                .and_then(|e| e.first())
6282                .is_some_and(|v| v == "ZB4")
6283        });
6284        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
6285        let has_cav = cav_segs.iter().any(|s| {
6286            s.elements
6287                .first()
6288                .and_then(|e| e.first())
6289                .is_some_and(|v| ["ZF1"].contains(&v.as_str()))
6290        });
6291        ConditionResult::from(has_cci && has_cav)
6292    }
6293
6294    /// [345] Wenn SG5 LOC+Z17 (Messlokation) vorhanden
6295    fn evaluate_345(&self, ctx: &EvaluationContext) -> ConditionResult {
6296        let loc_segs = ctx.find_segments("LOC");
6297        let found = loc_segs.iter().any(|s| {
6298            s.elements
6299                .first()
6300                .and_then(|e| e.first())
6301                .is_some_and(|v| v == "Z17")
6302        });
6303        ConditionResult::from(found)
6304    }
6305
6306    /// [346] Wenn SG10 CCI+++E13 (Zählertyp) CAV+MME (mME) vorhanden
6307    fn evaluate_346(&self, ctx: &EvaluationContext) -> ConditionResult {
6308        // HAND-EDITED: the CCI and the CAV of one SG10.
6309        sg10_has(ctx, false, |segs| {
6310            sg4_instances::has_cci(segs, "", "E13") && sg4_instances::has_cav(segs, "MME")
6311        })
6312    }
6313
6314    /// [347] Wenn SG5 LOC+Z16 (Marktlokation) nicht vorhanden
6315    fn evaluate_347(&self, ctx: &EvaluationContext) -> ConditionResult {
6316        let loc_segs = ctx.find_segments("LOC");
6317        let found = loc_segs.iter().any(|s| {
6318            s.elements
6319                .first()
6320                .and_then(|e| e.first())
6321                .is_some_and(|v| v == "Z16")
6322        });
6323        ConditionResult::from(!found)
6324    }
6325
6326    /// [348] Wenn SG5 LOC+Z21 (Tranche) nicht vorhanden
6327    fn evaluate_348(&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 == "Z21")
6334        });
6335        ConditionResult::from(!found)
6336    }
6337
6338    /// [349] Wenn SG5 LOC+Z17 (Messlokation) nicht vorhanden
6339    fn evaluate_349(&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 == "Z17")
6346        });
6347        ConditionResult::from(!found)
6348    }
6349
6350    /// [350] Wenn SG8 SEQ+Z78/ZD5 RFF+Z31 (Referenz auf die Lokationsbündelstruktur) vorhanden
6351    fn evaluate_350(&self, ctx: &EvaluationContext) -> ConditionResult {
6352        let seq_segs = ctx.find_segments("SEQ");
6353        let has_seq = seq_segs.iter().any(|s| {
6354            s.elements
6355                .first()
6356                .and_then(|e| e.first())
6357                .is_some_and(|v| ["Z78", "ZD5"].contains(&v.as_str()))
6358        });
6359        let rff_segs = ctx.find_segments("RFF");
6360        let has_rff = rff_segs.iter().any(|s| {
6361            s.elements
6362                .first()
6363                .and_then(|e| e.first())
6364                .is_some_and(|v| v == "Z31")
6365        });
6366        ConditionResult::from(has_seq && has_rff)
6367    }
6368
6369    /// [351] Wenn SG4 STS+E01++A04/ A05 /A13 /A14 (Status der Antwort) vorhanden
6370    fn evaluate_351(&self, ctx: &EvaluationContext) -> ConditionResult {
6371        let sts_segs = ctx.find_segments("STS");
6372        let found = sts_segs.iter().any(|s| {
6373            s.elements
6374                .first()
6375                .and_then(|e| e.first())
6376                .is_some_and(|v| v == "E01")
6377                && s.elements
6378                    .get(2)
6379                    .and_then(|e| e.first())
6380                    .is_some_and(|v| ["A04", "A05", "A13", "A14"].contains(&v.as_str()))
6381        });
6382        ConditionResult::from(found)
6383    }
6384
6385    /// [352] Wenn SG4 STS++A05/ A06/ A14 /A15 (Status der Antwort) vorhanden
6386    fn evaluate_352(&self, ctx: &EvaluationContext) -> ConditionResult {
6387        let sts_segs = ctx.find_segments("STS");
6388        let found = sts_segs.iter().any(|s| {
6389            s.elements
6390                .get(1)
6391                .and_then(|e| e.first())
6392                .is_some_and(|v| ["A05", "A06", "A14", "A15"].contains(&v.as_str()))
6393        });
6394        ConditionResult::from(found)
6395    }
6396
6397    /// [355] Wenn SG4 STS+E01++A45 (Status der Antwort) vorhanden
6398    fn evaluate_355(&self, ctx: &EvaluationContext) -> ConditionResult {
6399        let sts_segs = ctx.find_segments("STS");
6400        let found = sts_segs.iter().any(|s| {
6401            s.elements
6402                .first()
6403                .and_then(|e| e.first())
6404                .is_some_and(|v| v == "E01")
6405                && s.elements
6406                    .get(2)
6407                    .and_then(|e| e.first())
6408                    .is_some_and(|v| ["A45"].contains(&v.as_str()))
6409        });
6410        ConditionResult::from(found)
6411    }
6412
6413    /// [356] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6414    fn evaluate_356(&self, ctx: &EvaluationContext) -> ConditionResult {
6415        let sts_segs = ctx.find_segments("STS");
6416        let found = sts_segs.iter().any(|s| {
6417            s.elements
6418                .first()
6419                .and_then(|e| e.first())
6420                .is_some_and(|v| v == "E01")
6421                && s.elements
6422                    .get(2)
6423                    .and_then(|e| e.first())
6424                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6425        });
6426        ConditionResult::from(found)
6427    }
6428
6429    /// [357] Wenn SG4 STS+E01++A03/ A09/ A12/ A17 (Status der Antwort) vorhanden
6430    fn evaluate_357(&self, ctx: &EvaluationContext) -> ConditionResult {
6431        let sts_segs = ctx.find_segments("STS");
6432        let found = sts_segs.iter().any(|s| {
6433            s.elements
6434                .first()
6435                .and_then(|e| e.first())
6436                .is_some_and(|v| v == "E01")
6437                && s.elements
6438                    .get(2)
6439                    .and_then(|e| e.first())
6440                    .is_some_and(|v| ["A03", "A09", "A12", "A17"].contains(&v.as_str()))
6441        });
6442        ConditionResult::from(found)
6443    }
6444
6445    /// [358] Wenn SG4 STS+E01++A06 (Status der Antwort) vorhanden
6446    fn evaluate_358(&self, ctx: &EvaluationContext) -> ConditionResult {
6447        let sts_segs = ctx.find_segments("STS");
6448        let found = sts_segs.iter().any(|s| {
6449            s.elements
6450                .first()
6451                .and_then(|e| e.first())
6452                .is_some_and(|v| v == "E01")
6453                && s.elements
6454                    .get(2)
6455                    .and_then(|e| e.first())
6456                    .is_some_and(|v| ["A06"].contains(&v.as_str()))
6457        });
6458        ConditionResult::from(found)
6459    }
6460
6461    /// [359] Es sind nur Antwortcodes aus dem Cluster Ablehnung erlaubt
6462    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6463    // Preserve across regeneration until the codegen prompt is updated.
6464    fn evaluate_359(&self, ctx: &EvaluationContext) -> ConditionResult {
6465        all_sts_e01_in_cluster(ctx, Cluster::is_ablehnung)
6466    }
6467
6468    /// [360] Es sind nur Antwortcodes aus dem Cluster Zustimmung erlaubt
6469    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6470    // Preserve across regeneration until the codegen prompt is updated.
6471    fn evaluate_360(&self, ctx: &EvaluationContext) -> ConditionResult {
6472        all_sts_e01_in_cluster(ctx, Cluster::is_zustimmung)
6473    }
6474
6475    /// [363] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6476    fn evaluate_363(&self, ctx: &EvaluationContext) -> ConditionResult {
6477        let sts_segs = ctx.find_segments("STS");
6478        let found = sts_segs.iter().any(|s| {
6479            s.elements
6480                .first()
6481                .and_then(|e| e.first())
6482                .is_some_and(|v| v == "E01")
6483                && s.elements
6484                    .get(2)
6485                    .and_then(|e| e.first())
6486                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6487        });
6488        ConditionResult::from(found)
6489    }
6490
6491    /// [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
6492    fn evaluate_365(&self, ctx: &EvaluationContext) -> ConditionResult {
6493        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6494        // Check: STS+E01 does NOT have A04 with E_0047/E_0014 or A03 with E_0049/E_0004
6495        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6496        let has_excluded = sts_segs.iter().any(|s| {
6497            let code = s
6498                .elements
6499                .get(2)
6500                .and_then(|e| e.first())
6501                .map(|v| v.as_str());
6502            let ebd = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
6503            matches!(
6504                (code, ebd),
6505                (Some("A04"), Some("E_0047"))
6506                    | (Some("A04"), Some("E_0014"))
6507                    | (Some("A03"), Some("E_0049"))
6508                    | (Some("A03"), Some("E_0004"))
6509            )
6510        });
6511        ConditionResult::from(!has_excluded)
6512    }
6513
6514    /// [366] Bis auf den Code A30 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6515    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6516    // Preserve across regeneration until the codegen prompt is updated.
6517    fn evaluate_366(&self, ctx: &EvaluationContext) -> ConditionResult {
6518        ebd_ablehnung_except(ctx, "E_0624", &["A30"])
6519    }
6520
6521    /// [367] Wenn SG4 STS+E01++A05/ A14 (Status der Antwort) vorhanden
6522    fn evaluate_367(&self, ctx: &EvaluationContext) -> ConditionResult {
6523        let sts_segs = ctx.find_segments("STS");
6524        let found = sts_segs.iter().any(|s| {
6525            s.elements
6526                .first()
6527                .and_then(|e| e.first())
6528                .is_some_and(|v| v == "E01")
6529                && s.elements
6530                    .get(2)
6531                    .and_then(|e| e.first())
6532                    .is_some_and(|v| ["A05", "A14"].contains(&v.as_str()))
6533        });
6534        ConditionResult::from(found)
6535    }
6536
6537    /// [368] Bis auf den Code A41 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6538    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6539    // Preserve across regeneration until the codegen prompt is updated.
6540    fn evaluate_368(&self, ctx: &EvaluationContext) -> ConditionResult {
6541        ebd_ablehnung_except(ctx, "E_0624", &["A41"])
6542    }
6543
6544    /// [370] Wenn SG10 CAV+TLS/TES/BIT/GET/GAT/SOT/WNT/WFT/WAT
6545    fn evaluate_370(&self, ctx: &EvaluationContext) -> ConditionResult {
6546        let cav_segs = ctx.find_segments("CAV");
6547        let found = cav_segs.iter().any(|s| {
6548            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6549                [
6550                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
6551                ]
6552                .contains(&v.as_str())
6553            })
6554        });
6555        ConditionResult::from(found)
6556    }
6557
6558    /// [371] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
6559    fn evaluate_371(&self, ctx: &EvaluationContext) -> ConditionResult {
6560        let seq_segs = ctx.find_segments("SEQ");
6561        let found = seq_segs.iter().any(|s| {
6562            s.elements
6563                .first()
6564                .and_then(|e| e.first())
6565                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
6566        });
6567        ConditionResult::from(!found)
6568    }
6569
6570    /// [372] Wenn SG10 CAV+TES/BIT/GET/GAT/SOT/WNT/WFT/WAT vorhanden
6571    fn evaluate_372(&self, ctx: &EvaluationContext) -> ConditionResult {
6572        let cav_segs = ctx.find_segments("CAV");
6573        let found = cav_segs.iter().any(|s| {
6574            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6575                ["TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT"].contains(&v.as_str())
6576            })
6577        });
6578        ConditionResult::from(found)
6579    }
6580
6581    /// [373] Wenn ein Referenzprofil an der Marktlokation vorhanden ist
6582    fn evaluate_373(&self, _ctx: &EvaluationContext) -> ConditionResult {
6583        // TODO: implement
6584        ConditionResult::Unknown
6585    }
6586
6587    /// [375] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z24 (Daten der Überführungszeitreihe) nicht vorhanden
6588    fn evaluate_375(&self, ctx: &EvaluationContext) -> ConditionResult {
6589        // Check: no SG8 SEQ+Z24 (Daten der Überführungszeitreihe) exists
6590        ctx.lacks_qualifier("SEQ", 0, "Z24")
6591    }
6592
6593    /// [376] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z22 (Daten der Summenzeitreihe) nicht vorhanden
6594    fn evaluate_376(&self, ctx: &EvaluationContext) -> ConditionResult {
6595        // Check: no SG8 SEQ+Z22 (Daten der Summenzeitreihe) exists
6596        ctx.lacks_qualifier("SEQ", 0, "Z22")
6597    }
6598
6599    /// [377] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS vorhanden
6600    // 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)
6601    fn evaluate_377(&self, ctx: &EvaluationContext) -> ConditionResult {
6602        let nav = match ctx.navigator {
6603            Some(n) => n,
6604            None => return ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]),
6605        };
6606        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6607        for i in 0..sg8_count {
6608            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6609            for j in 0..sg10_count {
6610                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6611                let has_cci = ccis.iter().any(|s| {
6612                    s.elements
6613                        .first()
6614                        .and_then(|e: &Vec<String>| e.first())
6615                        .is_some_and(|v: &String| v == "15")
6616                        && s.elements
6617                            .get(2)
6618                            .and_then(|e: &Vec<String>| e.first())
6619                            .is_some_and(|v: &String| v == "Z21")
6620                });
6621                if has_cci {
6622                    let cavs =
6623                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6624                    if cavs.iter().any(|s| {
6625                        s.elements
6626                            .first()
6627                            .and_then(|e: &Vec<String>| e.first())
6628                            .is_some_and(|v: &String| v == "AUS")
6629                    }) {
6630                        return ConditionResult::True;
6631                    }
6632                }
6633            }
6634        }
6635        ConditionResult::False
6636    }
6637
6638    /// [378] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS nicht vorhanden
6639    // 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)
6640    fn evaluate_378(&self, ctx: &EvaluationContext) -> ConditionResult {
6641        let nav = match ctx.navigator {
6642            Some(n) => n,
6643            None => {
6644                let r = ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]);
6645                return match r {
6646                    ConditionResult::True => ConditionResult::False,
6647                    ConditionResult::False => ConditionResult::True,
6648                    ConditionResult::Unknown => ConditionResult::Unknown,
6649                };
6650            }
6651        };
6652        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6653        for i in 0..sg8_count {
6654            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6655            for j in 0..sg10_count {
6656                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6657                let has_cci = ccis.iter().any(|s| {
6658                    s.elements
6659                        .first()
6660                        .and_then(|e: &Vec<String>| e.first())
6661                        .is_some_and(|v: &String| v == "15")
6662                        && s.elements
6663                            .get(2)
6664                            .and_then(|e: &Vec<String>| e.first())
6665                            .is_some_and(|v: &String| v == "Z21")
6666                });
6667                if has_cci {
6668                    let cavs =
6669                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6670                    if cavs.iter().any(|s| {
6671                        s.elements
6672                            .first()
6673                            .and_then(|e: &Vec<String>| e.first())
6674                            .is_some_and(|v: &String| v == "AUS")
6675                    }) {
6676                        return ConditionResult::False;
6677                    }
6678                }
6679            }
6680        }
6681        ConditionResult::True
6682    }
6683
6684    /// [379] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+ Code für EEG-Überführungszeitreihen (ausgenommen AU1) vorhanden
6685    fn evaluate_379(&self, ctx: &EvaluationContext) -> ConditionResult {
6686        let cci_segs = ctx.find_segments("CCI");
6687        let found = cci_segs.iter().any(|s| {
6688            s.elements
6689                .first()
6690                .and_then(|e| e.first())
6691                .is_some_and(|v| v == "15")
6692                && s.elements
6693                    .get(2)
6694                    .and_then(|e| e.first())
6695                    .is_some_and(|v| ["Z21"].contains(&v.as_str()))
6696        });
6697        ConditionResult::from(found)
6698    }
6699
6700    /// [380] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS/AU1 vorhanden
6701    // 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)
6702    fn evaluate_380(&self, ctx: &EvaluationContext) -> ConditionResult {
6703        let nav = match ctx.navigator {
6704            Some(n) => n,
6705            None => {
6706                return ctx.any_group_has_any_qualifier("CAV", 0, &["AUS", "AU1"], &["SG4", "SG8"])
6707            }
6708        };
6709        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6710        for i in 0..sg8_count {
6711            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6712            for j in 0..sg10_count {
6713                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6714                let has_cci = ccis.iter().any(|s| {
6715                    s.elements
6716                        .first()
6717                        .and_then(|e: &Vec<String>| e.first())
6718                        .is_some_and(|v: &String| v == "15")
6719                        && s.elements
6720                            .get(2)
6721                            .and_then(|e: &Vec<String>| e.first())
6722                            .is_some_and(|v: &String| v == "Z21")
6723                });
6724                if has_cci {
6725                    let cavs =
6726                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6727                    if cavs.iter().any(|s| {
6728                        s.elements
6729                            .first()
6730                            .and_then(|e: &Vec<String>| e.first())
6731                            .is_some_and(|v: &String| v == "AUS" || v == "AU1")
6732                    }) {
6733                        return ConditionResult::True;
6734                    }
6735                }
6736            }
6737        }
6738        ConditionResult::False
6739    }
6740
6741    /// [384] Wenn in derselben SG8 SG10 CCI+Z37++ZD1 (Basis zur Bildung der Tranchengröße) (Prozentual) vorhanden
6742    fn evaluate_384(&self, ctx: &EvaluationContext) -> ConditionResult {
6743        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6744        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6745        let found = cci_segs.iter().any(|s| {
6746            s.elements
6747                .first()
6748                .and_then(|e| e.first())
6749                .is_some_and(|v| v == "Z37")
6750                && s.elements
6751                    .get(2)
6752                    .and_then(|e| e.first())
6753                    .is_some_and(|v| v == "ZD1")
6754        });
6755        ConditionResult::from(found)
6756    }
6757
6758    /// [386] Wenn mehr als eine SG8 SEQ+Z02/ ZE3 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden
6759    fn evaluate_386(&self, ctx: &EvaluationContext) -> ConditionResult {
6760        // Check: more than one SG8 SEQ+Z02/ZE3 with OBIS energy quantity in PIA+5
6761        let seq_count = ctx
6762            .find_segments("SEQ")
6763            .iter()
6764            .filter(|s| {
6765                s.elements
6766                    .first()
6767                    .and_then(|e| e.first())
6768                    .is_some_and(|v| v == "Z02" || v == "ZE3")
6769            })
6770            .count();
6771        ConditionResult::from(seq_count > 1)
6772    }
6773
6774    /// [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...
6775    fn evaluate_387(&self, ctx: &EvaluationContext) -> ConditionResult {
6776        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6777        // Check: same SG8 (OBIS Daten) has OBIS energy quantity code in PIA+5
6778        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6779        let found = pia_segs.iter().any(|s| {
6780            s.elements
6781                .first()
6782                .and_then(|e| e.first())
6783                .is_some_and(|v| v == "5")
6784                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6785                    v.starts_with("1-")
6786                        && (v.contains(":1.8.")
6787                            || v.contains(":2.8.")
6788                            || v.contains(":1.9.")
6789                            || v.contains(":2.9."))
6790                })
6791        });
6792        ConditionResult::from(found)
6793    }
6794
6795    /// [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...
6796    fn evaluate_388(&self, ctx: &EvaluationContext) -> ConditionResult {
6797        // Check: at least one RFF+Z49 (Zeitraum) exists
6798        let found = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6799        ConditionResult::from(found)
6800    }
6801
6802    /// [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...
6803    fn evaluate_391(&self, ctx: &EvaluationContext) -> ConditionResult {
6804        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6805        // Check: OBIS Zählerstand (meter reading) code in PIA+5 within SEQ+Z20
6806        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6807        let found = pia_segs.iter().any(|s| {
6808            s.elements
6809                .first()
6810                .and_then(|e| e.first())
6811                .is_some_and(|v| v == "5")
6812                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6813                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
6814                })
6815        });
6816        ConditionResult::from(found)
6817    }
6818
6819    /// [392] Wenn in diesem PIA der Code im Format n1-n2-n1 angegeben ist
6820    fn evaluate_392(&self, ctx: &EvaluationContext) -> ConditionResult {
6821        // Check: PIA code in format n1-n2-n1 (e.g., "1-08-1")
6822        let pia_segs = ctx.find_segments("PIA");
6823        let found = pia_segs.iter().any(|s| {
6824            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6825                let parts: Vec<&str> = v.split('-').collect();
6826                parts.len() == 3
6827                    && parts[0].len() == 1
6828                    && parts[0].chars().all(|c| c.is_ascii_digit())
6829                    && parts[1].len() == 2
6830                    && parts[1].chars().all(|c| c.is_ascii_digit())
6831                    && parts[2].len() == 1
6832                    && parts[2].chars().all(|c| c.is_ascii_digit())
6833            })
6834        });
6835        ConditionResult::from(found)
6836    }
6837
6838    /// [393] Wenn in diesem PIA der Code im Format n1-n2-n1-n3 angegeben ist
6839    fn evaluate_393(&self, ctx: &EvaluationContext) -> ConditionResult {
6840        // Check: PIA code in format n1-n2-n1-n3 (e.g., "1-08-1-001")
6841        let pia_segs = ctx.find_segments("PIA");
6842        let found = pia_segs.iter().any(|s| {
6843            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6844                let parts: Vec<&str> = v.split('-').collect();
6845                parts.len() == 4
6846                    && parts[0].len() == 1
6847                    && parts[0].chars().all(|c| c.is_ascii_digit())
6848                    && parts[1].len() == 2
6849                    && parts[1].chars().all(|c| c.is_ascii_digit())
6850                    && parts[2].len() == 1
6851                    && parts[2].chars().all(|c| c.is_ascii_digit())
6852                    && parts[3].len() == 3
6853                    && parts[3].chars().all(|c| c.is_ascii_digit())
6854            })
6855        });
6856        ConditionResult::from(found)
6857    }
6858
6859    /// [394] Wenn in diesem PIA der Code im Format n1-n2-n1-n8-n2 angegeben ist
6860    fn evaluate_394(&self, ctx: &EvaluationContext) -> ConditionResult {
6861        // Check: PIA code in format n1-n2-n1-n8-n2
6862        let pia_segs = ctx.find_segments("PIA");
6863        let found = pia_segs.iter().any(|s| {
6864            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6865                let parts: Vec<&str> = v.split('-').collect();
6866                parts.len() == 5
6867                    && parts[0].len() == 1
6868                    && parts[0].chars().all(|c| c.is_ascii_digit())
6869                    && parts[1].len() == 2
6870                    && parts[1].chars().all(|c| c.is_ascii_digit())
6871                    && parts[2].len() == 1
6872                    && parts[2].chars().all(|c| c.is_ascii_digit())
6873                    && parts[3].len() == 8
6874                    && parts[3].chars().all(|c| c.is_ascii_digit())
6875                    && parts[4].len() == 2
6876                    && parts[4].chars().all(|c| c.is_ascii_digit())
6877            })
6878        });
6879        ConditionResult::from(found)
6880    }
6881
6882    /// [395] Wenn in diesem PIA der Code im Format n1-n2-n1-n8 angegeben ist
6883    fn evaluate_395(&self, ctx: &EvaluationContext) -> ConditionResult {
6884        // Check: PIA code in format n1-n2-n1-n8
6885        let pia_segs = ctx.find_segments("PIA");
6886        let found = pia_segs.iter().any(|s| {
6887            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6888                let parts: Vec<&str> = v.split('-').collect();
6889                parts.len() == 4
6890                    && parts[0].len() == 1
6891                    && parts[0].chars().all(|c| c.is_ascii_digit())
6892                    && parts[1].len() == 2
6893                    && parts[1].chars().all(|c| c.is_ascii_digit())
6894                    && parts[2].len() == 1
6895                    && parts[2].chars().all(|c| c.is_ascii_digit())
6896                    && parts[3].len() == 8
6897                    && parts[3].chars().all(|c| c.is_ascii_digit())
6898            })
6899        });
6900        ConditionResult::from(found)
6901    }
6902
6903    /// [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
6904    fn evaluate_396(&self, ctx: &EvaluationContext) -> ConditionResult {
6905        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6906        // Check: OBIS Wirkarbeit und 1/4 Stunde code in PIA+5
6907        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6908        let found = pia_segs.iter().any(|s| {
6909            s.elements
6910                .first()
6911                .and_then(|e| e.first())
6912                .is_some_and(|v| v == "5")
6913                && s.elements
6914                    .get(1)
6915                    .and_then(|e| e.first())
6916                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
6917        });
6918        ConditionResult::from(found)
6919    }
6920
6921    /// [397] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/1-08-4 beginnt
6922    fn evaluate_397(&self, ctx: &EvaluationContext) -> ConditionResult {
6923        let pia_segs = ctx.find_segments("PIA");
6924        let found = pia_segs.iter().any(|s| {
6925            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6926                let prefixes = ["1-08-1", "1-08-4"];
6927                prefixes.iter().any(|p| v.starts_with(p))
6928            })
6929        });
6930        ConditionResult::from(found)
6931    }
6932
6933    /// [398] Wenn in diesem PIA der Code im DE7140 mit 1-08-2/1-08-5 beginnt
6934    fn evaluate_398(&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-2", "1-08-5"];
6939                prefixes.iter().any(|p| v.starts_with(p))
6940            })
6941        });
6942        ConditionResult::from(found)
6943    }
6944
6945    /// [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...
6946    // 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)
6947    fn evaluate_399(&self, ctx: &EvaluationContext) -> ConditionResult {
6948        let nav = match ctx.navigator() {
6949            Some(n) => n,
6950            None => return ConditionResult::Unknown,
6951        };
6952        // Collect DE3224 values from LOC segments in SG5 — LOC.C517.DE3224 = elements[1][3]
6953        // These serve as the Zeitraum-IDs referenced by the condition's "dieses Segments"
6954        let sg5_count = nav.group_instance_count(&["SG4", "SG5"]);
6955        let mut loc_zeitraum_ids: Vec<String> = Vec::new();
6956        for i in 0..sg5_count {
6957            let locs = nav.find_segments_in_group("LOC", &["SG4", "SG5"], i);
6958            for loc in &locs {
6959                if let Some(zid) = loc.elements.get(1).and_then(|e| e.get(3)) {
6960                    if !zid.is_empty() {
6961                        loc_zeitraum_ids.push(zid.clone());
6962                    }
6963                }
6964            }
6965        }
6966        if loc_zeitraum_ids.is_empty() {
6967            return ConditionResult::Unknown;
6968        }
6969        let pauschal_id = "9992000000018";
6970        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6971        // Find any Z78 SG8 whose Zeitraum-ID (SEQ.C286.DE1050) matches a LOC DE3224
6972        // and that has RFF+Z31 with DE1154 != pauschal_id
6973        for i in 0..sg8_count {
6974            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
6975            for seq in &seqs {
6976                if !seq
6977                    .elements
6978                    .first()
6979                    .and_then(|e| e.first())
6980                    .is_some_and(|v| v == "Z78")
6981                {
6982                    continue;
6983                }
6984                let zid = seq
6985                    .elements
6986                    .get(1)
6987                    .and_then(|e| e.first())
6988                    .map(|s| s.as_str())
6989                    .unwrap_or("");
6990                if !loc_zeitraum_ids.iter().any(|z| z == zid) {
6991                    continue;
6992                }
6993                // Matching Z78 SG8 found — check RFF+Z31 DE1154
6994                let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
6995                for rff in &rffs {
6996                    let qual = rff
6997                        .elements
6998                        .first()
6999                        .and_then(|e| e.first())
7000                        .map(|v| v.as_str())
7001                        .unwrap_or("");
7002                    if qual == "Z31" {
7003                        let id = rff
7004                            .elements
7005                            .first()
7006                            .and_then(|e| e.get(1))
7007                            .map(|v| v.as_str())
7008                            .unwrap_or("");
7009                        let normalized = id.replace(' ', "");
7010                        if !normalized.is_empty() && normalized != pauschal_id {
7011                            return ConditionResult::True;
7012                        }
7013                    }
7014                }
7015            }
7016        }
7017        ConditionResult::False
7018    }
7019
7020    /// [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+...
7021    fn evaluate_401(&self, ctx: &EvaluationContext) -> ConditionResult {
7022        // Check: this DTM+Z25 is not in SG6 RFF+Z49 or Z53
7023        // Approximate: check if RFF+Z49 or Z53 exists
7024        let has_z49 = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
7025        let has_z53 = !ctx.find_segments_with_qualifier("RFF", 0, "Z53").is_empty();
7026        ConditionResult::from(!has_z49 && !has_z53)
7027    }
7028
7029    /// [402] Wenn in derselben SG8  (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl der Werteart \"Zählerstand\" im PIA+5 vorhanden
7030    fn evaluate_402(&self, ctx: &EvaluationContext) -> ConditionResult {
7031        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
7032        // Check: OBIS Zählerstand code in PIA+5
7033        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7034        let found = pia_segs.iter().any(|s| {
7035            s.elements
7036                .first()
7037                .and_then(|e| e.first())
7038                .is_some_and(|v| v == "5")
7039                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7040                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
7041                })
7042        });
7043        ConditionResult::from(found)
7044    }
7045
7046    /// [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
7047    // 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)
7048    fn evaluate_403(&self, ctx: &EvaluationContext) -> ConditionResult {
7049        let sender_mp_id: String = match ctx.find_segments_with_qualifier("NAD", 0, "MS").first() {
7050            Some(nad) => match nad.elements.get(1).and_then(|e: &Vec<String>| e.first()) {
7051                Some(id) if !id.is_empty() => id.clone(),
7052                _ => return ConditionResult::Unknown,
7053            },
7054            None => return ConditionResult::Unknown,
7055        };
7056        let nav = match ctx.navigator {
7057            Some(n) => n,
7058            None => return ConditionResult::Unknown,
7059        };
7060        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7061        for i in 0..sg8_count {
7062            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7063            for j in 0..sg10_count {
7064                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7065                let has_cci_zb3 = ccis.iter().any(|s| {
7066                    s.elements
7067                        .get(2)
7068                        .and_then(|e: &Vec<String>| e.first())
7069                        .is_some_and(|v: &String| v == "ZB3")
7070                });
7071                if !has_cci_zb3 {
7072                    continue;
7073                }
7074                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
7075                for cav in &cavs {
7076                    if cav
7077                        .elements
7078                        .first()
7079                        .and_then(|e: &Vec<String>| e.first())
7080                        .is_some_and(|v: &String| v == "Z91")
7081                    {
7082                        if let Some(msb_id) = cav.elements.first().and_then(|e| e.get(1)) {
7083                            if !msb_id.is_empty() {
7084                                return ConditionResult::from(
7085                                    sender_mp_id.as_str() != msb_id.as_str(),
7086                                );
7087                            }
7088                        }
7089                    }
7090                }
7091            }
7092        }
7093        ConditionResult::Unknown
7094    }
7095
7096    /// [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
7097    // 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)
7098    fn evaluate_404(&self, ctx: &EvaluationContext) -> ConditionResult {
7099        let mr_nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
7100        let mr_mp_id = match mr_nads.first() {
7101            Some(nad) => nad
7102                .elements
7103                .get(1)
7104                .and_then(|e: &Vec<String>| e.first())
7105                .cloned()
7106                .unwrap_or_default(),
7107            None => return ConditionResult::Unknown,
7108        };
7109        if mr_mp_id.is_empty() {
7110            return ConditionResult::Unknown;
7111        }
7112        // HAND-EDITED: "im SG8 SEQ+Z01/ Z98 … SG10 CCI+++ZB3 CAV+Z91" — the MSB in
7113        // the Marktlokation data of this SG4, of the MaLo this SG8 refers to
7114        // (RFF+Z18) where it refers to one, when the scope reaches an SG8. Any
7115        // SG8 of the message (the MeLo's SEQ+Z18 names the recipient by design)
7116        // otherwise, as before.
7117        if let Some(scope) = ctx.enclosing_scope("SG8") {
7118            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
7119            let names_recipient = |s: &[OwnedSegment]| {
7120                sg4_instances::has_cci(s, "", "ZB3")
7121                    && s.iter().any(|c| {
7122                        c.id == "CAV"
7123                            && c.get_element(0) == "Z91"
7124                            && c.get_component(0, 1) == mr_mp_id
7125                    })
7126            };
7127            let same = sg4_instances::sg8s(ctx).iter().any(|p| {
7128                sg4_instances::seq_at(ctx, p)
7129                    .is_some_and(|(code, _)| code == "Z01" || code == "Z98")
7130                    && (refs.is_empty()
7131                        || sg4_instances::rff_values_at(ctx, p, "Z18")
7132                            .iter()
7133                            .any(|v| refs.contains(v)))
7134                    && sg4_instances::sg10_at(ctx, p, names_recipient)
7135            });
7136            return ConditionResult::from(!same);
7137        }
7138
7139        let nav = match ctx.navigator {
7140            Some(n) => n,
7141            None => {
7142                let cavs = ctx.find_segments("CAV");
7143                let same_id_exists = cavs.iter().any(|s| {
7144                    s.elements
7145                        .first()
7146                        .and_then(|e: &Vec<String>| e.first())
7147                        .is_some_and(|v: &String| v == "Z91")
7148                        && s.elements
7149                            .first()
7150                            .and_then(|e| e.get(1))
7151                            .is_some_and(|v: &String| v == &mr_mp_id)
7152                });
7153                return ConditionResult::from(!same_id_exists);
7154            }
7155        };
7156
7157        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7158        for i in 0..sg8_count {
7159            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7160            for j in 0..sg10_count {
7161                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7162                let has_cci_zb3 = ccis.iter().any(|s| {
7163                    s.elements
7164                        .get(2)
7165                        .and_then(|e: &Vec<String>| e.first())
7166                        .is_some_and(|v: &String| v == "ZB3")
7167                });
7168                if !has_cci_zb3 {
7169                    continue;
7170                }
7171                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
7172                for cav in &cavs {
7173                    if cav
7174                        .elements
7175                        .first()
7176                        .and_then(|e: &Vec<String>| e.first())
7177                        .is_some_and(|v: &String| v == "Z91")
7178                    {
7179                        let cav_mp_id = cav
7180                            .elements
7181                            .first()
7182                            .and_then(|e| e.get(1))
7183                            .map(|s| s.as_str())
7184                            .unwrap_or("");
7185                        if cav_mp_id == mr_mp_id.as_str() {
7186                            return ConditionResult::False;
7187                        }
7188                    }
7189                }
7190            }
7191        }
7192
7193        ConditionResult::True
7194    }
7195
7196    /// [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
7197    fn evaluate_405(&self, ctx: &EvaluationContext) -> ConditionResult {
7198        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7199        // Check: OBIS Wirkarbeit kumuliert code in PIA+5
7200        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7201        let found = pia_segs.iter().any(|s| {
7202            s.elements
7203                .first()
7204                .and_then(|e| e.first())
7205                .is_some_and(|v| v == "5")
7206                && s.elements
7207                    .get(1)
7208                    .and_then(|e| e.first())
7209                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7210        });
7211        ConditionResult::from(found)
7212    }
7213
7214    /// [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
7215    fn evaluate_406(&self, ctx: &EvaluationContext) -> ConditionResult {
7216        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7217        // Check: OBIS Wirkarbeit höchste 1/4 Stunde code in PIA+5
7218        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7219        let found = pia_segs.iter().any(|s| {
7220            s.elements
7221                .first()
7222                .and_then(|e| e.first())
7223                .is_some_and(|v| v == "5")
7224                && s.elements
7225                    .get(1)
7226                    .and_then(|e| e.first())
7227                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7228        });
7229        ConditionResult::from(found)
7230    }
7231
7232    /// [407] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Blindarbeit im PIA+5 vorhanden
7233    fn evaluate_407(&self, ctx: &EvaluationContext) -> ConditionResult {
7234        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7235        // Check: OBIS Blindarbeit code in PIA+5
7236        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7237        let found = pia_segs.iter().any(|s| {
7238            s.elements
7239                .first()
7240                .and_then(|e| e.first())
7241                .is_some_and(|v| v == "5")
7242                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7243                    v.starts_with("1-") && (v.contains(":3.8.") || v.contains(":4.8."))
7244                })
7245        });
7246        ConditionResult::from(found)
7247    }
7248
7249    /// [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...
7250    fn evaluate_408(&self, ctx: &EvaluationContext) -> ConditionResult {
7251        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7252        // reaches an SG8; the message as before otherwise.
7253        if let Some(scope) = ctx.enclosing_scope("SG8") {
7254            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7255                ctx,
7256                scope,
7257                is_obis_wirkarbeit_kumuliert,
7258            ));
7259        }
7260        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7261        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7262        ConditionResult::from(has_seq_z02)
7263    }
7264
7265    /// [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 ...
7266    fn evaluate_409(&self, ctx: &EvaluationContext) -> ConditionResult {
7267        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7268        // reaches an SG8; the message as before otherwise.
7269        if let Some(scope) = ctx.enclosing_scope("SG8") {
7270            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7271                ctx,
7272                scope,
7273                is_obis_wirkarbeit_kumuliert,
7274            ));
7275        }
7276        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7277        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7278        ConditionResult::from(!has_seq_z02)
7279    }
7280
7281    /// [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...
7282    fn evaluate_410(&self, ctx: &EvaluationContext) -> ConditionResult {
7283        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7284        // reaches an SG8; the message as before otherwise.
7285        if let Some(scope) = ctx.enclosing_scope("SG8") {
7286            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7287                ctx,
7288                scope,
7289                is_obis_wirkarbeit_quarter_hour,
7290            ));
7291        }
7292        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7293        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7294        ConditionResult::from(has_seq_z02)
7295    }
7296
7297    /// [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 ...
7298    fn evaluate_411(&self, ctx: &EvaluationContext) -> ConditionResult {
7299        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7300        // reaches an SG8; the message as before otherwise.
7301        if let Some(scope) = ctx.enclosing_scope("SG8") {
7302            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7303                ctx,
7304                scope,
7305                is_obis_wirkarbeit_quarter_hour,
7306            ));
7307        }
7308        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7309        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7310        ConditionResult::from(!has_seq_z02)
7311    }
7312
7313    /// [412] Es sind ausschließlich Konfigurationsprodukt-Codes der \"EDI@Energy Codeliste der Konfigurationen\" aus Kapitel 4.2 Konfigurationsprodukte Leistungskurvendefinition erlaubt
7314    fn evaluate_412(&self, ctx: &EvaluationContext) -> ConditionResult {
7315        ctx.external.evaluate("code_list_membership_check")
7316    }
7317
7318    /// [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
7319    fn evaluate_413(&self, ctx: &EvaluationContext) -> ConditionResult {
7320        // Check: SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7321        let found = ctx.find_segments("SEQ").iter().any(|s| {
7322            s.elements
7323                .first()
7324                .and_then(|e| e.first())
7325                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7326        });
7327        ConditionResult::from(found)
7328    }
7329
7330    /// [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
7331    fn evaluate_414(&self, ctx: &EvaluationContext) -> ConditionResult {
7332        // Check: no 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    /// [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
7343    fn evaluate_415(&self, ctx: &EvaluationContext) -> ConditionResult {
7344        // Check: another SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7345        let count = ctx
7346            .find_segments("SEQ")
7347            .iter()
7348            .filter(|s| {
7349                s.elements
7350                    .first()
7351                    .and_then(|e| e.first())
7352                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7353            })
7354            .count();
7355        ConditionResult::from(count > 1)
7356    }
7357
7358    /// [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
7359    fn evaluate_416(&self, ctx: &EvaluationContext) -> ConditionResult {
7360        // Check: no other SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7361        let count = ctx
7362            .find_segments("SEQ")
7363            .iter()
7364            .filter(|s| {
7365                s.elements
7366                    .first()
7367                    .and_then(|e| e.first())
7368                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7369            })
7370            .count();
7371        ConditionResult::from(count <= 1)
7372    }
7373
7374    /// [417] Wenn für den erforderlichen Wert keine Zählzeit benötigt wird
7375    fn evaluate_417(&self, _ctx: &EvaluationContext) -> ConditionResult {
7376        // TODO: implement
7377        ConditionResult::Unknown
7378    }
7379
7380    /// [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
7381    fn evaluate_419(&self, _ctx: &EvaluationContext) -> ConditionResult {
7382        // TODO: implement
7383        ConditionResult::Unknown
7384    }
7385
7386    /// [420] Wenn in dieser SG8 das RFF+Z14 (Smartmeter-Gateway) vorhanden ist
7387    fn evaluate_420(&self, ctx: &EvaluationContext) -> ConditionResult {
7388        // HAND-EDITED: "in dieser SG8" — scoped to the enclosing SG8.
7389        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7390        let found = rff_segs.iter().any(|s| {
7391            s.elements
7392                .first()
7393                .and_then(|e| e.first())
7394                .is_some_and(|v| v == "Z14")
7395        });
7396        ConditionResult::from(found)
7397    }
7398
7399    /// [421] Wenn in dieser SG8 das SG10 CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden ist
7400    fn evaluate_421(&self, ctx: &EvaluationContext) -> ConditionResult {
7401        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing group.
7402        // Check: CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden
7403        ctx.this_group_has_qualifier("CCI", 0, "Z39", &["SG4", "SG8"])
7404    }
7405
7406    /// [425] Messprodukt-Code aus den Kapitel 2.1 \"Standard-Messprodukte der Marktlokation\" der Codeliste der Konfigurationen
7407    fn evaluate_425(&self, ctx: &EvaluationContext) -> ConditionResult {
7408        ctx.external.evaluate("code_list_membership_check")
7409    }
7410
7411    /// [426] Messprodukt-Code aus den Kapitel 2.2 \"Standard-Messprodukte der Tranche\" der Codeliste der Konfigurationen
7412    fn evaluate_426(&self, ctx: &EvaluationContext) -> ConditionResult {
7413        ctx.external.evaluate("code_list_membership_check")
7414    }
7415
7416    /// [427] Messprodukt-Code aus den Kapitel 2.3 \"Standard-Messprodukte der Messlokation\" der Codeliste der Konfigurationen
7417    fn evaluate_427(&self, ctx: &EvaluationContext) -> ConditionResult {
7418        ctx.external.evaluate("code_list_membership_check")
7419    }
7420
7421    /// [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...
7422    fn evaluate_428(&self, ctx: &EvaluationContext) -> ConditionResult {
7423        ctx.external.evaluate("code_list_membership_check")
7424    }
7425
7426    /// [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...
7427    fn evaluate_429(&self, ctx: &EvaluationContext) -> ConditionResult {
7428        ctx.external.evaluate("code_list_membership_check")
7429    }
7430
7431    /// [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...
7432    fn evaluate_430(&self, ctx: &EvaluationContext) -> ConditionResult {
7433        // Check: another SG8 SEQ+Z27 exists with matching RFF+Z18
7434        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7435        ConditionResult::from(seq_z27_count > 1)
7436    }
7437
7438    /// [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+...
7439    fn evaluate_431(&self, ctx: &EvaluationContext) -> ConditionResult {
7440        // Check: no other SG8 SEQ+Z27 exists with matching RFF+Z18
7441        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7442        ConditionResult::from(seq_z27_count <= 1)
7443    }
7444
7445    /// [432] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
7446    fn evaluate_432(&self, ctx: &EvaluationContext) -> ConditionResult {
7447        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7448        // Check: PIA+5 with code 9991000000078 (Wirkarbeit Lastgang) exists
7449        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7450        let found = pia_segs.iter().any(|s| {
7451            s.elements
7452                .first()
7453                .and_then(|e| e.first())
7454                .is_some_and(|v| v == "5")
7455                && s.elements
7456                    .get(1)
7457                    .and_then(|e| e.first())
7458                    .is_some_and(|v| v == "9991000000078")
7459        });
7460        ConditionResult::from(found)
7461    }
7462
7463    /// [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 ...
7464    fn evaluate_433(&self, ctx: &EvaluationContext) -> ConditionResult {
7465        ctx.external.evaluate("code_list_membership_check")
7466    }
7467
7468    /// [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...
7469    fn evaluate_435(&self, ctx: &EvaluationContext) -> ConditionResult {
7470        ctx.external.evaluate("code_list_membership_check")
7471    }
7472
7473    /// [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
7474    fn evaluate_436(&self, ctx: &EvaluationContext) -> ConditionResult {
7475        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7476        // Check: PIA+5 with code 9991000000086 (Wirkarbeit höchste 1/4h Leistung) exists
7477        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7478        let found = pia_segs.iter().any(|s| {
7479            s.elements
7480                .first()
7481                .and_then(|e| e.first())
7482                .is_some_and(|v| v == "5")
7483                && s.elements
7484                    .get(1)
7485                    .and_then(|e| e.first())
7486                    .is_some_and(|v| v == "9991000000086")
7487        });
7488        ConditionResult::from(found)
7489    }
7490
7491    /// [437] Wenn in dieser SG4 das STS+E01++A04 / A23 (Status der Antwort) vorhanden
7492    fn evaluate_437(&self, ctx: &EvaluationContext) -> ConditionResult {
7493        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
7494        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
7495        let found = sts_segs.iter().any(|s| {
7496            s.elements
7497                .first()
7498                .and_then(|e| e.first())
7499                .is_some_and(|v| v == "E01")
7500                && s.elements
7501                    .get(2)
7502                    .and_then(|e| e.first())
7503                    .is_some_and(|v| ["A04", "A23"].contains(&v.as_str()))
7504        });
7505        ConditionResult::from(found)
7506    }
7507
7508    /// [438] Wenn im selben CCI im DE7059 der Code Z39 (Code der Zählzeit) vorhanden ist
7509    fn evaluate_438(&self, ctx: &EvaluationContext) -> ConditionResult {
7510        // HAND-EDITED: "im selben CCI" — the CCI the field is in (its own segment
7511        // in tree validation), else the enclosing SG10's CCI.
7512        let is_z39 = |elements: &[Vec<String>]| {
7513            elements
7514                .first()
7515                .and_then(|e| e.first())
7516                .is_some_and(|v| v == "Z39")
7517        };
7518        if let Some(own) = ctx.resolved_segment.filter(|_| {
7519            ctx.scoped_find_segments_in("SG10", "CCI")
7520                .iter()
7521                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
7522        }) {
7523            return ConditionResult::from(is_z39(own));
7524        }
7525        ConditionResult::from(
7526            ctx.scoped_find_segments_in("SG10", "CCI")
7527                .iter()
7528                .any(|s| is_z39(&s.elements)),
7529        )
7530    }
7531
7532    /// [440] Wenn in demselben RFF der Code Z35 (vorgelagerte Netzlokation) im DE1153 vorhanden
7533    fn evaluate_440(&self, ctx: &EvaluationContext) -> ConditionResult {
7534        // Check: RFF with qualifier Z35 (vorgelagerte Netzlokation) exists
7535        ctx.has_qualifier("RFF", 0, "Z35")
7536    }
7537
7538    /// [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
7539    fn evaluate_441(&self, ctx: &EvaluationContext) -> ConditionResult {
7540        // Check: SG8 SEQ+Z03/ZF5 device without RFF+Z14 (SMGW reference)
7541        ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
7542    }
7543
7544    /// [442] Wenn in demselben RFF der Code Z34 (vorgelagerte Messlokation) im DE1153 vorhanden
7545    fn evaluate_442(&self, ctx: &EvaluationContext) -> ConditionResult {
7546        // Check: RFF with qualifier Z34 (vorgelagerte Messlokation) exists
7547        ctx.has_qualifier("RFF", 0, "Z34")
7548    }
7549
7550    /// [444] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das PIA+5 (Produkt-Daten der Netzlokation) nicht vorhanden
7551    // 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)
7552    fn evaluate_444(&self, ctx: &EvaluationContext) -> ConditionResult {
7553        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7554        // scope reaches one; the message as before otherwise.
7555        if let Some(scope) = ctx.enclosing_scope("SG8") {
7556            return ConditionResult::from(
7557                ctx.find_segments_at(scope, "SEQ")
7558                    .iter()
7559                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7560                    && !ctx
7561                        .find_segments_at(scope, "PIA")
7562                        .iter()
7563                        .any(|s| s.get_element(0) == "5"),
7564            );
7565        }
7566        for code in ["Z60", "ZE0", "ZG8", "ZG9"] {
7567            if matches!(
7568                ctx.any_group_has_qualifier_without("SEQ", 0, code, "PIA", 0, "5", &["SG4", "SG8"]),
7569                ConditionResult::True
7570            ) {
7571                return ConditionResult::True;
7572            }
7573        }
7574        ConditionResult::False
7575    }
7576
7577    /// [445] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das SG10 CCI+11 (Details zum Produkt der Netzlokation) nicht vorhanden
7578    // 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)
7579    fn evaluate_445(&self, ctx: &EvaluationContext) -> ConditionResult {
7580        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7581        // scope reaches one; the message as before otherwise.
7582        if let Some(scope) = ctx.enclosing_scope("SG8") {
7583            return ConditionResult::from(
7584                ctx.find_segments_at(scope, "SEQ")
7585                    .iter()
7586                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7587                    && !ctx
7588                        .find_segments_at(scope, "CCI")
7589                        .iter()
7590                        .any(|s| s.get_element(0) == "11"),
7591            );
7592        }
7593        let nav = match ctx.navigator {
7594            Some(n) => n,
7595            None => {
7596                let has_cci11 = ["Z60", "ZE0", "ZG8", "ZG9"].iter().any(|code| {
7597                    matches!(
7598                        ctx.filtered_parent_child_has_qualifier(
7599                            &["SG4", "SG8"],
7600                            "SEQ",
7601                            0,
7602                            code,
7603                            "SG10",
7604                            "CCI",
7605                            0,
7606                            "11",
7607                        ),
7608                        ConditionResult::True
7609                    )
7610                });
7611                return ConditionResult::from(!has_cci11);
7612            }
7613        };
7614
7615        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7616        for i in 0..sg8_count {
7617            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7618            let has_cci11 = (0..sg10_count).any(|j| {
7619                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7620                ccis.iter().any(|s| {
7621                    s.elements
7622                        .first()
7623                        .and_then(|e: &Vec<String>| e.first())
7624                        .is_some_and(|v: &String| v == "11")
7625                })
7626            });
7627            if !has_cci11 {
7628                return ConditionResult::True;
7629            }
7630        }
7631        ConditionResult::False
7632    }
7633
7634    /// [446] Wenn in derselben SG8 das CCI+Z17 (Stromverbrauchsart) vorhanden
7635    fn evaluate_446(&self, ctx: &EvaluationContext) -> ConditionResult {
7636        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7637        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7638        let found = cci_segs.iter().any(|s| {
7639            s.elements
7640                .first()
7641                .and_then(|e| e.first())
7642                .is_some_and(|v| v == "Z17")
7643        });
7644        ConditionResult::from(found)
7645    }
7646
7647    /// [447] Wenn in derselben SG8 das CCI+Z50 (Stromerzeugungsart) vorhanden
7648    fn evaluate_447(&self, ctx: &EvaluationContext) -> ConditionResult {
7649        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7650        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7651        let found = cci_segs.iter().any(|s| {
7652            s.elements
7653                .first()
7654                .and_then(|e| e.first())
7655                .is_some_and(|v| v == "Z50")
7656        });
7657        ConditionResult::from(found)
7658    }
7659
7660    /// [448] Wenn in derselben SG8 das CCI+Z56 (Speicher) vorhanden
7661    fn evaluate_448(&self, ctx: &EvaluationContext) -> ConditionResult {
7662        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7663        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7664        let found = cci_segs.iter().any(|s| {
7665            s.elements
7666                .first()
7667                .and_then(|e| e.first())
7668                .is_some_and(|v| v == "Z56")
7669        });
7670        ConditionResult::from(found)
7671    }
7672
7673    /// [449] Wenn in derselben SG8 das SG10 CCI+6++ZA9 CAV=ZG3 (Aggreg.verantw. ÜNB und Lokation im Regelbetrieb) vorhanden
7674    fn evaluate_449(&self, ctx: &EvaluationContext) -> ConditionResult {
7675        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7676        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7677        let has_cci = cci_segs.iter().any(|s| {
7678            s.elements
7679                .first()
7680                .and_then(|e| e.first())
7681                .is_some_and(|v| v == "6")
7682                && s.elements
7683                    .get(2)
7684                    .and_then(|e| e.first())
7685                    .is_some_and(|v| v == "ZA9")
7686        });
7687        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
7688        let has_cav = cav_segs.iter().any(|s| {
7689            s.elements
7690                .first()
7691                .and_then(|e| e.first())
7692                .is_some_and(|v| v == "ZG3")
7693        });
7694        ConditionResult::from(has_cci && has_cav)
7695    }
7696
7697    /// [450] Wenn in demselben RFF der Code Z32 (Netzlokation) im DE1153 vorhanden
7698    fn evaluate_450(&self, ctx: &EvaluationContext) -> ConditionResult {
7699        // Check: RFF with qualifier Z32 (Netzlokation) exists
7700        ctx.has_qualifier("RFF", 0, "Z32")
7701    }
7702
7703    /// [451] Wenn in demselben RFF der Code Z18 (Marktlokation) im DE1153 vorhanden
7704    fn evaluate_451(&self, ctx: &EvaluationContext) -> ConditionResult {
7705        // Check: RFF with qualifier Z18 (Marktlokation) exists
7706        ctx.has_qualifier("RFF", 0, "Z18")
7707    }
7708
7709    /// [452] Wenn in demselben RFF der Code Z19 (Messlokation) im DE1153 vorhanden
7710    fn evaluate_452(&self, ctx: &EvaluationContext) -> ConditionResult {
7711        // Check: RFF with qualifier Z19 (Messlokation) exists
7712        ctx.has_qualifier("RFF", 0, "Z19")
7713    }
7714
7715    /// [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...
7716    fn evaluate_453(&self, ctx: &EvaluationContext) -> ConditionResult {
7717        ctx.external.evaluate("code_list_membership_check")
7718    }
7719
7720    /// [454] Das Bilanzierungsbeginn Datum muss kleiner sein als das Bilanzierungsende Datum in der SG8 „Datenstand des ÜNB", SG 9 Energiemenge DZÜ Anteil
7721    fn evaluate_454(&self, _ctx: &EvaluationContext) -> ConditionResult {
7722        // Check: begin date < end date (structural — always true for valid messages)
7723        ConditionResult::True
7724    }
7725
7726    /// [455] Das Bilanzierungsende-Datum muss größer sein, als das Bilanzierungsbeginn Datum in der SG8 „Datenstand des ÜNB" , SG 9 Energiemenge DZÜ Anteil
7727    fn evaluate_455(&self, _ctx: &EvaluationContext) -> ConditionResult {
7728        // Check: end date > begin date (structural — always true for valid messages)
7729        ConditionResult::True
7730    }
7731
7732    /// [456] Wenn in demselben RFF der Code Z37 (Technische Ressource) im DE1153 vorhanden
7733    fn evaluate_456(&self, ctx: &EvaluationContext) -> ConditionResult {
7734        // Check: RFF with qualifier Z37 (Technische Ressource) exists
7735        ctx.has_qualifier("RFF", 0, "Z37")
7736    }
7737
7738    /// [457] Wenn bei der Bestellung das Messprodukten 9991000001498 bestellt wurde
7739    fn evaluate_457(&self, ctx: &EvaluationContext) -> ConditionResult {
7740        // Check: PIA+5 with Messprodukt 9991000001498 exists
7741        let pia_segs = ctx.find_segments("PIA");
7742        let found = pia_segs.iter().any(|s| {
7743            s.elements
7744                .first()
7745                .and_then(|e| e.first())
7746                .is_some_and(|v| v == "5")
7747                && s.elements
7748                    .get(1)
7749                    .and_then(|e| e.first())
7750                    .is_some_and(|v| v == "9991000001498")
7751        });
7752        ConditionResult::from(found)
7753    }
7754
7755    /// [458] Wenn bei der Bestellung das Konfigurationsprodukte 9991000000739 bestellt wurde
7756    fn evaluate_458(&self, ctx: &EvaluationContext) -> ConditionResult {
7757        // Check: PIA+5 with Konfigurationsprodukt 9991000000739 exists
7758        let pia_segs = ctx.find_segments("PIA");
7759        let found = pia_segs.iter().any(|s| {
7760            s.elements
7761                .first()
7762                .and_then(|e| e.first())
7763                .is_some_and(|v| v == "5")
7764                && s.elements
7765                    .get(1)
7766                    .and_then(|e| e.first())
7767                    .is_some_and(|v| v == "9991000000739")
7768        });
7769        ConditionResult::from(found)
7770    }
7771
7772    /// [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
7773    fn evaluate_459(&self, ctx: &EvaluationContext) -> ConditionResult {
7774        ctx.external.evaluate("code_list_membership_check")
7775    }
7776
7777    /// [460] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"aus dem SMGW\" bestellt wurde
7778    fn evaluate_460(&self, ctx: &EvaluationContext) -> ConditionResult {
7779        ctx.external.evaluate("code_list_membership_check")
7780    }
7781
7782    /// [461] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden
7783    fn evaluate_461(&self, ctx: &EvaluationContext) -> ConditionResult {
7784        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
7785        // SG10, when the scope reaches one; of any one SG10 otherwise.
7786        this_sg10_has(ctx, |segs| {
7787            segs.iter()
7788                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
7789                && sg4_instances::has_cav(segs, "ZG8")
7790        })
7791    }
7792
7793    /// [462] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden
7794    fn evaluate_462(&self, ctx: &EvaluationContext) -> ConditionResult {
7795        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
7796        // SG10, when the scope reaches one; of any one SG10 otherwise.
7797        this_sg10_has(ctx, |segs| {
7798            segs.iter()
7799                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
7800                && sg4_instances::has_cav(segs, "ZH1")
7801        })
7802    }
7803
7804    /// [463] Wenn in derselben SG10 das CCI+Z61++ZF9 (Kunde erfüllt die Voraussetzung nach EnFG) vorhanden
7805    fn evaluate_463(&self, ctx: &EvaluationContext) -> ConditionResult {
7806        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7807        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7808        let found = cci_segs.iter().any(|s| {
7809            s.elements
7810                .first()
7811                .and_then(|e| e.first())
7812                .is_some_and(|v| v == "Z61")
7813                && s.elements
7814                    .get(2)
7815                    .and_then(|e| e.first())
7816                    .is_some_and(|v| v == "ZF9")
7817        });
7818        ConditionResult::from(found)
7819    }
7820
7821    /// [465] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z38 (Referenz auf die der Technischen Ressource zugeordnete Steuerbare Ressource) vorhanden ist
7822    fn evaluate_465(&self, ctx: &EvaluationContext) -> ConditionResult {
7823        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7824        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7825        let found = rff_segs.iter().any(|s| {
7826            s.elements
7827                .first()
7828                .and_then(|e| e.first())
7829                .is_some_and(|v| v == "Z38")
7830        });
7831        ConditionResult::from(found)
7832    }
7833
7834    /// [466] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z32 (Referenz auf die der Technischen Ressource zugeordneten Netzlokation) vorhanden ist
7835    fn evaluate_466(&self, ctx: &EvaluationContext) -> ConditionResult {
7836        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7837        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7838        let found = rff_segs.iter().any(|s| {
7839            s.elements
7840                .first()
7841                .and_then(|e| e.first())
7842                .is_some_and(|v| v == "Z32")
7843        });
7844        ConditionResult::from(found)
7845    }
7846
7847    /// [467] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden ist
7848    fn evaluate_467(&self, ctx: &EvaluationContext) -> ConditionResult {
7849        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7850        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7851        let found = segs.iter().any(|s| {
7852            s.elements
7853                .first()
7854                .and_then(|e| e.first())
7855                .is_some_and(|v| v == "ZG8")
7856        });
7857        ConditionResult::from(found)
7858    }
7859
7860    /// [468] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH0 (Inbetriebsetzung der TR nach 2023) vorhanden ist
7861    fn evaluate_468(&self, ctx: &EvaluationContext) -> ConditionResult {
7862        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7863        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7864        let found = segs.iter().any(|s| {
7865            s.elements
7866                .first()
7867                .and_then(|e| e.first())
7868                .is_some_and(|v| v == "ZH0")
7869        });
7870        ConditionResult::from(found)
7871    }
7872
7873    /// [469] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden ist
7874    fn evaluate_469(&self, ctx: &EvaluationContext) -> ConditionResult {
7875        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7876        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7877        let found = segs.iter().any(|s| {
7878            s.elements
7879                .first()
7880                .and_then(|e| e.first())
7881                .is_some_and(|v| v == "ZH1")
7882        });
7883        ConditionResult::from(found)
7884    }
7885
7886    /// [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
7887    fn evaluate_470(&self, ctx: &EvaluationContext) -> ConditionResult {
7888        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7889        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7890        let found = segs.iter().any(|s| {
7891            s.elements
7892                .first()
7893                .and_then(|e| e.first())
7894                .is_some_and(|v| v == "ZH5")
7895        });
7896        ConditionResult::from(found)
7897    }
7898
7899    /// [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/ ...
7900    fn evaluate_471(&self, ctx: &EvaluationContext) -> ConditionResult {
7901        // Check: SG6 RFF+Z49/Z53 has Zeitraum-ID > 1 in DE1156
7902        let found = ctx.find_segments("RFF").iter().any(|s| {
7903            let qual = s
7904                .elements
7905                .first()
7906                .and_then(|e| e.first())
7907                .map(|v| v.as_str());
7908            matches!(qual, Some("Z49") | Some("Z53"))
7909                && s.elements
7910                    .first()
7911                    .and_then(|e| e.get(2))
7912                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7913        });
7914        ConditionResult::from(found)
7915    }
7916
7917    /// [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...
7918    fn evaluate_472(&self, ctx: &EvaluationContext) -> ConditionResult {
7919        // Check: SG6 RFF+Z48/Z55 has Zeitraum-ID > 1 in DE1156
7920        let found = ctx.find_segments("RFF").iter().any(|s| {
7921            let qual = s
7922                .elements
7923                .first()
7924                .and_then(|e| e.first())
7925                .map(|v| v.as_str());
7926            matches!(qual, Some("Z48") | Some("Z55"))
7927                && s.elements
7928                    .first()
7929                    .and_then(|e| e.get(2))
7930                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7931        });
7932        ConditionResult::from(found)
7933    }
7934
7935    /// [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...
7936    fn evaluate_473(&self, ctx: &EvaluationContext) -> ConditionResult {
7937        // Check: SG6 RFF+Z47/Z54 has Zeitraum-ID > 1 in DE1156
7938        let found = ctx.find_segments("RFF").iter().any(|s| {
7939            let qual = s
7940                .elements
7941                .first()
7942                .and_then(|e| e.first())
7943                .map(|v| v.as_str());
7944            matches!(qual, Some("Z47") | Some("Z54"))
7945                && s.elements
7946                    .first()
7947                    .and_then(|e| e.get(2))
7948                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7949        });
7950        ConditionResult::from(found)
7951    }
7952
7953    /// [474] Wenn SG4 STS+7++ZC8 (Transaktionsgrund \"Beendigung der Zuordnung\") vorhanden
7954    fn evaluate_474(&self, ctx: &EvaluationContext) -> ConditionResult {
7955        let sts_segs = ctx.find_segments("STS");
7956        let found = sts_segs.iter().any(|s| {
7957            s.elements
7958                .first()
7959                .and_then(|e| e.first())
7960                .is_some_and(|v| v == "7")
7961                && s.elements
7962                    .get(2)
7963                    .and_then(|e| e.first())
7964                    .is_some_and(|v| ["ZC8"].contains(&v.as_str()))
7965        });
7966        ConditionResult::from(found)
7967    }
7968
7969    /// [475] Wenn SG4 STS+7++ZH1 (Transaktionsgrund \"Aufhebung einer zukünftigen Zuordnung wegen Stilllegung\") vorhanden
7970    fn evaluate_475(&self, ctx: &EvaluationContext) -> ConditionResult {
7971        let sts_segs = ctx.find_segments("STS");
7972        let found = sts_segs.iter().any(|s| {
7973            s.elements
7974                .first()
7975                .and_then(|e| e.first())
7976                .is_some_and(|v| v == "7")
7977                && s.elements
7978                    .get(2)
7979                    .and_then(|e| e.first())
7980                    .is_some_and(|v| ["ZH1"].contains(&v.as_str()))
7981        });
7982        ConditionResult::from(found)
7983    }
7984
7985    /// [476] Wenn SG4 STS+7++xxx+ZW0 (Transaktionsgrundergänzung Geschäftsvorfall 1) vorhanden
7986    fn evaluate_476(&self, ctx: &EvaluationContext) -> ConditionResult {
7987        let sts_segs = ctx.find_segments("STS");
7988        let found = sts_segs.iter().any(|s| {
7989            s.elements
7990                .first()
7991                .and_then(|e| e.first())
7992                .is_some_and(|v| v == "7")
7993                && s.elements
7994                    .get(3)
7995                    .and_then(|e| e.first())
7996                    .is_some_and(|v| ["ZW0"].contains(&v.as_str()))
7997        });
7998        ConditionResult::from(found)
7999    }
8000
8001    /// [477] Wenn SG4 STS+7++xxx+ZW1 (Transaktionsgrundergänzung Geschäftsvorfall 2) vorhanden
8002    fn evaluate_477(&self, ctx: &EvaluationContext) -> ConditionResult {
8003        let sts_segs = ctx.find_segments("STS");
8004        let found = sts_segs.iter().any(|s| {
8005            s.elements
8006                .first()
8007                .and_then(|e| e.first())
8008                .is_some_and(|v| v == "7")
8009                && s.elements
8010                    .get(3)
8011                    .and_then(|e| e.first())
8012                    .is_some_and(|v| ["ZW1"].contains(&v.as_str()))
8013        });
8014        ConditionResult::from(found)
8015    }
8016
8017    /// [478] Wenn SG4 STS+7++xxx+ZW2 (Transaktionsgrundergänzung Geschäftsvorfall 3) vorhanden
8018    fn evaluate_478(&self, ctx: &EvaluationContext) -> ConditionResult {
8019        let sts_segs = ctx.find_segments("STS");
8020        let found = sts_segs.iter().any(|s| {
8021            s.elements
8022                .first()
8023                .and_then(|e| e.first())
8024                .is_some_and(|v| v == "7")
8025                && s.elements
8026                    .get(3)
8027                    .and_then(|e| e.first())
8028                    .is_some_and(|v| ["ZW2"].contains(&v.as_str()))
8029        });
8030        ConditionResult::from(found)
8031    }
8032
8033    /// [479] Wenn SG4 STS+7++xxx+ZW3 (Transaktionsgrundergänzung Erzeugende Marktlokation) vorhanden
8034    fn evaluate_479(&self, ctx: &EvaluationContext) -> ConditionResult {
8035        let sts_segs = ctx.find_segments("STS");
8036        let found = sts_segs.iter().any(|s| {
8037            s.elements
8038                .first()
8039                .and_then(|e| e.first())
8040                .is_some_and(|v| v == "7")
8041                && s.elements
8042                    .get(3)
8043                    .and_then(|e| e.first())
8044                    .is_some_and(|v| ["ZW3"].contains(&v.as_str()))
8045        });
8046        ConditionResult::from(found)
8047    }
8048
8049    /// [480] Wenn SG4 STS+7++xxx+ZW4 (Transaktionsgrundergänzung Verbrauchende Marktlokation) vorhanden
8050    fn evaluate_480(&self, ctx: &EvaluationContext) -> ConditionResult {
8051        let sts_segs = ctx.find_segments("STS");
8052        let found = sts_segs.iter().any(|s| {
8053            s.elements
8054                .first()
8055                .and_then(|e| e.first())
8056                .is_some_and(|v| v == "7")
8057                && s.elements
8058                    .get(3)
8059                    .and_then(|e| e.first())
8060                    .is_some_and(|v| ["ZW4"].contains(&v.as_str()))
8061        });
8062        ConditionResult::from(found)
8063    }
8064
8065    /// [481] Wenn SG4 STS+7++xxx+ZW5 (Transaktionsgrundergänzung Tranche) vorhanden
8066    fn evaluate_481(&self, ctx: &EvaluationContext) -> ConditionResult {
8067        let sts_segs = ctx.find_segments("STS");
8068        let found = sts_segs.iter().any(|s| {
8069            s.elements
8070                .first()
8071                .and_then(|e| e.first())
8072                .is_some_and(|v| v == "7")
8073                && s.elements
8074                    .get(3)
8075                    .and_then(|e| e.first())
8076                    .is_some_and(|v| ["ZW5"].contains(&v.as_str()))
8077        });
8078        ConditionResult::from(found)
8079    }
8080
8081    /// [483] Wenn SG4 STS+7++xxx+ZW7 (Transaktionsgrundergänzung Gemessene Marktlokation) vorhanden
8082    fn evaluate_483(&self, ctx: &EvaluationContext) -> ConditionResult {
8083        let sts_segs = ctx.find_segments("STS");
8084        let found = sts_segs.iter().any(|s| {
8085            s.elements
8086                .first()
8087                .and_then(|e| e.first())
8088                .is_some_and(|v| v == "7")
8089                && s.elements
8090                    .get(3)
8091                    .and_then(|e| e.first())
8092                    .is_some_and(|v| ["ZW7"].contains(&v.as_str()))
8093        });
8094        ConditionResult::from(found)
8095    }
8096
8097    /// [484] Wenn SG4 STS+7++xxx+ZW8 (Transaktionsgrundergänzung Fall1) vorhanden
8098    fn evaluate_484(&self, ctx: &EvaluationContext) -> ConditionResult {
8099        let sts_segs = ctx.find_segments("STS");
8100        let found = sts_segs.iter().any(|s| {
8101            s.elements
8102                .first()
8103                .and_then(|e| e.first())
8104                .is_some_and(|v| v == "7")
8105                && s.elements
8106                    .get(3)
8107                    .and_then(|e| e.first())
8108                    .is_some_and(|v| ["ZW8"].contains(&v.as_str()))
8109        });
8110        ConditionResult::from(found)
8111    }
8112
8113    /// [487] Wenn SG4 STS+7++xxx+ZX1 (Transaktionsgrundergänzung Fall4) vorhanden
8114    fn evaluate_487(&self, ctx: &EvaluationContext) -> ConditionResult {
8115        let sts_segs = ctx.find_segments("STS");
8116        let found = sts_segs.iter().any(|s| {
8117            s.elements
8118                .first()
8119                .and_then(|e| e.first())
8120                .is_some_and(|v| v == "7")
8121                && s.elements
8122                    .get(3)
8123                    .and_then(|e| e.first())
8124                    .is_some_and(|v| ["ZX1"].contains(&v.as_str()))
8125        });
8126        ConditionResult::from(found)
8127    }
8128
8129    /// [489] Nur bei dem ältesten Zeitraum welcher mit SG6 RFF+Z49 (Verwendungszeitraum der Daten: Gültige Daten) beschrieben ist
8130    fn evaluate_489(&self, ctx: &EvaluationContext) -> ConditionResult {
8131        // Check: this is the oldest Zeitraum (lowest Zeitraum-ID) with RFF+Z49
8132        let rff_z49 = ctx.find_segments_with_qualifier("RFF", 0, "Z49");
8133        if rff_z49.is_empty() {
8134            return ConditionResult::Unknown;
8135        }
8136        // Approximate: condition applies when RFF+Z49 exists
8137        ConditionResult::True
8138    }
8139
8140    /// [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
8141    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
8142        ctx.format_check("DTM", 0, 1, is_mesz_utc)
8143    }
8144
8145    /// [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
8146    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
8147        ctx.format_check("DTM", 0, 1, is_mez_utc)
8148    }
8149
8150    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
8151    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
8152        // Document date must be <= now. Format 303 is CCYYMMDDHHMM — fixed-width,
8153        // so lexicographic comparison works directly.
8154        let val = ctx.resolved_value.or_else(|| {
8155            ctx.find_segments_with_qualifier("DTM", 0, "137")
8156                .first()
8157                .and_then(|s| s.elements.first())
8158                .and_then(|e| e.get(1))
8159                .map(|v| v.as_str())
8160        });
8161        let Some(dtm_val) = val else {
8162            return ConditionResult::Unknown;
8163        };
8164        if dtm_val.len() < 12 {
8165            return ConditionResult::Unknown;
8166        }
8167        let now_str = crate::eval::format_validators::utc_now_ccyymmddhhmm();
8168        ConditionResult::from(&dtm_val[..12] <= now_str.as_str())
8169    }
8170
8171    /// [500] Hinweis: Code ist gemäß der Kategorie der zu stornierenden Meldung zu wählen
8172    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
8173        ConditionResult::True
8174    }
8175
8176    /// [501] Hinweis: Es sind zur Unterstützung die Daten des LFA anzugeben
8177    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
8178        ConditionResult::True
8179    }
8180
8181    /// [502] Hinweis: Es wird der Betrachtungsmonat der DZR bzw. BAS angegeben
8182    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
8183        ConditionResult::True
8184    }
8185
8186    /// [503] Hinweis: Angabe des BGM DE1004 aus der ORDERS
8187    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
8188        ConditionResult::True
8189    }
8190
8191    /// [504] Hinweis: Je Profil (ggf. inkl. Profilschar) ein Vorgang erforderlich
8192    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
8193        ConditionResult::True
8194    }
8195
8196    /// [505] Hinweis: Je zugeordneter ID und je EEG-Überführungszeitreihe ein Vorgang erforderlich
8197    fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
8198        ConditionResult::True
8199    }
8200
8201    /// [506] Hinweis: Für jeden Code im LOC+Z16 (Marktlokation) sollen die entsprechenden Kundendaten des LF angegeben werden, falls bekannt
8202    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
8203        ConditionResult::True
8204    }
8205
8206    /// [507] Hinweis: Ursprünglich vom NB bestätigtes Beginndatum
8207    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
8208        ConditionResult::True
8209    }
8210
8211    /// [508] Hinweis: Ausschließlich kME sind über die NNE abrechenbar
8212    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
8213        ConditionResult::True
8214    }
8215
8216    /// [509] Hinweis: Handelt sich um ein mME oder iMS ist das RFF 1154 immer mit NEIN anzugeben
8217    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
8218        ConditionResult::True
8219    }
8220
8221    /// [510] Hinweis: Zu verwenden bei der Abmeldung der ESV
8222    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
8223        ConditionResult::True
8224    }
8225
8226    /// [511] Hinweis: Es sind alle Bilanzierungsgebiete anzugeben in denen das Profil verwendet wird
8227    fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
8228        ConditionResult::True
8229    }
8230
8231    /// [512] Hinweis: Es ist informativ die bisherige Veräußerungsform des LFA an der erzeugenden Marktlokation anzugeben
8232    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
8233        ConditionResult::True
8234    }
8235
8236    /// [513] Hinweis: Es ist das nächstmögliche Datum ab dem DTM+471 aus der entsprechenden Anfrage zu ermitteln
8237    fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
8238        ConditionResult::True
8239    }
8240
8241    /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
8242    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
8243        ConditionResult::True
8244    }
8245
8246    /// [515] Hinweis: Für den ZP der LieferantensummenZR anzugeben
8247    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
8248        ConditionResult::True
8249    }
8250
8251    /// [516] Hinweis: Es ist der Änderungszeitpunkt anzugeben an dem das Zuordnungsende des NBA und der Zuordnungsbeginn des NBN zu einer Marktlokation fallen
8252    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
8253        ConditionResult::True
8254    }
8255
8256    /// [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
8257    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
8258        ConditionResult::True
8259    }
8260
8261    /// [518] Hinweis: Es sind alle Altlieferanten anzugeben, an die eine Abmeldeanfrage gesendet wird
8262    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
8263        ConditionResult::True
8264    }
8265
8266    /// [519] Hinweis: Wenn in der Anmeldung der Code ZAP vorhanden war, so ist dieser auch in der Antwort zu verwenden
8267    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
8268        ConditionResult::True
8269    }
8270
8271    /// [520] Hinweis: Bei der Verwendung des Codes ZAP handelt es sich immer um eine gemessene Marktlokation
8272    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
8273        ConditionResult::True
8274    }
8275
8276    /// [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...
8277    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
8278        ConditionResult::True
8279    }
8280
8281    /// [522] Hinweis: Es ist der NBN anzugeben
8282    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
8283        ConditionResult::True
8284    }
8285
8286    /// [523] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
8287    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
8288        ConditionResult::True
8289    }
8290
8291    /// [524] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, für die der LF nicht die gemeldete Ansicht des NB teilt
8292    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
8293        ConditionResult::True
8294    }
8295
8296    /// [525] Hinweis: Es sind nur die OBIS-Kennzahlen erlaubt, die im vorherigen Stammdatenaustausch zu dieser Marktlokation vom MSB zu diesem Zeitpunkt übermittelt wurden
8297    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
8298        ConditionResult::True
8299    }
8300
8301    /// [526] Hinweis: Wenn der Marktlokation keine Technische Ressource in der Lokationsbündelstruktur zugeordnet wurde
8302    fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
8303        ConditionResult::True
8304    }
8305
8306    /// [527] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben
8307    fn evaluate_527(&self, _ctx: &EvaluationContext) -> ConditionResult {
8308        ConditionResult::True
8309    }
8310
8311    /// [528] Hinweis: Es ist das Datum/ Daten aus der Anfrage zu verwenden
8312    fn evaluate_528(&self, _ctx: &EvaluationContext) -> ConditionResult {
8313        ConditionResult::True
8314    }
8315
8316    /// [529] Hinweis: Für zusätzliche nicht im Markt standardisierte Identifikatoren wie z.B. eine Netzbetreiber-Projektnummer
8317    fn evaluate_529(&self, _ctx: &EvaluationContext) -> ConditionResult {
8318        ConditionResult::True
8319    }
8320
8321    /// [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, ...
8322    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
8323        ConditionResult::True
8324    }
8325
8326    /// [531] Hinweis: Es ist das Jahr anzugeben in dem die nächste Netznutzungsabrechnung erfolgt
8327    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
8328        ConditionResult::True
8329    }
8330
8331    /// [532] Hinweis: Kritische Daten (gemäß GPKE)
8332    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
8333        ConditionResult::True
8334    }
8335
8336    /// [533] Hinweis: Es ist die MP-ID des Lieferanten anzugeben
8337    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
8338        ConditionResult::True
8339    }
8340
8341    /// [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...
8342    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
8343        // TODO: implement
8344        ConditionResult::Unknown
8345    }
8346
8347    /// [556] Hinweis: Wenn keine Korrespondenzanschrift des Endverbrauchers/ Kunden vorliegt, ist die Anschrift der Marktlokation zu übermitteln
8348    fn evaluate_556(&self, _ctx: &EvaluationContext) -> ConditionResult {
8349        ConditionResult::True
8350    }
8351
8352    /// [558] Hinweis: Diese Information kann freiwillig ausgetauscht werden
8353    fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
8354        ConditionResult::True
8355    }
8356
8357    /// [559] Hinweis: Die Korrespondenzanschrift des Endverbrauchers/Kunden wird nicht zur Identifikation genutzt
8358    fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
8359        ConditionResult::True
8360    }
8361
8362    /// [563] Hinweis: Für die ID der LieferantensummenZR
8363    fn evaluate_563(&self, _ctx: &EvaluationContext) -> ConditionResult {
8364        ConditionResult::True
8365    }
8366
8367    /// [566] Hinweis: Altlieferant
8368    fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
8369        ConditionResult::True
8370    }
8371
8372    /// [567] Hinweis: Neulieferant
8373    fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
8374        ConditionResult::True
8375    }
8376
8377    /// [568] Hinweis: Lieferant der LieferantensummenZR
8378    fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
8379        ConditionResult::True
8380    }
8381
8382    /// [569] Hinweis: Dritter Nutzer
8383    fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
8384        ConditionResult::True
8385    }
8386
8387    /// [570] Hinweis: Netzbetreiber Alt
8388    fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
8389        ConditionResult::True
8390    }
8391
8392    /// [572] Hinweis: Kundenname aus Anmeldung Lieferant neu
8393    fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
8394        ConditionResult::True
8395    }
8396
8397    /// [576] Hinweis: Stammdaten des bisherigen Messstellenbetreibers
8398    fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
8399        ConditionResult::True
8400    }
8401
8402    /// [577] Hinweis: Wird mit dem ursprünglich vom NB bestätigten Beginnzeitpunkt gefüllt
8403    fn evaluate_577(&self, _ctx: &EvaluationContext) -> ConditionResult {
8404        ConditionResult::True
8405    }
8406
8407    /// [579] Hinweis: Auslösender Marktpartner (LFN bei STS+7++ZH0/ZG9, NB bei STS+7++ZH1)
8408    fn evaluate_579(&self, _ctx: &EvaluationContext) -> ConditionResult {
8409        ConditionResult::True
8410    }
8411
8412    /// [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...
8413    fn evaluate_580(&self, _ctx: &EvaluationContext) -> ConditionResult {
8414        ConditionResult::True
8415    }
8416
8417    /// [581] Hinweis: Es ist der Zeitpunkt anzugeben, zu welchem der Vertrag am Tag des Versandes der Antwort noch kündbar ist.
8418    fn evaluate_581(&self, _ctx: &EvaluationContext) -> ConditionResult {
8419        ConditionResult::True
8420    }
8421
8422    /// [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...
8423    fn evaluate_586(&self, _ctx: &EvaluationContext) -> ConditionResult {
8424        ConditionResult::True
8425    }
8426
8427    /// [590] Hinweis: Für den Empfang des Steuerbefehls
8428    fn evaluate_590(&self, _ctx: &EvaluationContext) -> ConditionResult {
8429        ConditionResult::True
8430    }
8431
8432    /// [594] Hinweis: Es ist der ZPB des ZP der NGZ und die ZPB der NZR anzugeben
8433    fn evaluate_594(&self, _ctx: &EvaluationContext) -> ConditionResult {
8434        ConditionResult::True
8435    }
8436
8437    /// [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...
8438    fn evaluate_599(&self, _ctx: &EvaluationContext) -> ConditionResult {
8439        ConditionResult::True
8440    }
8441
8442    /// [601] Hinweis: Es ist die ID der Marktlokation und alle Identifikatoren der Messlokationen anzugeben. Sowie wenn vorhanden die Tranche/n der Marklokation
8443    fn evaluate_601(&self, _ctx: &EvaluationContext) -> ConditionResult {
8444        ConditionResult::True
8445    }
8446
8447    /// [606] Hinweis: In diesem Segment bzw. SG sind die Daten aus der Sicht des NB anzugeben
8448    fn evaluate_606(&self, _ctx: &EvaluationContext) -> ConditionResult {
8449        ConditionResult::True
8450    }
8451
8452    /// [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
8453    fn evaluate_609(&self, _ctx: &EvaluationContext) -> ConditionResult {
8454        ConditionResult::True
8455    }
8456
8457    /// [611] Hinweis: Wenn das Unternehmen NB die Aufgaben der Marktrolle LF wahrnimmt, ist die MP-ID des Unternehmens NB in der Marktrolle LF anzugeben
8458    fn evaluate_611(&self, _ctx: &EvaluationContext) -> ConditionResult {
8459        ConditionResult::True
8460    }
8461
8462    /// [614] Hinweis: Es werden nur die OBIS Kennzahlen übermittelt die für die Bilanzierung relevant sind
8463    fn evaluate_614(&self, _ctx: &EvaluationContext) -> ConditionResult {
8464        ConditionResult::True
8465    }
8466
8467    /// [617] Hinweis: Mehrere Vorgänge für eine Marktlokation bei Wechsel bilanzierungsrelevanter Stammdaten innerhalb des Bilanzierungsmonats
8468    fn evaluate_617(&self, _ctx: &EvaluationContext) -> ConditionResult {
8469        ConditionResult::True
8470    }
8471
8472    /// [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
8473    fn evaluate_618(&self, _ctx: &EvaluationContext) -> ConditionResult {
8474        ConditionResult::True
8475    }
8476
8477    /// [619] Hinweis: Für die selbständige Änderungsmöglichkeit der Schwellwertunter- / -über-schreitung
8478    fn evaluate_619(&self, _ctx: &EvaluationContext) -> ConditionResult {
8479        ConditionResult::True
8480    }
8481
8482    /// [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.
8483    fn evaluate_621(&self, _ctx: &EvaluationContext) -> ConditionResult {
8484        ConditionResult::True
8485    }
8486
8487    /// [622] Hinweis: Falls die OBIS-Kennzahl für mehrere Marktrollen relevant ist, so muss die Segmentgruppe pro Marktrolle wiederholt werden
8488    fn evaluate_622(&self, _ctx: &EvaluationContext) -> ConditionResult {
8489        ConditionResult::True
8490    }
8491
8492    /// [623] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben, die zur Ermittlung der Energiemenge der im Vorgang genannten Marktlokation benötigt werden
8493    fn evaluate_623(&self, _ctx: &EvaluationContext) -> ConditionResult {
8494        ConditionResult::True
8495    }
8496
8497    /// [630] Hinweis: Wenn die Liste abgelehnt wird, ist kein Vorgang enthalten
8498    fn evaluate_630(&self, _ctx: &EvaluationContext) -> ConditionResult {
8499        ConditionResult::True
8500    }
8501
8502    /// [631] Hinweis: Es ist die Listennummer aus der Lieferanten- bzw. Bilanzierungsgebietsclearingliste zu verwenden
8503    fn evaluate_631(&self, _ctx: &EvaluationContext) -> ConditionResult {
8504        ConditionResult::True
8505    }
8506
8507    /// [632] Hinweis: Es ist die Listennummer aus der Lieferanten-ausfallarbeits-clearingliste zu verwenden
8508    fn evaluate_632(&self, _ctx: &EvaluationContext) -> ConditionResult {
8509        ConditionResult::True
8510    }
8511
8512    /// [637] Hinweis: Bei Verpflichtungsanfrage
8513    fn evaluate_637(&self, _ctx: &EvaluationContext) -> ConditionResult {
8514        ConditionResult::True
8515    }
8516
8517    /// [638] Hinweis: Bei Aufforderung zur Übernahme der einzelnen Messlokation durch den gMSB
8518    fn evaluate_638(&self, _ctx: &EvaluationContext) -> ConditionResult {
8519        ConditionResult::True
8520    }
8521
8522    /// [639] Hinweis: Wenn Antwort auf Deaktivierung
8523    fn evaluate_639(&self, _ctx: &EvaluationContext) -> ConditionResult {
8524        ConditionResult::True
8525    }
8526
8527    /// [640] Hinweis: Wenn Antwort auf Aktivierung
8528    fn evaluate_640(&self, _ctx: &EvaluationContext) -> ConditionResult {
8529        ConditionResult::True
8530    }
8531
8532    /// [641] Hinweis: Wenn Einzelanforderung vorliegt
8533    fn evaluate_641(&self, _ctx: &EvaluationContext) -> ConditionResult {
8534        ConditionResult::True
8535    }
8536
8537    /// [642] Hinweis: Wenn Abo vorliegt
8538    fn evaluate_642(&self, _ctx: &EvaluationContext) -> ConditionResult {
8539        ConditionResult::True
8540    }
8541
8542    /// [643] Hinweis: Nachfolgender Netzbetreiber
8543    fn evaluate_643(&self, _ctx: &EvaluationContext) -> ConditionResult {
8544        ConditionResult::True
8545    }
8546
8547    /// [645] Hinweis: Es ist der Bilanzkreis des LF anzugeben
8548    fn evaluate_645(&self, _ctx: &EvaluationContext) -> ConditionResult {
8549        ConditionResult::True
8550    }
8551
8552    /// [646] Hinweis: Es ist der RD-Bilanzkreis des ANB anzugeben
8553    fn evaluate_646(&self, _ctx: &EvaluationContext) -> ConditionResult {
8554        ConditionResult::True
8555    }
8556
8557    /// [647] Hinweis: Es ist der RD-Bilanzkreis des anfNB anzugeben
8558    fn evaluate_647(&self, _ctx: &EvaluationContext) -> ConditionResult {
8559        ConditionResult::True
8560    }
8561
8562    /// [648] Hinweis: Soll ein Produkt ab dem Datum \"Änderung zum\" nicht mehr vorhanden sein, wird dies durch nicht angeben des PIA+5 ausgedrückt
8563    fn evaluate_648(&self, _ctx: &EvaluationContext) -> ConditionResult {
8564        ConditionResult::True
8565    }
8566
8567    /// [651] Hinweis: Es sind die Marktlokationen anzugeben, zu welchen die hier genannte OBIS benötigt wird
8568    fn evaluate_651(&self, _ctx: &EvaluationContext) -> ConditionResult {
8569        ConditionResult::True
8570    }
8571
8572    /// [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...
8573    fn evaluate_653(&self, _ctx: &EvaluationContext) -> ConditionResult {
8574        ConditionResult::True
8575    }
8576
8577    /// [655] Hinweis: Es ist die Listennummer aus der DZÜ Liste zu verwenden
8578    fn evaluate_655(&self, _ctx: &EvaluationContext) -> ConditionResult {
8579        ConditionResult::True
8580    }
8581
8582    /// [659] Hinweis: Bei Tranchen ist nur der Meldepunkt der Tranche anzugeben
8583    fn evaluate_659(&self, _ctx: &EvaluationContext) -> ConditionResult {
8584        ConditionResult::True
8585    }
8586
8587    /// [660] Hinweis: Es ist die ID der BK-Summenzeitreihe Aggregationsebene BG anzugeben
8588    fn evaluate_660(&self, _ctx: &EvaluationContext) -> ConditionResult {
8589        ConditionResult::True
8590    }
8591
8592    /// [662] Hinweis: Es sind die Daten NGZ und die Daten der NZR anzugeben
8593    fn evaluate_662(&self, _ctx: &EvaluationContext) -> ConditionResult {
8594        ConditionResult::True
8595    }
8596
8597    /// [663] Hinweis: Es ist die ID der Marktlokation und die ZPB des ZP der NGZ anzugeben
8598    fn evaluate_663(&self, _ctx: &EvaluationContext) -> ConditionResult {
8599        ConditionResult::True
8600    }
8601
8602    /// [664] Hinweis: Es ist das BG des NB (LPB) anzugeben
8603    fn evaluate_664(&self, _ctx: &EvaluationContext) -> ConditionResult {
8604        ConditionResult::True
8605    }
8606
8607    /// [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
8608    fn evaluate_665(&self, _ctx: &EvaluationContext) -> ConditionResult {
8609        ConditionResult::True
8610    }
8611
8612    /// [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...
8613    fn evaluate_667(&self, _ctx: &EvaluationContext) -> ConditionResult {
8614        ConditionResult::True
8615    }
8616
8617    /// [668] Hinweis: Dieses Segment wird nach Abschluss der Einführung der Lokationsbündelstruktur zum 01.10.2025 aus der UTILMD entfernt
8618    fn evaluate_668(&self, _ctx: &EvaluationContext) -> ConditionResult {
8619        ConditionResult::True
8620    }
8621
8622    /// [670] Hinweis: Es sind alle Netzlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8623    fn evaluate_670(&self, _ctx: &EvaluationContext) -> ConditionResult {
8624        ConditionResult::True
8625    }
8626
8627    /// [671] Hinweis: Es sind alle Marktlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8628    fn evaluate_671(&self, _ctx: &EvaluationContext) -> ConditionResult {
8629        ConditionResult::True
8630    }
8631
8632    /// [672] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8633    fn evaluate_672(&self, _ctx: &EvaluationContext) -> ConditionResult {
8634        ConditionResult::True
8635    }
8636
8637    /// [673] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8638    fn evaluate_673(&self, _ctx: &EvaluationContext) -> ConditionResult {
8639        ConditionResult::True
8640    }
8641
8642    /// [674] Hinweis: Es sind alle Tranchen, zu nennen, die dem gleichen Lokationsbündel angehören
8643    fn evaluate_674(&self, _ctx: &EvaluationContext) -> ConditionResult {
8644        ConditionResult::True
8645    }
8646
8647    /// [675] Hinweis: Es sind alle Messlokationen, zu nennen, die Lokationsbündel angehören
8648    fn evaluate_675(&self, _ctx: &EvaluationContext) -> ConditionResult {
8649        ConditionResult::True
8650    }
8651
8652    /// [677] Hinweis: Es sind alle Netzlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8653    fn evaluate_677(&self, _ctx: &EvaluationContext) -> ConditionResult {
8654        ConditionResult::True
8655    }
8656
8657    /// [678] Hinweis: Es sind alle Marktlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8658    fn evaluate_678(&self, _ctx: &EvaluationContext) -> ConditionResult {
8659        ConditionResult::True
8660    }
8661
8662    /// [679] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8663    fn evaluate_679(&self, _ctx: &EvaluationContext) -> ConditionResult {
8664        ConditionResult::True
8665    }
8666
8667    /// [680] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8668    fn evaluate_680(&self, _ctx: &EvaluationContext) -> ConditionResult {
8669        ConditionResult::True
8670    }
8671
8672    /// [682] Hinweis: Es sind alle Messlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8673    fn evaluate_682(&self, _ctx: &EvaluationContext) -> ConditionResult {
8674        ConditionResult::True
8675    }
8676
8677    /// [683] Hinweis: Es sind alle ID der Netzlokationen, welche der im SG5 LOC+Z16 angegebenen Marktlokation aktuell und / oder zukünftig vorgelagert sind, anzugeben
8678    fn evaluate_683(&self, _ctx: &EvaluationContext) -> ConditionResult {
8679        ConditionResult::True
8680    }
8681
8682    /// [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
8683    fn evaluate_684(&self, _ctx: &EvaluationContext) -> ConditionResult {
8684        ConditionResult::True
8685    }
8686
8687    /// [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.
8688    fn evaluate_685(&self, _ctx: &EvaluationContext) -> ConditionResult {
8689        ConditionResult::True
8690    }
8691
8692    /// [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
8693    fn evaluate_687(&self, _ctx: &EvaluationContext) -> ConditionResult {
8694        ConditionResult::True
8695    }
8696
8697    /// [688] Hinweis: Es sind alle ID der Netzlokationen, welche der im LOC+Z16 angegebenen Marktlokation vorgelagert sind, anzugeben
8698    fn evaluate_688(&self, _ctx: &EvaluationContext) -> ConditionResult {
8699        ConditionResult::True
8700    }
8701
8702    /// [689] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16 angegebenen Marktlokation zugehören, anzugeben
8703    fn evaluate_689(&self, _ctx: &EvaluationContext) -> ConditionResult {
8704        ConditionResult::True
8705    }
8706
8707    /// [690] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen zugeordnet sind, anzugeben
8708    fn evaluate_690(&self, _ctx: &EvaluationContext) -> ConditionResult {
8709        ConditionResult::True
8710    }
8711
8712    /// [693] Hinweis: Es sind die Daten NGZ anzugeben
8713    fn evaluate_693(&self, _ctx: &EvaluationContext) -> ConditionResult {
8714        ConditionResult::True
8715    }
8716
8717    /// [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...
8718    fn evaluate_694(&self, _ctx: &EvaluationContext) -> ConditionResult {
8719        ConditionResult::True
8720    }
8721
8722    /// [695] Hinweis: Verwendung, wenn Rolle LF einem Unternehmen NB zugeordnet ist
8723    fn evaluate_695(&self, _ctx: &EvaluationContext) -> ConditionResult {
8724        ConditionResult::True
8725    }
8726
8727    /// [696] Hinweis: Verwendung, nur wenn die Rolle LF nicht einem Unternehmen NB zugeordnet ist
8728    fn evaluate_696(&self, _ctx: &EvaluationContext) -> ConditionResult {
8729        ConditionResult::True
8730    }
8731
8732    /// [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...
8733    fn evaluate_698(&self, _ctx: &EvaluationContext) -> ConditionResult {
8734        ConditionResult::True
8735    }
8736
8737    /// [699] Hinweis: Es ist die ID der Steuerbare Ressource zu nennen über die die Technische Ressource gesteuert wird
8738    fn evaluate_699(&self, _ctx: &EvaluationContext) -> ConditionResult {
8739        ConditionResult::True
8740    }
8741
8742    /// [700] Hinweis: Es ist die ID der Netzlokation zu nennen über die die Technische Ressource gesteuert wird
8743    fn evaluate_700(&self, _ctx: &EvaluationContext) -> ConditionResult {
8744        ConditionResult::True
8745    }
8746
8747    /// [704] Hinweis: Segment ist zu verwenden, wenn es in Anfrage vorhanden war
8748    fn evaluate_704(&self, _ctx: &EvaluationContext) -> ConditionResult {
8749        ConditionResult::True
8750    }
8751
8752    /// [705] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Änderung\" durchgeführt wird
8753    fn evaluate_705(&self, _ctx: &EvaluationContext) -> ConditionResult {
8754        ConditionResult::True
8755    }
8756
8757    /// [706] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Bestellung einer Änderung von Stammdaten...\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8758    fn evaluate_706(&self, ctx: &EvaluationContext) -> ConditionResult {
8759        ctx.external.evaluate("data_clearing_required")
8760    }
8761
8762    /// [707] Hinweis: für weitere Details siehe Kapitel \"SG6 Verwendungszeitraum der Daten\"
8763    fn evaluate_707(&self, _ctx: &EvaluationContext) -> ConditionResult {
8764        ConditionResult::True
8765    }
8766
8767    /// [708] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des E/G zur Marktlokation\" durchgeführt wird
8768    fn evaluate_708(&self, _ctx: &EvaluationContext) -> ConditionResult {
8769        ConditionResult::True
8770    }
8771
8772    /// [709] Hinweis: Wenn die Aktion \"Zuordnung des E/G zur Marktlokation aufgrund fehlender Antwort\" durchgeführt wird
8773    fn evaluate_709(&self, _ctx: &EvaluationContext) -> ConditionResult {
8774        ConditionResult::True
8775    }
8776
8777    /// [710] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des LFN zur Marktlokation bzw. Tranche\" durchgeführt wird
8778    fn evaluate_710(&self, _ctx: &EvaluationContext) -> ConditionResult {
8779        ConditionResult::True
8780    }
8781
8782    /// [711] Hinweis: Wenn die Aktion \"Zuordnung des LFN zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8783    fn evaluate_711(&self, _ctx: &EvaluationContext) -> ConditionResult {
8784        ConditionResult::True
8785    }
8786
8787    /// [712] Hinweis: Wenn die Aktion \"Ankündigung der Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche\" durchgeführt wird
8788    fn evaluate_712(&self, _ctx: &EvaluationContext) -> ConditionResult {
8789        ConditionResult::True
8790    }
8791
8792    /// [713] Hinweis: Wenn die Aktion \"Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8793    fn evaluate_713(&self, _ctx: &EvaluationContext) -> ConditionResult {
8794        ConditionResult::True
8795    }
8796
8797    /// [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...
8798    fn evaluate_714(&self, _ctx: &EvaluationContext) -> ConditionResult {
8799        ConditionResult::True
8800    }
8801
8802    /// [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...
8803    fn evaluate_715(&self, _ctx: &EvaluationContext) -> ConditionResult {
8804        ConditionResult::True
8805    }
8806
8807    /// [716] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Abrechnungsdaten\" durchgeführt wird
8808    fn evaluate_716(&self, _ctx: &EvaluationContext) -> ConditionResult {
8809        ConditionResult::True
8810    }
8811
8812    /// [717] Hinweis: Wenn die Aktion des Sequenzdiagramms \"Bestellung einer Änderung von Abrechnungsdaten\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8813    fn evaluate_717(&self, ctx: &EvaluationContext) -> ConditionResult {
8814        ctx.external.evaluate("data_clearing_required")
8815    }
8816
8817    /// [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
8818    fn evaluate_718(&self, _ctx: &EvaluationContext) -> ConditionResult {
8819        ConditionResult::True
8820    }
8821
8822    /// [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...
8823    fn evaluate_719(&self, _ctx: &EvaluationContext) -> ConditionResult {
8824        ConditionResult::True
8825    }
8826
8827    /// [902] Format: Möglicher Wert: ≥ 0
8828    fn evaluate_902(&self, _ctx: &EvaluationContext) -> ConditionResult {
8829        // Format condition — informational, always applies
8830        ConditionResult::True
8831    }
8832
8833    /// [910] Format: Möglicher Wert: < 0 oder ≥ 0
8834    fn evaluate_910(&self, _ctx: &EvaluationContext) -> ConditionResult {
8835        // Format condition — informational, always applies
8836        ConditionResult::True
8837    }
8838
8839    /// [914] Format: Möglicher Wert: > 0
8840    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
8841        ctx.format_check("SEQ", 1, 0, |val| validate_numeric(val, ">", 0.0))
8842    }
8843
8844    /// [922] Format: TR-ID
8845    fn evaluate_922(&self, _ctx: &EvaluationContext) -> ConditionResult {
8846        // Format condition — informational, always applies
8847        ConditionResult::True
8848    }
8849
8850    /// [926] Format: Möglicher Wert: 0
8851    fn evaluate_926(&self, _ctx: &EvaluationContext) -> ConditionResult {
8852        // Format condition — informational, always applies
8853        ConditionResult::True
8854    }
8855
8856    /// [930] Format: max. 2 Nachkommastellen
8857    fn evaluate_930(&self, _ctx: &EvaluationContext) -> ConditionResult {
8858        // Format condition — informational, always applies
8859        ConditionResult::True
8860    }
8861
8862    /// [931] Format: ZZZ = +00
8863    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
8864        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
8865    }
8866
8867    /// [932] Format: HHMM = 2200
8868    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
8869        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
8870    }
8871
8872    /// [933] Format: HHMM = 2300
8873    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
8874        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
8875    }
8876
8877    /// [937] Format: keine Nachkommastelle
8878    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
8879        ctx.format_check("SEQ", 1, 0, |val| validate_max_decimal_places(val, 0))
8880    }
8881
8882    /// [938] Format: Möglicher Wert: <= 10
8883    fn evaluate_938(&self, _ctx: &EvaluationContext) -> ConditionResult {
8884        ConditionResult::True
8885    }
8886
8887    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
8888    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
8889        ctx.format_check("COM", 0, 0, validate_email)
8890    }
8891
8892    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
8893    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
8894        ctx.format_check("COM", 0, 0, validate_phone)
8895    }
8896
8897    /// [942] Format: n1-n2-n1-n3
8898    fn evaluate_942(&self, _ctx: &EvaluationContext) -> ConditionResult {
8899        // Format condition — informational, always applies
8900        ConditionResult::True
8901    }
8902
8903    /// [943] Format: n1-n2-n1
8904    fn evaluate_943(&self, _ctx: &EvaluationContext) -> ConditionResult {
8905        // Format condition — informational, always applies
8906        ConditionResult::True
8907    }
8908
8909    /// [946] Format: max. 11 Nachkommastellen
8910    fn evaluate_946(&self, _ctx: &EvaluationContext) -> ConditionResult {
8911        // Format condition — informational, always applies
8912        ConditionResult::True
8913    }
8914
8915    /// [948] Format: n1-n2-n1-n8-n2
8916    fn evaluate_948(&self, _ctx: &EvaluationContext) -> ConditionResult {
8917        // Format condition — informational, always applies
8918        ConditionResult::True
8919    }
8920
8921    /// [950] Format: Marktlokations-ID
8922    fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
8923        // Format condition — informational, always applies
8924        ConditionResult::True
8925    }
8926
8927    /// [951] Format: Zählpunktbezeichnung
8928    fn evaluate_951(&self, _ctx: &EvaluationContext) -> ConditionResult {
8929        // Format condition — informational, always applies
8930        ConditionResult::True
8931    }
8932
8933    /// [952] Format: Gerätenummer nach DIN 43863-5
8934    fn evaluate_952(&self, _ctx: &EvaluationContext) -> ConditionResult {
8935        // Format condition — informational, always applies
8936        ConditionResult::True
8937    }
8938
8939    /// [955] Format: Möglicher Wert: < 100
8940    fn evaluate_955(&self, _ctx: &EvaluationContext) -> ConditionResult {
8941        // Format condition — informational, always applies
8942        ConditionResult::True
8943    }
8944
8945    /// [957] Format: n1-n2-n1-n8
8946    fn evaluate_957(&self, _ctx: &EvaluationContext) -> ConditionResult {
8947        // Format condition — informational, always applies
8948        ConditionResult::True
8949    }
8950
8951    /// [960] Format: Netzlokations-ID
8952    fn evaluate_960(&self, _ctx: &EvaluationContext) -> ConditionResult {
8953        // Format condition — informational, always applies
8954        ConditionResult::True
8955    }
8956
8957    /// [961] Format: SR-ID
8958    fn evaluate_961(&self, _ctx: &EvaluationContext) -> ConditionResult {
8959        // Format condition — informational, always applies
8960        ConditionResult::True
8961    }
8962
8963    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
8964    fn evaluate_967(&self, _ctx: &EvaluationContext) -> ConditionResult {
8965        // Format condition — informational, always applies
8966        ConditionResult::True
8967    }
8968
8969    /// [2001] Segmentgruppe ist mindestens zweimal je SG4 IDE+24 (Vorgang) anzugeben
8970    fn evaluate_2001(&self, ctx: &EvaluationContext) -> ConditionResult {
8971        // Cardinality: at least twice per SG4 IDE+24
8972        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8973        ConditionResult::from(ide_count > 0)
8974    }
8975
8976    /// [2002] Für jede Produktpaket-ID im SG8 SEQ+Z79 (Erforderliches Produkt) DE1050 genau einmal anzugeben
8977    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
8978        let seq_segs = ctx.find_segments("SEQ");
8979        let has_z79 = seq_segs.iter().any(|s| {
8980            s.elements
8981                .first()
8982                .and_then(|e| e.first())
8983                .is_some_and(|v| v == "Z79")
8984        });
8985        ConditionResult::from(has_z79)
8986    }
8987
8988    /// [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
8989    fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
8990        // TODO: implement
8991        ConditionResult::Unknown
8992    }
8993
8994    /// [2004] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8995    // 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)
8996    fn evaluate_2004(&self, ctx: &EvaluationContext) -> ConditionResult {
8997        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
8998        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
8999
9000        let zeitraum_ids: Vec<String> = rff_qualifiers
9001            .iter()
9002            .filter(|(_, qual)| qual.as_str() == "Z49")
9003            .filter_map(|(idx, _)| {
9004                rff_zeitraum_ids
9005                    .iter()
9006                    .find(|(i, v)| i == idx && !v.is_empty())
9007                    .map(|(_, v)| v.clone())
9008            })
9009            .collect();
9010
9011        if zeitraum_ids.is_empty() {
9012            return ConditionResult::Unknown;
9013        }
9014
9015        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
9016
9017        for zid in &zeitraum_ids {
9018            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
9019            if count != 1 {
9020                return ConditionResult::False;
9021            }
9022        }
9023
9024        ConditionResult::True
9025    }
9026
9027    /// [2005] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
9028    // 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)
9029    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
9030        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
9031        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
9032
9033        let zeitraum_ids: Vec<String> = rff_qualifiers
9034            .iter()
9035            .filter(|(_, qual)| qual.as_str() == "Z49")
9036            .filter_map(|(idx, _)| {
9037                rff_zeitraum_ids
9038                    .iter()
9039                    .find(|(i, v)| i == idx && !v.is_empty())
9040                    .map(|(_, v)| v.clone())
9041            })
9042            .collect();
9043
9044        if zeitraum_ids.is_empty() {
9045            return ConditionResult::Unknown;
9046        }
9047
9048        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
9049
9050        for zid in &zeitraum_ids {
9051            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
9052            if count == 0 {
9053                return ConditionResult::False;
9054            }
9055        }
9056
9057        ConditionResult::True
9058    }
9059
9060    /// [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 ...
9061    fn evaluate_2010(&self, _ctx: &EvaluationContext) -> ConditionResult {
9062        // TODO: implement
9063        ConditionResult::Unknown
9064    }
9065
9066    /// [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 ...
9067    /// EXTERNAL: Requires context from outside the message.
9068    // 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)
9069    fn evaluate_2011(&self, ctx: &EvaluationContext) -> ConditionResult {
9070        ctx.external
9071            .evaluate("request_message_zeitraum_ids_present")
9072    }
9073
9074    /// [2012] Segmentgruppe ist genau einmal für die Angabe der Informativen Daten der Marktlokation anzugeben
9075    fn evaluate_2012(&self, _ctx: &EvaluationContext) -> ConditionResult {
9076        // TODO: implement
9077        ConditionResult::Unknown
9078    }
9079
9080    /// [2013] Mindesten einmal anzugeben wenn kein SG6 RFF+Z54 (Verwendungszeitraum der Daten: Im System keine Daten vorhanden) vorhanden
9081    fn evaluate_2013(&self, ctx: &EvaluationContext) -> ConditionResult {
9082        // Check: condition applies when no RFF+Z54 exists
9083        ctx.lacks_qualifier("RFF", 0, "Z54")
9084    }
9085
9086    /// [2014] Mindesten einmal anzugeben wenn kein SG6 RFF+Z47 (Verwendungszeitraum der Daten: Im System vorhandene Daten) vorhanden
9087    fn evaluate_2014(&self, ctx: &EvaluationContext) -> ConditionResult {
9088        // Check: condition applies when no RFF+Z47 exists
9089        ctx.lacks_qualifier("RFF", 0, "Z47")
9090    }
9091
9092    /// [2015] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) untergeordnet ist
9093    fn evaluate_2015(&self, _ctx: &EvaluationContext) -> ConditionResult {
9094        // TODO: implement
9095        ConditionResult::Unknown
9096    }
9097
9098    /// [2016] Je SG5 LOC+Z16 (Marktlokation) ist genau einmal die Segmentgruppe anzugeben
9099    fn evaluate_2016(&self, ctx: &EvaluationContext) -> ConditionResult {
9100        // Cardinality: exactly once per SG5 LOC+Z16 (Marktlokation)
9101        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9102        ConditionResult::from(loc_count > 0)
9103    }
9104
9105    /// [2017] Je SG5 LOC+Z17 (Messlokation) ist genau einmal die Segmentgruppe anzugeben
9106    fn evaluate_2017(&self, ctx: &EvaluationContext) -> ConditionResult {
9107        // Cardinality: exactly once per SG5 LOC+Z17 (Messlokation)
9108        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9109        ConditionResult::from(loc_count > 0)
9110    }
9111
9112    /// [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...
9113    fn evaluate_2018(&self, _ctx: &EvaluationContext) -> ConditionResult {
9114        // TODO: implement
9115        ConditionResult::Unknown
9116    }
9117
9118    /// [2060] Wenn SG10 CAV+NZR ist die Segmentgruppe genau zwei Mal je IDE+24 anzugeben
9119    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
9120        // Check: CAV+NZR exists (then segment group exactly twice per IDE+24)
9121        let found = ctx.find_segments("CAV").iter().any(|s| {
9122            s.elements
9123                .first()
9124                .and_then(|e| e.first())
9125                .is_some_and(|v| v == "NZR")
9126        });
9127        ConditionResult::from(found)
9128    }
9129
9130    /// [2061] Segment bzw. Segmentgruppe ist genau einmal je SG4 IDE (Vorgang) anzugeben
9131    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
9132        // Cardinality: exactly once per SG4 IDE (always true for single-transaction messages)
9133        let ide_count = ctx.find_segments("IDE").len();
9134        ConditionResult::from(ide_count > 0)
9135    }
9136
9137    /// [2071] Für die ID der LieferantensummenZR einmal je SG4 IDE+24 (Vorgang)
9138    fn evaluate_2071(&self, ctx: &EvaluationContext) -> ConditionResult {
9139        // Cardinality: LieferantensummenZR once per SG4 IDE+24
9140        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9141        ConditionResult::from(ide_count > 0)
9142    }
9143
9144    /// [2073] Für die ID der BilanzkreissummenZR einmal je SG4 IDE+24 (Vorgang)
9145    fn evaluate_2073(&self, ctx: &EvaluationContext) -> ConditionResult {
9146        // Cardinality: BilanzkreissummenZR once per SG4 IDE+24
9147        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9148        ConditionResult::from(ide_count > 0)
9149    }
9150
9151    /// [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)
9152    fn evaluate_2075(&self, ctx: &EvaluationContext) -> ConditionResult {
9153        // Cardinality: Zeitreihen (not EEG-EUZ/AAÜZ) once per SG4 IDE+24
9154        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9155        ConditionResult::from(ide_count > 0)
9156    }
9157
9158    /// [2080] Segmentgruppe ist max. zweimal je SG4 IDE+24 (Vorgang) anzugeben
9159    fn evaluate_2080(&self, ctx: &EvaluationContext) -> ConditionResult {
9160        // Cardinality: max twice per SG4 IDE+24
9161        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9162        ConditionResult::from(ide_count > 0)
9163    }
9164
9165    /// [2095] Je SG5 LOC+Z15 (MaBiS-Zählpunkt) ist genau einmal die Segmentgruppe anzugeben
9166    fn evaluate_2095(&self, ctx: &EvaluationContext) -> ConditionResult {
9167        // Cardinality: exactly once per SG5 LOC+Z15 (MaBiS-Zählpunkt)
9168        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z15").len();
9169        ConditionResult::from(loc_count > 0)
9170    }
9171
9172    /// [2096] Segmentgruppe ist genau zweimal je SG4 IDE anzugeben
9173    fn evaluate_2096(&self, ctx: &EvaluationContext) -> ConditionResult {
9174        // Cardinality: exactly twice per SG4 IDE
9175        let ide_count = ctx.find_segments("IDE").len();
9176        ConditionResult::from(ide_count > 0)
9177    }
9178
9179    /// [2119] Je SG8 SEQ+Z13/ ZG0 (Smartmeter-Gateway) ist genau einmal die Segmentgruppe anzugeben
9180    fn evaluate_2119(&self, ctx: &EvaluationContext) -> ConditionResult {
9181        // Cardinality: exactly once per SG8 SEQ+Z13/ZG0 (Smartmeter-Gateway)
9182        let found = ctx.find_segments("SEQ").iter().any(|s| {
9183            s.elements
9184                .first()
9185                .and_then(|e| e.first())
9186                .is_some_and(|v| v == "Z13" || v == "ZG0")
9187        });
9188        ConditionResult::from(found)
9189    }
9190
9191    /// [2140] Für die ID der LieferantensummenZR einmal je SG4 IDE+Z01 (Liste)
9192    fn evaluate_2140(&self, ctx: &EvaluationContext) -> ConditionResult {
9193        // Cardinality: LieferantensummenZR once per SG4 IDE+Z01
9194        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9195        ConditionResult::from(ide_count > 0)
9196    }
9197
9198    /// [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
9199    fn evaluate_2182(&self, ctx: &EvaluationContext) -> ConditionResult {
9200        // Cardinality: exactly once per SG8 SEQ+Z01/Z80/Z81/Z98
9201        let found = ctx.find_segments("SEQ").iter().any(|s| {
9202            s.elements
9203                .first()
9204                .and_then(|e| e.first())
9205                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9206        });
9207        ConditionResult::from(found)
9208    }
9209
9210    /// [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
9211    fn evaluate_2183(&self, ctx: &EvaluationContext) -> ConditionResult {
9212        // Cardinality: exactly twice per SG8 SEQ+Z01/Z80/Z81/Z98
9213        let found = ctx.find_segments("SEQ").iter().any(|s| {
9214            s.elements
9215                .first()
9216                .and_then(|e| e.first())
9217                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9218        });
9219        ConditionResult::from(found)
9220    }
9221
9222    /// [2207] Für die ID der Lieferanten-ausfall-arbeits-summen-zeitreihe einmal je SG4 IDE+Z01 (Liste)
9223    fn evaluate_2207(&self, ctx: &EvaluationContext) -> ConditionResult {
9224        // Cardinality: Lieferantenausfallarbeitssummenzeitreihe once per SG4 IDE+Z01
9225        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9226        ConditionResult::from(ide_count > 0)
9227    }
9228
9229    /// [2225] Einmal für jede Marktlokation bzw. Tranche für die der LF nicht die gemeldete Ansicht des NB teilt
9230    fn evaluate_2225(&self, _ctx: &EvaluationContext) -> ConditionResult {
9231        // TODO: implement
9232        ConditionResult::Unknown
9233    }
9234
9235    /// [2236] Code einmal je SG4 IDE+24 (Vorgang)
9236    fn evaluate_2236(&self, ctx: &EvaluationContext) -> ConditionResult {
9237        // Cardinality: code once per SG4 IDE+24
9238        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9239        ConditionResult::from(ide_count > 0)
9240    }
9241
9242    /// [2252] Einmal für jede Marktlokation bzw. Tranche, die in der Lieferantenausfallarbeits-summenzeitreihe berücksichtigt wurde
9243    fn evaluate_2252(&self, _ctx: &EvaluationContext) -> ConditionResult {
9244        // TODO: implement
9245        ConditionResult::Unknown
9246    }
9247
9248    /// [2261] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9249    fn evaluate_2261(&self, ctx: &EvaluationContext) -> ConditionResult {
9250        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9251        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9252        ConditionResult::from(loc_count > 0)
9253    }
9254
9255    /// [2284] Für jede Messlokations-ID im SG5 LOC+Z17 (Messlokation) DE3225 genau einmal anzugeben
9256    fn evaluate_2284(&self, ctx: &EvaluationContext) -> ConditionResult {
9257        // Cardinality: exactly once per LOC+Z17 (Messlokation) ID
9258        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9259        ConditionResult::from(loc_count > 0)
9260    }
9261
9262    /// [2286] Für jede SEQ+ZF3 (Daten der Messlokation) mindestens einmal anzugeben
9263    fn evaluate_2286(&self, ctx: &EvaluationContext) -> ConditionResult {
9264        // Cardinality: at least once per SEQ+ZF3 (Daten der Messlokation)
9265        let found = !ctx.find_segments_with_qualifier("SEQ", 0, "ZF3").is_empty();
9266        ConditionResult::from(found)
9267    }
9268
9269    /// [2287] Für jede SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) mindestens einmal anzugeben
9270    fn evaluate_2287(&self, ctx: &EvaluationContext) -> ConditionResult {
9271        // Cardinality: at least once per SEQ+Z03/ZF5 (Zähleinrichtungsdaten)
9272        let found = ctx.find_segments("SEQ").iter().any(|s| {
9273            s.elements
9274                .first()
9275                .and_then(|e| e.first())
9276                .is_some_and(|v| v == "Z03" || v == "ZF5")
9277        });
9278        ConditionResult::from(found)
9279    }
9280
9281    /// [2288] Einmal für jede Zeitreihe, die in der DZR berücksichtigt wurde
9282    fn evaluate_2288(&self, _ctx: &EvaluationContext) -> ConditionResult {
9283        // TODO: implement
9284        ConditionResult::Unknown
9285    }
9286
9287    /// [2307] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225 genau einmal anzugeben
9288    fn evaluate_2307(&self, ctx: &EvaluationContext) -> ConditionResult {
9289        // Cardinality: exactly once per LOC+Z21 (Tranche) ID
9290        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9291        ConditionResult::from(loc_count > 0)
9292    }
9293
9294    /// [2308] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225, mindestens einmal anzugeben
9295    fn evaluate_2308(&self, ctx: &EvaluationContext) -> ConditionResult {
9296        // Cardinality: at least once per LOC+Z16 (Marktlokation) ID
9297        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9298        ConditionResult::from(loc_count > 0)
9299    }
9300
9301    /// [2309] Für jede ID im SG5 LOC+Z17 (Messlokation) DE3225 mindestens einmal anzugeben
9302    fn evaluate_2309(&self, ctx: &EvaluationContext) -> ConditionResult {
9303        // Cardinality: at least once per LOC+Z17 (Messlokation) ID
9304        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9305        ConditionResult::from(loc_count > 0)
9306    }
9307
9308    /// [2310] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225 genau einmal anzugeben
9309    fn evaluate_2310(&self, ctx: &EvaluationContext) -> ConditionResult {
9310        // Cardinality: exactly once per LOC+Z16 (Marktlokation) ID
9311        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9312        ConditionResult::from(loc_count > 0)
9313    }
9314
9315    /// [2311] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9316    fn evaluate_2311(&self, ctx: &EvaluationContext) -> ConditionResult {
9317        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9318        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9319        ConditionResult::from(loc_count > 0)
9320    }
9321
9322    /// [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...
9323    fn evaluate_2312(&self, ctx: &EvaluationContext) -> ConditionResult {
9324        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9325        // Check: RFF+Z33 DE1154 does NOT contain Objektcode 9992000001256 (Netzlokation)
9326        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
9327        let has_netzlokation = rff_z33.iter().any(|s| {
9328            s.elements
9329                .first()
9330                .and_then(|e| e.get(1))
9331                .is_some_and(|v| v == "9992000001256")
9332        });
9333        ConditionResult::from(!has_netzlokation)
9334    }
9335
9336    /// [2313] Je SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels) genau einmal anzugeben
9337    fn evaluate_2313(&self, ctx: &EvaluationContext) -> ConditionResult {
9338        // Check: SG8 with SEQ+Z58/ZC9/ZD0/ZD6 exists (cardinality constraint)
9339        let found = ctx.find_segments("SEQ").iter().any(|s| {
9340            s.elements
9341                .first()
9342                .and_then(|e| e.first())
9343                .is_some_and(|v| ["Z58", "ZC9", "ZD0", "ZD6"].contains(&v.as_str()))
9344        });
9345        ConditionResult::from(found)
9346    }
9347
9348    /// [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
9349    fn evaluate_2317(&self, ctx: &EvaluationContext) -> ConditionResult {
9350        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9351        // Check: SG10 CCI+++Z25 has CAV+MIW/MPW/MUW (Wandler types)
9352        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9353            s.elements
9354                .first()
9355                .and_then(|e| e.first())
9356                .is_some_and(|v| ["MIW", "MPW", "MUW"].contains(&v.as_str()))
9357        });
9358        ConditionResult::from(has_cav)
9359    }
9360
9361    /// [2318] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MBW (Blockstromwandler) vorhanden, ist das Segment genau einmal anzugeben
9362    fn evaluate_2318(&self, ctx: &EvaluationContext) -> ConditionResult {
9363        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9364        // Check: SG10 CCI+++Z25 has CAV+MBW (Blockstromwandler)
9365        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9366            s.elements
9367                .first()
9368                .and_then(|e| e.first())
9369                .is_some_and(|v| v == "MBW")
9370        });
9371        ConditionResult::from(has_cav)
9372    }
9373
9374    /// [2344] Einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
9375    fn evaluate_2344(&self, _ctx: &EvaluationContext) -> ConditionResult {
9376        // TODO: implement
9377        ConditionResult::Unknown
9378    }
9379
9380    /// [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...
9381    fn evaluate_2350(&self, _ctx: &EvaluationContext) -> ConditionResult {
9382        // TODO: implement
9383        ConditionResult::Unknown
9384    }
9385
9386    /// [2351] Einmal je ZP einer BK-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufgenommen wurde
9387    fn evaluate_2351(&self, _ctx: &EvaluationContext) -> ConditionResult {
9388        // TODO: implement
9389        ConditionResult::Unknown
9390    }
9391
9392    /// [2352] Einmal je ZP einer BG-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufge-nommen wurde
9393    fn evaluate_2352(&self, _ctx: &EvaluationContext) -> ConditionResult {
9394        // TODO: implement
9395        ConditionResult::Unknown
9396    }
9397
9398    /// [2356] Je SG5 LOC+Z18 (Netzlokation) ist genau einmal die Segmentgruppe anzugeben
9399    fn evaluate_2356(&self, ctx: &EvaluationContext) -> ConditionResult {
9400        // Cardinality: exactly once per SG5 LOC+Z18 (Netzlokation)
9401        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9402        ConditionResult::from(loc_count > 0)
9403    }
9404
9405    /// [2357] Je SG5 LOC+Z20 (Technische Ressource) ist genau einmal die Segmentgruppe anzugeben
9406    fn evaluate_2357(&self, ctx: &EvaluationContext) -> ConditionResult {
9407        // Cardinality: exactly once per SG5 LOC+Z20 (Technische Ressource)
9408        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z20").len();
9409        ConditionResult::from(loc_count > 0)
9410    }
9411
9412    /// [2358] Je SG5 LOC+Z19 (Steuerbare Ressource) ist genau einmal die Segmentgruppe anzugeben
9413    fn evaluate_2358(&self, ctx: &EvaluationContext) -> ConditionResult {
9414        // Cardinality: exactly once per SG5 LOC+Z19 (Steuerbare Ressource)
9415        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z19").len();
9416        ConditionResult::from(loc_count > 0)
9417    }
9418
9419    /// [2359] Für jede ID im SG5 LOC+Z16 / Z17 / Z20 (Marktlokation / Messlokation / Technische Ressource) DE3225 mindestens einmal anzugeben
9420    fn evaluate_2359(&self, ctx: &EvaluationContext) -> ConditionResult {
9421        // Cardinality: at least once per LOC+Z16/Z17/Z20 ID
9422        let found = ctx.find_segments("LOC").iter().any(|s| {
9423            s.elements
9424                .first()
9425                .and_then(|e| e.first())
9426                .is_some_and(|v| ["Z16", "Z17", "Z20"].contains(&v.as_str()))
9427        });
9428        ConditionResult::from(found)
9429    }
9430
9431    /// [2360] Für jede ID im SG5 LOC+Z18 (Netzlokation) DE3225 mindestens einmal anzugeben
9432    fn evaluate_2360(&self, ctx: &EvaluationContext) -> ConditionResult {
9433        // Cardinality: at least once per LOC+Z18 (Netzlokation) ID
9434        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9435        ConditionResult::from(loc_count > 0)
9436    }
9437
9438    /// [2361] Für jede ID im SG5 LOC+Z17 / Z20 / Z22 (Messlokation / Technische Ressource / ruhende Marktlokation) DE3225 mindestens einmal anzugeben
9439    fn evaluate_2361(&self, ctx: &EvaluationContext) -> ConditionResult {
9440        // Cardinality: at least once per LOC+Z17/Z20/Z22 ID
9441        let found = ctx.find_segments("LOC").iter().any(|s| {
9442            s.elements
9443                .first()
9444                .and_then(|e| e.first())
9445                .is_some_and(|v| ["Z17", "Z20", "Z22"].contains(&v.as_str()))
9446        });
9447        ConditionResult::from(found)
9448    }
9449}
9450
9451// HAND-EDITED: helpers for [223] / [265].
9452
9453use mig_types::segment::OwnedSegment;
9454
9455/// One SG8 repetition: its SEQ qualifier, the Marktlokation IDs of its
9456/// RFF+Z18, and its SG10s as `(CCIs, CAVs)`.
9457struct Sg8Block {
9458    seq: String,
9459    malo_refs: Vec<String>,
9460    sg10: Vec<(Vec<OwnedSegment>, Vec<OwnedSegment>)>,
9461}
9462
9463fn component(seg: &OwnedSegment, element: usize, comp: usize) -> Option<&str> {
9464    seg.elements
9465        .get(element)
9466        .and_then(|e| e.get(comp))
9467        .map(String::as_str)
9468}
9469
9470/// The message's SG8 repetitions, from the group navigator when there is one,
9471/// else from the segments in message order: SEQ opens an SG8, CCI opens an
9472/// SG10 within it that takes the CAVs after it. (`validate_pid_with_conditions`
9473/// evaluates BO4E input without a navigator.) `None` without either.
9474fn sg8_blocks(ctx: &EvaluationContext) -> Option<Vec<Sg8Block>> {
9475    let malo_refs = |rffs: &[OwnedSegment]| -> Vec<String> {
9476        rffs.iter()
9477            .filter(|s| component(s, 0, 0) == Some("Z18"))
9478            .filter_map(|s| component(s, 0, 1).map(str::to_string))
9479            .collect()
9480    };
9481    if let Some(nav) = ctx.navigator() {
9482        let sg8 = ["SG4", "SG8"];
9483        return Some(
9484            (0..nav.group_instance_count(&sg8))
9485                .map(|i| Sg8Block {
9486                    seq: nav
9487                        .find_segments_in_group("SEQ", &sg8, i)
9488                        .first()
9489                        .and_then(|s| component(s, 0, 0).map(str::to_string))
9490                        .unwrap_or_default(),
9491                    malo_refs: malo_refs(&nav.find_segments_in_group("RFF", &sg8, i)),
9492                    sg10: (0..nav.child_group_instance_count(&sg8, i, "SG10"))
9493                        .map(|j| {
9494                            (
9495                                nav.find_segments_in_child_group("CCI", &sg8, i, "SG10", j),
9496                                nav.find_segments_in_child_group("CAV", &sg8, i, "SG10", j),
9497                            )
9498                        })
9499                        .collect(),
9500                })
9501                .collect(),
9502        );
9503    }
9504    if ctx.segments.is_empty() {
9505        return None;
9506    }
9507    let mut blocks: Vec<Sg8Block> = Vec::new();
9508    let mut rffs: Vec<OwnedSegment> = Vec::new();
9509    for seg in ctx.segments {
9510        match seg.id.as_str() {
9511            "SEQ" => {
9512                if let Some(b) = blocks.last_mut() {
9513                    b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9514                }
9515                rffs.clear();
9516                blocks.push(Sg8Block {
9517                    seq: component(seg, 0, 0).unwrap_or_default().to_string(),
9518                    malo_refs: Vec::new(),
9519                    sg10: Vec::new(),
9520                });
9521            }
9522            // Segments after the SG8s end the last one.
9523            "NAD" | "UNT" | "IDE" => {
9524                if let Some(b) = blocks.last_mut() {
9525                    if b.malo_refs.is_empty() {
9526                        b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9527                    }
9528                }
9529                rffs.clear();
9530                if seg.id == "IDE" {
9531                    continue;
9532                }
9533                break;
9534            }
9535            "RFF" if !blocks.is_empty() => rffs.push(seg.clone()),
9536            "CCI" => {
9537                if let Some(b) = blocks.last_mut() {
9538                    b.sg10.push((vec![seg.clone()], Vec::new()));
9539                }
9540            }
9541            "CAV" => {
9542                if let Some((_, cavs)) = blocks.last_mut().and_then(|b| b.sg10.last_mut()) {
9543                    cavs.push(seg.clone());
9544                }
9545            }
9546            _ => {}
9547        }
9548    }
9549    if let Some(b) = blocks.last_mut() {
9550        if b.malo_refs.is_empty() {
9551            b.malo_refs = malo_refs(&rffs);
9552        }
9553    }
9554    Some(blocks)
9555}
9556
9557// HAND-EDITED: "SG10 CCI+… CAV+… vorhanden" pairs a CCI and a CAV of one SG10
9558// (#255): [101], [102], [136], [216], [232], [234], [239], [346], [461], [462].
9559
9560/// Whether one SG10 — its CCI and CAV segments — satisfies `pred`: an SG10 of
9561/// the enclosing SG8 when `this_sg8` ("in dieser SG8") and the scope reaches
9562/// one; any SG10 of the message otherwise.
9563fn sg10_has(
9564    ctx: &EvaluationContext,
9565    this_sg8: bool,
9566    pred: impl Fn(&[OwnedSegment]) -> bool,
9567) -> ConditionResult {
9568    if this_sg8 {
9569        if let Some(sg10s) = ctx.enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"]) {
9570            return ConditionResult::from(sg10s.iter().any(|segs| pred(segs)));
9571        }
9572    }
9573    let Some(blocks) = sg8_blocks(ctx) else {
9574        return ConditionResult::False;
9575    };
9576    ConditionResult::from(blocks.iter().flat_map(|b| &b.sg10).any(|(ccis, cavs)| {
9577        let segs: Vec<OwnedSegment> = ccis.iter().chain(cavs).cloned().collect();
9578        pred(&segs)
9579    }))
9580}
9581
9582/// "in derselben SG10": whether the enclosing SG10's CCI and CAV segments
9583/// satisfy `pred`, when the scope reaches one; [`sg10_has`] over the message
9584/// otherwise.
9585fn this_sg10_has(
9586    ctx: &EvaluationContext,
9587    pred: impl Fn(&[OwnedSegment]) -> bool,
9588) -> ConditionResult {
9589    if let Some(path) = ctx.enclosing_scope("SG10") {
9590        let segs: Vec<OwnedSegment> = ["CCI", "CAV"]
9591            .iter()
9592            .flat_map(|tag| ctx.find_segments_at(path, tag))
9593            .collect();
9594        return ConditionResult::from(pred(&segs));
9595    }
9596    sg10_has(ctx, false, pred)
9597}