Skip to main content

automapper_validation/generated/fv2504/
utilmd_strom_conditions_fv2504.rs

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