Skip to main content

automapper_validation/generated/fv2504/
utilmd_strom_conditions_fv2504.rs

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