Skip to main content

automapper_validation/generated/fv2504/
utilmd_strom_conditions_fv2504.rs

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