Skip to main content

automapper_validation/generated/fv2504/
iftsta_conditions_fv2504.rs

1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2504/IFTSTA_AHB_2_0g_Fehlerkorrektur_20250225.xml
4// Generated: 2026-03-12T09:53:39Z
5// </auto-generated>
6
7// LLM-produced bodies aren't idiomatic Rust — silence stylistic lints at
8// file scope so `cargo clippy -D warnings` stays green. Correctness,
9// suspicious, and perf categories stay on so real bugs still surface.
10#![allow(clippy::style, clippy::complexity)]
11
12#[allow(unused_imports)]
13use crate::eval::format_validators::*;
14use crate::eval::{ConditionEvaluator, ConditionResult, EvaluationContext};
15
16/// Generated condition evaluator for IFTSTA FV2504.
17pub struct IftstaConditionEvaluatorFV2504 {
18    // External condition IDs that require runtime context.
19    external_conditions: std::collections::HashSet<u32>,
20}
21
22impl Default for IftstaConditionEvaluatorFV2504 {
23    fn default() -> Self {
24        let mut external_conditions = std::collections::HashSet::new();
25        external_conditions.insert(1);
26        external_conditions.insert(6);
27        external_conditions.insert(7);
28        external_conditions.insert(8);
29        external_conditions.insert(10);
30        external_conditions.insert(16);
31        external_conditions.insert(17);
32        external_conditions.insert(20);
33        external_conditions.insert(26);
34        external_conditions.insert(27);
35        external_conditions.insert(28);
36        external_conditions.insert(29);
37        external_conditions.insert(72);
38        external_conditions.insert(76);
39        external_conditions.insert(86);
40        external_conditions.insert(114);
41        external_conditions.insert(115);
42        external_conditions.insert(117);
43        external_conditions.insert(492);
44        external_conditions.insert(493);
45        Self {
46            external_conditions,
47        }
48    }
49}
50
51impl ConditionEvaluator for IftstaConditionEvaluatorFV2504 {
52    fn message_type(&self) -> &str {
53        "IFTSTA"
54    }
55
56    fn format_version(&self) -> &str {
57        "FV2504"
58    }
59
60    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
61        match condition {
62            1 => self.evaluate_1(ctx),
63            3 => self.evaluate_3(ctx),
64            4 => self.evaluate_4(ctx),
65            5 => self.evaluate_5(ctx),
66            6 => self.evaluate_6(ctx),
67            7 => self.evaluate_7(ctx),
68            8 => self.evaluate_8(ctx),
69            10 => self.evaluate_10(ctx),
70            16 => self.evaluate_16(ctx),
71            17 => self.evaluate_17(ctx),
72            18 => self.evaluate_18(ctx),
73            19 => self.evaluate_19(ctx),
74            20 => self.evaluate_20(ctx),
75            26 => self.evaluate_26(ctx),
76            27 => self.evaluate_27(ctx),
77            28 => self.evaluate_28(ctx),
78            29 => self.evaluate_29(ctx),
79            30 => self.evaluate_30(ctx),
80            31 => self.evaluate_31(ctx),
81            32 => self.evaluate_32(ctx),
82            33 => self.evaluate_33(ctx),
83            43 => self.evaluate_43(ctx),
84            44 => self.evaluate_44(ctx),
85            45 => self.evaluate_45(ctx),
86            46 => self.evaluate_46(ctx),
87            47 => self.evaluate_47(ctx),
88            48 => self.evaluate_48(ctx),
89            49 => self.evaluate_49(ctx),
90            50 => self.evaluate_50(ctx),
91            51 => self.evaluate_51(ctx),
92            52 => self.evaluate_52(ctx),
93            53 => self.evaluate_53(ctx),
94            54 => self.evaluate_54(ctx),
95            55 => self.evaluate_55(ctx),
96            56 => self.evaluate_56(ctx),
97            57 => self.evaluate_57(ctx),
98            58 => self.evaluate_58(ctx),
99            59 => self.evaluate_59(ctx),
100            60 => self.evaluate_60(ctx),
101            61 => self.evaluate_61(ctx),
102            62 => self.evaluate_62(ctx),
103            63 => self.evaluate_63(ctx),
104            64 => self.evaluate_64(ctx),
105            65 => self.evaluate_65(ctx),
106            66 => self.evaluate_66(ctx),
107            67 => self.evaluate_67(ctx),
108            68 => self.evaluate_68(ctx),
109            69 => self.evaluate_69(ctx),
110            70 => self.evaluate_70(ctx),
111            71 => self.evaluate_71(ctx),
112            72 => self.evaluate_72(ctx),
113            76 => self.evaluate_76(ctx),
114            77 => self.evaluate_77(ctx),
115            78 => self.evaluate_78(ctx),
116            79 => self.evaluate_79(ctx),
117            80 => self.evaluate_80(ctx),
118            83 => self.evaluate_83(ctx),
119            84 => self.evaluate_84(ctx),
120            85 => self.evaluate_85(ctx),
121            86 => self.evaluate_86(ctx),
122            91 => self.evaluate_91(ctx),
123            92 => self.evaluate_92(ctx),
124            93 => self.evaluate_93(ctx),
125            94 => self.evaluate_94(ctx),
126            95 => self.evaluate_95(ctx),
127            96 => self.evaluate_96(ctx),
128            97 => self.evaluate_97(ctx),
129            98 => self.evaluate_98(ctx),
130            99 => self.evaluate_99(ctx),
131            100 => self.evaluate_100(ctx),
132            101 => self.evaluate_101(ctx),
133            107 => self.evaluate_107(ctx),
134            114 => self.evaluate_114(ctx),
135            115 => self.evaluate_115(ctx),
136            117 => self.evaluate_117(ctx),
137            118 => self.evaluate_118(ctx),
138            119 => self.evaluate_119(ctx),
139            120 => self.evaluate_120(ctx),
140            121 => self.evaluate_121(ctx),
141            129 => self.evaluate_129(ctx),
142            130 => self.evaluate_130(ctx),
143            131 => self.evaluate_131(ctx),
144            132 => self.evaluate_132(ctx),
145            133 => self.evaluate_133(ctx),
146            134 => self.evaluate_134(ctx),
147            135 => self.evaluate_135(ctx),
148            136 => self.evaluate_136(ctx),
149            137 => self.evaluate_137(ctx),
150            138 => self.evaluate_138(ctx),
151            139 => self.evaluate_139(ctx),
152            140 => self.evaluate_140(ctx),
153            141 => self.evaluate_141(ctx),
154            142 => self.evaluate_142(ctx),
155            143 => self.evaluate_143(ctx),
156            144 => self.evaluate_144(ctx),
157            145 => self.evaluate_145(ctx),
158            146 => self.evaluate_146(ctx),
159            147 => self.evaluate_147(ctx),
160            148 => self.evaluate_148(ctx),
161            490 => self.evaluate_490(ctx),
162            491 => self.evaluate_491(ctx),
163            492 => self.evaluate_492(ctx),
164            493 => self.evaluate_493(ctx),
165            494 => self.evaluate_494(ctx),
166            495 => self.evaluate_495(ctx),
167            496 => self.evaluate_496(ctx),
168            501 => self.evaluate_501(ctx),
169            502 => self.evaluate_502(ctx),
170            503 => self.evaluate_503(ctx),
171            504 => self.evaluate_504(ctx),
172            505 => self.evaluate_505(ctx),
173            506 => self.evaluate_506(ctx),
174            510 => self.evaluate_510(ctx),
175            512 => self.evaluate_512(ctx),
176            519 => self.evaluate_519(ctx),
177            520 => self.evaluate_520(ctx),
178            521 => self.evaluate_521(ctx),
179            522 => self.evaluate_522(ctx),
180            523 => self.evaluate_523(ctx),
181            524 => self.evaluate_524(ctx),
182            525 => self.evaluate_525(ctx),
183            530 => self.evaluate_530(ctx),
184            531 => self.evaluate_531(ctx),
185            532 => self.evaluate_532(ctx),
186            533 => self.evaluate_533(ctx),
187            534 => self.evaluate_534(ctx),
188            535 => self.evaluate_535(ctx),
189            902 => self.evaluate_902(ctx),
190            903 => self.evaluate_903(ctx),
191            906 => self.evaluate_906(ctx),
192            911 => self.evaluate_911(ctx),
193            931 => self.evaluate_931(ctx),
194            932 => self.evaluate_932(ctx),
195            933 => self.evaluate_933(ctx),
196            934 => self.evaluate_934(ctx),
197            935 => self.evaluate_935(ctx),
198            939 => self.evaluate_939(ctx),
199            940 => self.evaluate_940(ctx),
200            950 => self.evaluate_950(ctx),
201            951 => self.evaluate_951(ctx),
202            960 => self.evaluate_960(ctx),
203            961 => self.evaluate_961(ctx),
204            _ => ConditionResult::Unknown,
205        }
206    }
207
208    fn is_external(&self, condition: u32) -> bool {
209        self.external_conditions.contains(&condition)
210    }
211    fn is_known(&self, condition: u32) -> bool {
212        matches!(
213            condition,
214            1 | 3
215                | 4
216                | 5
217                | 6
218                | 7
219                | 8
220                | 10
221                | 16
222                | 17
223                | 18
224                | 19
225                | 20
226                | 26
227                | 27
228                | 28
229                | 29
230                | 30
231                | 31
232                | 32
233                | 33
234                | 43
235                | 44
236                | 45
237                | 46
238                | 47
239                | 48
240                | 49
241                | 50
242                | 51
243                | 52
244                | 53
245                | 54
246                | 55
247                | 56
248                | 57
249                | 58
250                | 59
251                | 60
252                | 61
253                | 62
254                | 63
255                | 64
256                | 65
257                | 66
258                | 67
259                | 68
260                | 69
261                | 70
262                | 71
263                | 72
264                | 76
265                | 77
266                | 78
267                | 79
268                | 80
269                | 83
270                | 84
271                | 85
272                | 86
273                | 91
274                | 92
275                | 93
276                | 94
277                | 95
278                | 96
279                | 97
280                | 98
281                | 99
282                | 100
283                | 101
284                | 107
285                | 114
286                | 115
287                | 117
288                | 118
289                | 119
290                | 120
291                | 121
292                | 129
293                | 130
294                | 131
295                | 132
296                | 133
297                | 134
298                | 135
299                | 136
300                | 137
301                | 138
302                | 139
303                | 140
304                | 141
305                | 142
306                | 143
307                | 144
308                | 145
309                | 146
310                | 147
311                | 148
312                | 490
313                | 491
314                | 492
315                | 493
316                | 494
317                | 495
318                | 496
319                | 501
320                | 502
321                | 503
322                | 504
323                | 505
324                | 506
325                | 510
326                | 512
327                | 519
328                | 520
329                | 521
330                | 522
331                | 523
332                | 524
333                | 525
334                | 530
335                | 531
336                | 532
337                | 533
338                | 534
339                | 535
340                | 902
341                | 903
342                | 906
343                | 911
344                | 931
345                | 932
346                | 933
347                | 934
348                | 935
349                | 939
350                | 940
351                | 950
352                | 951
353                | 960
354                | 961
355        )
356    }
357}
358
359impl IftstaConditionEvaluatorFV2504 {
360    /// [1] Wenn die Übermittlung nicht codierbarer Informationen nötig ist.
361    /// EXTERNAL: Requires context from outside the message.
362    // REVIEW: Whether transmission of non-codeable information is necessary is a business context decision that cannot be determined from the EDIFACT message content alone. It depends on whether the sending system has information that cannot be expressed via defined code lists. (medium confidence)
363    fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
364        ctx.external.evaluate("non_codeable_info_needed")
365    }
366
367    /// [6] Wenn für das 3-Tupel (MaBiS-ZP, Betrachtungszeitraum, Version der Summenzeitreihe) dem BIKO der Prüfstatus vorliegt, so ist dieser immer zusammen mit dem Datenstatus zu übertragen.
368    /// EXTERNAL: Requires BIKO system state — whether a Prüfstatus is on file for the 3-tuple.
369    fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
370        ctx.external
371            .evaluate("biko_pruefstatus_available_for_tuple")
372    }
373
374    /// [7] Wenn der Datenstatus "Abgerechnete Daten" bzw. "Abgerechnete Daten Korrektur-BKA" nicht vorhanden.
375    /// EXTERNAL: Requires the authoritative IFTSTA Datenstatus codelist to map "Abgerechnete Daten"/"…-BKA" to STS qualifiers.
376    fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
377        ctx.external
378            .evaluate("iftsta_datenstatus_abgerechnet_absent")
379    }
380
381    /// [8] Wenn der Datenstatus einer NZR vom BIKO an NB nicht vorhanden.
382    /// EXTERNAL: Requires routing context (BIKO→NB) plus the NZR-specific Datenstatus codelist.
383    fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
384        ctx.external
385            .evaluate("iftsta_nzr_datenstatus_biko_to_nb_absent")
386    }
387
388    /// [10] Wenn Meldung des BKV/NB/ÜNB nach Frist eingeht.
389    /// EXTERNAL: Requires context from outside the message.
390    fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
391        ctx.external.evaluate("message_after_deadline")
392    }
393
394    /// [17] Wenn Meldung des BKV auf falscher Aggregationsebene eingeht.
395    /// EXTERNAL: Requires context from outside the message.
396    fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
397        ctx.external.evaluate("bkv_wrong_aggregation_level")
398    }
399
400    /// [72] Wenn Gegenvorschlag erstellt werden kann
401    /// EXTERNAL: Requires context from outside the message.
402    fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
403        ctx.external.evaluate("counter_proposal_possible")
404    }
405
406    /// [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
407    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
408        match ctx.resolved_value {
409            Some(val) => is_mesz_utc(val),
410            None => ConditionResult::False,
411        }
412    }
413
414    /// [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
415    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
416        match ctx.resolved_value {
417            Some(val) => is_mez_utc(val),
418            None => ConditionResult::False,
419        }
420    }
421
422    /// [3] Wenn SG7 STS+Z01 nicht vorhanden.
423    fn evaluate_3(&self, ctx: &EvaluationContext) -> ConditionResult {
424        ctx.lacks_qualifier("STS", 0, "Z01")
425    }
426
427    /// [4] Wenn SG7 STS+Z02 nicht vorhanden.
428    fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
429        ctx.lacks_qualifier("STS", 0, "Z02")
430    }
431
432    /// [5] Wenn SG7 STS+Z03 nicht vorhanden.
433    fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
434        ctx.lacks_qualifier("STS", 0, "Z03")
435    }
436
437    /// [16] Wenn MP-ID in SG1 NAD+MR in der Rolle BKV
438    /// EXTERNAL: Requires context from outside the message.
439    fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
440        ctx.external.evaluate("recipient_is_bkv")
441    }
442
443    /// [18] Wenn SG15 STS+Z19 nicht vorhanden.
444    fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
445        ctx.lacks_qualifier("STS", 0, "Z19")
446    }
447
448    /// [19] Wenn SG15 STS+Z24 nicht vorhanden.
449    fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
450        ctx.lacks_qualifier("STS", 0, "Z24")
451    }
452
453    /// [20] Wenn MP-ID in SG1 NAD+MR in der Rolle LF
454    /// EXTERNAL: Requires context from outside the message.
455    fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
456        ctx.external.evaluate("recipient_is_lf")
457    }
458
459    /// [26] Wenn MP-ID in SG1 NAD+MR in der Rolle NB
460    /// EXTERNAL: Requires context from outside the message.
461    fn evaluate_26(&self, ctx: &EvaluationContext) -> ConditionResult {
462        ctx.external.evaluate("recipient_is_nb")
463    }
464
465    /// [27] Nur MP-ID aus Sparte Strom
466    /// EXTERNAL: Requires context from outside the message.
467    fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
468        ctx.external.evaluate("mp_id_is_electricity_sector")
469    }
470
471    /// [28] Nur MP-ID aus Sparte Gas
472    /// EXTERNAL: Requires context from outside the message.
473    fn evaluate_28(&self, ctx: &EvaluationContext) -> ConditionResult {
474        ctx.external.evaluate("mp_id_is_gas_sector")
475    }
476
477    /// [29] Wenn MP-ID in SG1 NAD+MR in der Rolle ÜNB
478    /// EXTERNAL: Requires context from outside the message.
479    fn evaluate_29(&self, ctx: &EvaluationContext) -> ConditionResult {
480        ctx.external.evaluate("recipient_is_uenb")
481    }
482
483    /// [30] Wenn in dieser SG15 STS+Z20+Z32+A07:E_0207 vorhanden.
484    // REVIEW: Checks STS segment for Statuskategorie Z20 (elements[0][0]), Status Z32 (elements[1][0]), Prüfschritt A07 (elements[2][0]), and Codeliste E_0207 (elements[2][1]). Uses message-wide low-level access since no group-scoped helper supports simultaneous multi-field checks on a single segment. The 'A07' code is taken from the AHB shorthand notation at DE9013 position per the segment structure reference. (medium confidence)
485    fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
486        // HAND-EDITED: "in dieser/derselben SG15" — scoped to the enclosing group.
487        let found = ctx
488            .scoped_find_segments_in("SG15", "STS")
489            .into_iter()
490            .any(|s| {
491                s.elements
492                    .first()
493                    .and_then(|e| e.first())
494                    .is_some_and(|v| v == "Z20")
495                    && s.elements
496                        .get(1)
497                        .and_then(|e| e.first())
498                        .is_some_and(|v| v == "Z32")
499                    && s.elements
500                        .get(2)
501                        .and_then(|e| e.first())
502                        .is_some_and(|v| v == "A07")
503                    && s.elements
504                        .get(2)
505                        .and_then(|e| e.get(1))
506                        .is_some_and(|v| v == "E_0207")
507            });
508        ConditionResult::from(found)
509    }
510
511    /// [31] Wenn in dieser SG16 in QTY in DE6411 KWH/K3 vorhanden.
512    fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
513        // HAND-EDITED: "in dieser/derselben SG16" — scoped to the enclosing group.
514        let found = ctx
515            .scoped_find_segments_in("SG16", "QTY")
516            .into_iter()
517            .any(|s| {
518                let unit = s
519                    .elements
520                    .first()
521                    .and_then(|e| e.get(2))
522                    .map(|v| v.as_str());
523                unit == Some("KWH") || unit == Some("K3")
524            });
525        ConditionResult::from(found)
526    }
527
528    /// [32] Wenn in dieser SG16 in QTY in DE6411 KWT/K5 vorhanden.
529    fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
530        // HAND-EDITED: "in dieser/derselben SG16" — scoped to the enclosing group.
531        let found = ctx
532            .scoped_find_segments_in("SG16", "QTY")
533            .into_iter()
534            .any(|s| {
535                let unit = s
536                    .elements
537                    .first()
538                    .and_then(|e| e.get(2))
539                    .map(|v| v.as_str());
540                unit == Some("KWT") || unit == Some("K5")
541            });
542        ConditionResult::from(found)
543    }
544
545    /// [33] Wenn in dieser SG16 DTM+163 vorhanden.
546    fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
547        // HAND-EDITED: "in dieser/derselben SG16" — scoped to the enclosing group.
548        ctx.this_group_has_qualifier("DTM", 0, "163", &["SG16"])
549    }
550
551    /// [43] Wenn STS+Z01+Z07 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
552    fn evaluate_43(&self, ctx: &EvaluationContext) -> ConditionResult {
553        ctx.has_qualified_value("STS", 0, "Z01", 1, 0, &["Z07"])
554    }
555
556    /// [44] Wenn STS+Z01+Z08 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
557    fn evaluate_44(&self, ctx: &EvaluationContext) -> ConditionResult {
558        ctx.has_qualified_value("STS", 0, "Z01", 1, 0, &["Z08"])
559    }
560
561    /// [45] Wenn STS+Z03+Z07 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
562    fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
563        ctx.has_qualified_value("STS", 0, "Z03", 1, 0, &["Z07"])
564    }
565
566    /// [46] Wenn STS+Z03+Z08 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
567    fn evaluate_46(&self, ctx: &EvaluationContext) -> ConditionResult {
568        ctx.has_qualified_value("STS", 0, "Z03", 1, 0, &["Z08"])
569    }
570
571    /// [47] Es sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
572    fn evaluate_47(&self, _ctx: &EvaluationContext) -> ConditionResult {
573        ConditionResult::True
574    }
575
576    /// [48] Es sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
577    fn evaluate_48(&self, _ctx: &EvaluationContext) -> ConditionResult {
578        ConditionResult::True
579    }
580
581    /// [49] Wenn STS+Z25+Z30 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
582    fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
583        ctx.has_qualified_value("STS", 0, "Z25", 1, 0, &["Z30"])
584    }
585
586    /// [50] Wenn STS+Z25+Z31 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
587    fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
588        ctx.has_qualified_value("STS", 0, "Z25", 1, 0, &["Z31"])
589    }
590
591    /// [51] Es sind nur Codes aus dem EBD-Cluster "Abweisung" möglich.
592    fn evaluate_51(&self, _ctx: &EvaluationContext) -> ConditionResult {
593        ConditionResult::True
594    }
595
596    /// [52] Wenn STS+Z27+Z32 vorhanden
597    fn evaluate_52(&self, ctx: &EvaluationContext) -> ConditionResult {
598        ctx.has_qualified_value("STS", 0, "Z27", 1, 0, &["Z32"])
599    }
600
601    /// [53] Wenn STS+Z28+Z32 vorhanden
602    fn evaluate_53(&self, ctx: &EvaluationContext) -> ConditionResult {
603        ctx.has_qualified_value("STS", 0, "Z28", 1, 0, &["Z32"])
604    }
605
606    /// [54] Wenn STS+Z29+Z32 vorhanden
607    fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
608        ctx.has_qualified_value("STS", 0, "Z29", 1, 0, &["Z32"])
609    }
610
611    /// [55] Wenn STS+Z30+Z32 vorhanden
612    fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
613        ctx.has_qualified_value("STS", 0, "Z30", 1, 0, &["Z32"])
614    }
615
616    /// [56] Wenn in dieser SG15 STS das SG15 RFF+ACW nicht identisch mit dem SG15 RFF+ACW der SG15 STS+Z27 ist
617    // REVIEW: Cross-SG15 comparison: find SG15 instances with STS+Z27, collect their RFF+ACW values, then check if any SG15's RFF+ACW differs. Requires low-level navigator access. Medium confidence because the exact group path (SG14→SG15) is inferred from IFTSTA Family B structure. (medium confidence)
618    fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
619        // HAND-EDITED: "nicht identisch mit dem SG15 RFF+ACW der SG15 STS+Z27" — compared with the other instances when the
620        // scope reaches an SG15; the message as before otherwise.
621        if let Some(scope) = ctx.enclosing_scope("SG15") {
622            let acw = |path: &[(&str, usize)]| -> Vec<String> {
623                ctx.find_segments_at(path, "RFF")
624                    .iter()
625                    .filter(|s| s.get_element(0) == "ACW")
626                    .map(|s| s.get_component(0, 1).to_string())
627                    .filter(|v| !v.is_empty())
628                    .collect()
629            };
630            let own = acw(scope);
631            // The STS+Z27 SG15 of the same SG14 (Vorgang).
632            let reference: Vec<String> = ctx
633                .scopes_under("SG14", "SG15")
634                .unwrap_or_default()
635                .iter()
636                .filter(|p| {
637                    ctx.find_segments_at(p, "STS")
638                        .iter()
639                        .any(|s| s.get_element(0) == "Z27")
640                })
641                .flat_map(|p| acw(p))
642                .collect();
643            if own.is_empty() || reference.is_empty() {
644                return ConditionResult::Unknown;
645            }
646            return ConditionResult::from(own.iter().any(|v| !reference.contains(v)));
647        }
648        // True when this SG15's RFF+ACW value differs from the RFF+ACW of the SG15 containing STS+Z27
649        let nav = match ctx.navigator() {
650            Some(n) => n,
651            None => return ConditionResult::Unknown,
652        };
653        let sg15_path: &[&str] = &["SG14", "SG15"];
654        let sg15_count = nav.group_instance_count(sg15_path);
655        // Collect ACW reference values from SG15 instances that have STS with Z27
656        let mut ref_acw: Vec<String> = Vec::new();
657        for i in 0..sg15_count {
658            let sts_segs = nav.find_segments_in_group("STS", sg15_path, i);
659            let has_z27 = sts_segs.iter().any(|s| {
660                s.elements
661                    .first()
662                    .and_then(|e| e.first())
663                    .is_some_and(|v| v == "Z27")
664            });
665            if has_z27 {
666                let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
667                for rff in &rff_segs {
668                    if rff
669                        .elements
670                        .first()
671                        .and_then(|e| e.first())
672                        .is_some_and(|v| v == "ACW")
673                    {
674                        if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
675                            if !val.is_empty() {
676                                ref_acw.push(val.clone());
677                            }
678                        }
679                    }
680                }
681            }
682        }
683        if ref_acw.is_empty() {
684            return ConditionResult::Unknown;
685        }
686        // Check if any SG15 has an ACW value not present in the Z27 SG15 ACW values
687        for i in 0..sg15_count {
688            let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
689            for rff in &rff_segs {
690                if rff
691                    .elements
692                    .first()
693                    .and_then(|e| e.first())
694                    .is_some_and(|v| v == "ACW")
695                {
696                    if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
697                        if !val.is_empty() && !ref_acw.contains(val) {
698                            return ConditionResult::True;
699                        }
700                    }
701                }
702            }
703        }
704        ConditionResult::False
705    }
706
707    /// [57] Wenn in dieser SG15 STS das SG15 RFF+ACW nicht identisch mit dem SG15 RFF+ACW der SG15 STS+Z28 ist
708    // REVIEW: Same cross-SG15 comparison pattern as condition 56, but referencing STS+Z28 (Status des Fahrplananteils) instead of Z27. (medium confidence)
709    fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
710        // HAND-EDITED: "nicht identisch mit dem SG15 RFF+ACW der SG15 STS+Z28" — compared with the other instances when the
711        // scope reaches an SG15; the message as before otherwise.
712        if let Some(scope) = ctx.enclosing_scope("SG15") {
713            let acw = |path: &[(&str, usize)]| -> Vec<String> {
714                ctx.find_segments_at(path, "RFF")
715                    .iter()
716                    .filter(|s| s.get_element(0) == "ACW")
717                    .map(|s| s.get_component(0, 1).to_string())
718                    .filter(|v| !v.is_empty())
719                    .collect()
720            };
721            let own = acw(scope);
722            // The STS+Z28 SG15 of the same SG14 (Vorgang).
723            let reference: Vec<String> = ctx
724                .scopes_under("SG14", "SG15")
725                .unwrap_or_default()
726                .iter()
727                .filter(|p| {
728                    ctx.find_segments_at(p, "STS")
729                        .iter()
730                        .any(|s| s.get_element(0) == "Z28")
731                })
732                .flat_map(|p| acw(p))
733                .collect();
734            if own.is_empty() || reference.is_empty() {
735                return ConditionResult::Unknown;
736            }
737            return ConditionResult::from(own.iter().any(|v| !reference.contains(v)));
738        }
739        // True when this SG15's RFF+ACW value differs from the RFF+ACW of the SG15 containing STS+Z28
740        let nav = match ctx.navigator() {
741            Some(n) => n,
742            None => return ConditionResult::Unknown,
743        };
744        let sg15_path: &[&str] = &["SG14", "SG15"];
745        let sg15_count = nav.group_instance_count(sg15_path);
746        let mut ref_acw: Vec<String> = Vec::new();
747        for i in 0..sg15_count {
748            let sts_segs = nav.find_segments_in_group("STS", sg15_path, i);
749            let has_z28 = sts_segs.iter().any(|s| {
750                s.elements
751                    .first()
752                    .and_then(|e| e.first())
753                    .is_some_and(|v| v == "Z28")
754            });
755            if has_z28 {
756                let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
757                for rff in &rff_segs {
758                    if rff
759                        .elements
760                        .first()
761                        .and_then(|e| e.first())
762                        .is_some_and(|v| v == "ACW")
763                    {
764                        if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
765                            if !val.is_empty() {
766                                ref_acw.push(val.clone());
767                            }
768                        }
769                    }
770                }
771            }
772        }
773        if ref_acw.is_empty() {
774            return ConditionResult::Unknown;
775        }
776        for i in 0..sg15_count {
777            let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
778            for rff in &rff_segs {
779                if rff
780                    .elements
781                    .first()
782                    .and_then(|e| e.first())
783                    .is_some_and(|v| v == "ACW")
784                {
785                    if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
786                        if !val.is_empty() && !ref_acw.contains(val) {
787                            return ConditionResult::True;
788                        }
789                    }
790                }
791            }
792        }
793        ConditionResult::False
794    }
795
796    /// [58] Wenn in dieser SG15 STS das SG15 RFF+ACW nicht identisch mit dem SG15 RFF+ACW der SG15 STS+Z29 ist
797    // REVIEW: Same cross-SG15 comparison pattern as condition 56, but referencing STS+Z29 (Status des Gegenvorschlags der Ausfallarbeit). (medium confidence)
798    fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
799        // HAND-EDITED: "nicht identisch mit dem SG15 RFF+ACW der SG15 STS+Z29" — compared with the other instances when the
800        // scope reaches an SG15; the message as before otherwise.
801        if let Some(scope) = ctx.enclosing_scope("SG15") {
802            let acw = |path: &[(&str, usize)]| -> Vec<String> {
803                ctx.find_segments_at(path, "RFF")
804                    .iter()
805                    .filter(|s| s.get_element(0) == "ACW")
806                    .map(|s| s.get_component(0, 1).to_string())
807                    .filter(|v| !v.is_empty())
808                    .collect()
809            };
810            let own = acw(scope);
811            // The STS+Z29 SG15 of the same SG14 (Vorgang).
812            let reference: Vec<String> = ctx
813                .scopes_under("SG14", "SG15")
814                .unwrap_or_default()
815                .iter()
816                .filter(|p| {
817                    ctx.find_segments_at(p, "STS")
818                        .iter()
819                        .any(|s| s.get_element(0) == "Z29")
820                })
821                .flat_map(|p| acw(p))
822                .collect();
823            if own.is_empty() || reference.is_empty() {
824                return ConditionResult::Unknown;
825            }
826            return ConditionResult::from(own.iter().any(|v| !reference.contains(v)));
827        }
828        // True when this SG15's RFF+ACW value differs from the RFF+ACW of the SG15 containing STS+Z29
829        let nav = match ctx.navigator() {
830            Some(n) => n,
831            None => return ConditionResult::Unknown,
832        };
833        let sg15_path: &[&str] = &["SG14", "SG15"];
834        let sg15_count = nav.group_instance_count(sg15_path);
835        let mut ref_acw: Vec<String> = Vec::new();
836        for i in 0..sg15_count {
837            let sts_segs = nav.find_segments_in_group("STS", sg15_path, i);
838            let has_z29 = sts_segs.iter().any(|s| {
839                s.elements
840                    .first()
841                    .and_then(|e| e.first())
842                    .is_some_and(|v| v == "Z29")
843            });
844            if has_z29 {
845                let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
846                for rff in &rff_segs {
847                    if rff
848                        .elements
849                        .first()
850                        .and_then(|e| e.first())
851                        .is_some_and(|v| v == "ACW")
852                    {
853                        if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
854                            if !val.is_empty() {
855                                ref_acw.push(val.clone());
856                            }
857                        }
858                    }
859                }
860            }
861        }
862        if ref_acw.is_empty() {
863            return ConditionResult::Unknown;
864        }
865        for i in 0..sg15_count {
866            let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
867            for rff in &rff_segs {
868                if rff
869                    .elements
870                    .first()
871                    .and_then(|e| e.first())
872                    .is_some_and(|v| v == "ACW")
873                {
874                    if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
875                        if !val.is_empty() && !ref_acw.contains(val) {
876                            return ConditionResult::True;
877                        }
878                    }
879                }
880            }
881        }
882        ConditionResult::False
883    }
884
885    /// [59] Wenn in dieser SG15 STS das SG15 RFF+ACW nicht identisch mit dem SG15 RFF+ACW der SG15 STS+Z30 ist
886    // REVIEW: Same cross-SG15 comparison pattern as condition 56, but referencing STS+Z30 (Status des Gegenvorschlags des Fahrplananteils). (medium confidence)
887    fn evaluate_59(&self, ctx: &EvaluationContext) -> ConditionResult {
888        // HAND-EDITED: "nicht identisch mit dem SG15 RFF+ACW der SG15 STS+Z30" — compared with the other instances when the
889        // scope reaches an SG15; the message as before otherwise.
890        if let Some(scope) = ctx.enclosing_scope("SG15") {
891            let acw = |path: &[(&str, usize)]| -> Vec<String> {
892                ctx.find_segments_at(path, "RFF")
893                    .iter()
894                    .filter(|s| s.get_element(0) == "ACW")
895                    .map(|s| s.get_component(0, 1).to_string())
896                    .filter(|v| !v.is_empty())
897                    .collect()
898            };
899            let own = acw(scope);
900            // The STS+Z30 SG15 of the same SG14 (Vorgang).
901            let reference: Vec<String> = ctx
902                .scopes_under("SG14", "SG15")
903                .unwrap_or_default()
904                .iter()
905                .filter(|p| {
906                    ctx.find_segments_at(p, "STS")
907                        .iter()
908                        .any(|s| s.get_element(0) == "Z30")
909                })
910                .flat_map(|p| acw(p))
911                .collect();
912            if own.is_empty() || reference.is_empty() {
913                return ConditionResult::Unknown;
914            }
915            return ConditionResult::from(own.iter().any(|v| !reference.contains(v)));
916        }
917        // True when this SG15's RFF+ACW value differs from the RFF+ACW of the SG15 containing STS+Z30
918        let nav = match ctx.navigator() {
919            Some(n) => n,
920            None => return ConditionResult::Unknown,
921        };
922        let sg15_path: &[&str] = &["SG14", "SG15"];
923        let sg15_count = nav.group_instance_count(sg15_path);
924        let mut ref_acw: Vec<String> = Vec::new();
925        for i in 0..sg15_count {
926            let sts_segs = nav.find_segments_in_group("STS", sg15_path, i);
927            let has_z30 = sts_segs.iter().any(|s| {
928                s.elements
929                    .first()
930                    .and_then(|e| e.first())
931                    .is_some_and(|v| v == "Z30")
932            });
933            if has_z30 {
934                let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
935                for rff in &rff_segs {
936                    if rff
937                        .elements
938                        .first()
939                        .and_then(|e| e.first())
940                        .is_some_and(|v| v == "ACW")
941                    {
942                        if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
943                            if !val.is_empty() {
944                                ref_acw.push(val.clone());
945                            }
946                        }
947                    }
948                }
949            }
950        }
951        if ref_acw.is_empty() {
952            return ConditionResult::Unknown;
953        }
954        for i in 0..sg15_count {
955            let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
956            for rff in &rff_segs {
957                if rff
958                    .elements
959                    .first()
960                    .and_then(|e| e.first())
961                    .is_some_and(|v| v == "ACW")
962                {
963                    if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
964                        if !val.is_empty() && !ref_acw.contains(val) {
965                            return ConditionResult::True;
966                        }
967                    }
968                }
969            }
970        }
971        ConditionResult::False
972    }
973
974    /// [60] Wenn zusätzlich zum Fahrplananteil auch die Ausfallarbeit zu identischem Wert aus RFF+ACW nicht o.k. ist
975    // REVIEW: Checks whether any SG15 with STS+Z27+Z32 (Ausfallarbeit rejected) shares the same RFF+ACW reference value as an SG15 with STS+Z28+Z32 (Fahrplananteil rejected), within the same SG14 transaction group. Assumes SG15 is a child group of SG14 (consistent with IFTSTA Family B tx_group=SG14). (medium confidence)
976    fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
977        {
978            let nav = match ctx.navigator() {
979                Some(n) => n,
980                None => return ConditionResult::Unknown,
981            };
982            let sg14_count = nav.group_instance_count(&["SG14"]);
983            for i in 0..sg14_count {
984                let sg15_count = nav.child_group_instance_count(&["SG14"], i, "SG15");
985                let mut z27_z32_acw: Vec<String> = Vec::new();
986                let mut z28_z32_acw: Vec<String> = Vec::new();
987                for j in 0..sg15_count {
988                    let stses = nav.find_segments_in_child_group("STS", &["SG14"], i, "SG15", j);
989                    let rffs = nav.find_segments_in_child_group("RFF", &["SG14"], i, "SG15", j);
990                    let acw_val = rffs
991                        .iter()
992                        .find(|s| {
993                            s.elements
994                                .first()
995                                .and_then(|e| e.first())
996                                .is_some_and(|v| v == "ACW")
997                        })
998                        .and_then(|s| s.elements.first().and_then(|e| e.get(1)).cloned());
999                    let Some(acw) = acw_val else { continue };
1000                    for sts in &stses {
1001                        let cat = sts
1002                            .elements
1003                            .first()
1004                            .and_then(|e| e.first())
1005                            .map(|s| s.as_str());
1006                        let status = sts
1007                            .elements
1008                            .get(1)
1009                            .and_then(|e| e.first())
1010                            .map(|s| s.as_str());
1011                        match (cat, status) {
1012                            (Some("Z27"), Some("Z32")) => z27_z32_acw.push(acw.clone()),
1013                            (Some("Z28"), Some("Z32")) => z28_z32_acw.push(acw.clone()),
1014                            _ => {}
1015                        }
1016                    }
1017                }
1018                if z27_z32_acw.iter().any(|v| z28_z32_acw.contains(v)) {
1019                    return ConditionResult::True;
1020                }
1021            }
1022            ConditionResult::False
1023        }
1024    }
1025
1026    /// [61] Wenn zusätzlich zur Ausfallarbeit auch der Fahrplananteil zu identischem Wert aus RFF+ACW nicht o.k. ist
1027    // REVIEW: Mirror of condition 60: checks whether any SG15 with STS+Z28+Z32 (Fahrplananteil rejected) shares the same RFF+ACW as an SG15 with STS+Z27+Z32 (Ausfallarbeit rejected). The two conditions are semantically equivalent but expressed from different perspectives (the AHB uses them on different fields). Same SG14→SG15 navigator approach. (medium confidence)
1028    fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
1029        {
1030            let nav = match ctx.navigator() {
1031                Some(n) => n,
1032                None => return ConditionResult::Unknown,
1033            };
1034            let sg14_count = nav.group_instance_count(&["SG14"]);
1035            for i in 0..sg14_count {
1036                let sg15_count = nav.child_group_instance_count(&["SG14"], i, "SG15");
1037                let mut z27_z32_acw: Vec<String> = Vec::new();
1038                let mut z28_z32_acw: Vec<String> = Vec::new();
1039                for j in 0..sg15_count {
1040                    let stses = nav.find_segments_in_child_group("STS", &["SG14"], i, "SG15", j);
1041                    let rffs = nav.find_segments_in_child_group("RFF", &["SG14"], i, "SG15", j);
1042                    let acw_val = rffs
1043                        .iter()
1044                        .find(|s| {
1045                            s.elements
1046                                .first()
1047                                .and_then(|e| e.first())
1048                                .is_some_and(|v| v == "ACW")
1049                        })
1050                        .and_then(|s| s.elements.first().and_then(|e| e.get(1)).cloned());
1051                    let Some(acw) = acw_val else { continue };
1052                    for sts in &stses {
1053                        let cat = sts
1054                            .elements
1055                            .first()
1056                            .and_then(|e| e.first())
1057                            .map(|s| s.as_str());
1058                        let status = sts
1059                            .elements
1060                            .get(1)
1061                            .and_then(|e| e.first())
1062                            .map(|s| s.as_str());
1063                        match (cat, status) {
1064                            (Some("Z27"), Some("Z32")) => z27_z32_acw.push(acw.clone()),
1065                            (Some("Z28"), Some("Z32")) => z28_z32_acw.push(acw.clone()),
1066                            _ => {}
1067                        }
1068                    }
1069                }
1070                if z28_z32_acw.iter().any(|v| z27_z32_acw.contains(v)) {
1071                    return ConditionResult::True;
1072                }
1073            }
1074            ConditionResult::False
1075        }
1076    }
1077
1078    /// [62] Wenn STS+Z27+Z30 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
1079    fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
1080        ctx.has_qualified_value("STS", 0, "Z27", 1, 0, &["Z30"])
1081    }
1082
1083    /// [63] Wenn STS+Z27+Z32 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
1084    fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
1085        ctx.has_qualified_value("STS", 0, "Z27", 1, 0, &["Z32"])
1086    }
1087
1088    /// [64] Wenn STS+Z28+Z30 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
1089    fn evaluate_64(&self, ctx: &EvaluationContext) -> ConditionResult {
1090        ctx.has_qualified_value("STS", 0, "Z28", 1, 0, &["Z30"])
1091    }
1092
1093    /// [65] Wenn STS+Z28+Z32 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
1094    fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
1095        ctx.has_qualified_value("STS", 0, "Z28", 1, 0, &["Z32"])
1096    }
1097
1098    /// [66] Wenn STS+Z29+Z30 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
1099    fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
1100        ctx.has_qualified_value("STS", 0, "Z29", 1, 0, &["Z30"])
1101    }
1102
1103    /// [67] Wenn STS+Z29+Z32 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
1104    fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
1105        ctx.has_qualified_value("STS", 0, "Z29", 1, 0, &["Z32"])
1106    }
1107
1108    /// [68] Wenn STS+Z30+Z30 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
1109    fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
1110        ctx.has_qualified_value("STS", 0, "Z30", 1, 0, &["Z30"])
1111    }
1112
1113    /// [69] Wenn STS+Z30+Z32 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
1114    fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
1115        ctx.has_qualified_value("STS", 0, "Z30", 1, 0, &["Z32"])
1116    }
1117
1118    /// [70] Wenn zusätzlich zum Gegenvorschlag des Fahrplananteils auch der Gegenvorschlag der Ausfallarbeit zu identischem Wert aus RFF+ACW nicht o.k. ist
1119    // REVIEW: Condition 70 means: additionally to Fahrplananteil counter-proposal (STS Z30 Z32) being rejected, the Ausfallarbeit counter-proposal (STS Z29 Z32) is also rejected. The 'identischem Wert aus RFF+ACW' (same RFF+ACW reference) cross-group correlation is simplified — checks that both rejection STS types are present in the message. Full cross-SG14 value matching would require navigator group enumeration not fully supported by current helpers. (medium confidence)
1120    fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
1121        match ctx.has_qualified_value("STS", 0, "Z30", 1, 0, &["Z32"]) {
1122            ConditionResult::True => ctx.has_qualified_value("STS", 0, "Z29", 1, 0, &["Z32"]),
1123            ConditionResult::False => ConditionResult::False,
1124            ConditionResult::Unknown => ConditionResult::Unknown,
1125        }
1126    }
1127
1128    /// [71] Wenn zusätzlich zum Gegenvorschlag der Ausfallarbeit auch der Gegenvorschlag des Fahrplananteils zu identischem Wert aus RFF+ACW nicht o.k. ist
1129    // REVIEW: Symmetric to condition 70: additionally to Ausfallarbeit counter-proposal (STS Z29 Z32) being rejected, the Fahrplananteil counter-proposal (STS Z30 Z32) is also rejected. Same simplification of the RFF+ACW cross-group correlation applies. (medium confidence)
1130    fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
1131        match ctx.has_qualified_value("STS", 0, "Z29", 1, 0, &["Z32"]) {
1132            ConditionResult::True => ctx.has_qualified_value("STS", 0, "Z30", 1, 0, &["Z32"]),
1133            ConditionResult::False => ConditionResult::False,
1134            ConditionResult::Unknown => ConditionResult::Unknown,
1135        }
1136    }
1137
1138    /// [76] Wenn MP-ID in SG1 NAD+MR in der Rolle ESA
1139    /// EXTERNAL: Requires context from outside the message.
1140    fn evaluate_76(&self, ctx: &EvaluationContext) -> ConditionResult {
1141        ctx.external.evaluate("recipient_is_esa")
1142    }
1143
1144    /// [77] Wenn STS+Z37+Z14 in dieser SG14 vorhanden
1145    fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
1146        // HAND-EDITED: "in dieser/derselben SG14" — scoped to the enclosing group.
1147        ctx.this_group_has_qualified_value("STS", 0, "Z37", 1, 0, &["Z14"], &["SG14"])
1148    }
1149
1150    /// [78] Wenn STS+Z38 in dieser SG14 nicht vorhanden
1151    // REVIEW: STS+Z38 (Entsperren) is absent in the SG14 that contains STS+Z37 (Sperren). Uses any_group_has_qualifier_without to find an SG14 instance where STS+Z37 is present but STS+Z38 is not, covering the intra-SG14 scoping requirement. (medium confidence)
1152    fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
1153        // HAND-EDITED: "STS+Z38 in dieser SG14 nicht vorhanden" — the enclosing SG14 only, when the scope
1154        // reaches one; the message as before otherwise.
1155        if let Some(scope) = ctx.enclosing_scope("SG14") {
1156            return ConditionResult::from(
1157                !ctx.find_segments_at(scope, "STS")
1158                    .iter()
1159                    .any(|s| s.get_element(0) == "Z38"),
1160            );
1161        }
1162        ctx.any_group_has_qualifier_without("STS", 0, "Z37", "STS", 0, "Z38", &["SG14"])
1163    }
1164
1165    /// [79] Wenn STS+Z37 in dieser SG14 nicht vorhanden
1166    // REVIEW: STS+Z37 (Sperren) is absent in the SG14 that contains STS+Z38 (Entsperren). Symmetric to condition 78 — finds SG14 where STS+Z38 is present but STS+Z37 is not. (medium confidence)
1167    fn evaluate_79(&self, ctx: &EvaluationContext) -> ConditionResult {
1168        // HAND-EDITED: "STS+Z37 in dieser SG14 nicht vorhanden" — the enclosing SG14 only, when the scope
1169        // reaches one; the message as before otherwise.
1170        if let Some(scope) = ctx.enclosing_scope("SG14") {
1171            return ConditionResult::from(
1172                !ctx.find_segments_at(scope, "STS")
1173                    .iter()
1174                    .any(|s| s.get_element(0) == "Z37"),
1175            );
1176        }
1177        ctx.any_group_has_qualifier_without("STS", 0, "Z38", "STS", 0, "Z37", &["SG14"])
1178    }
1179
1180    /// [80] Wenn STS+Z38+Z14 in dieser SG14 vorhanden
1181    fn evaluate_80(&self, ctx: &EvaluationContext) -> ConditionResult {
1182        // HAND-EDITED: "in dieser/derselben SG14" — scoped to the enclosing group.
1183        ctx.this_group_has_qualified_value("STS", 0, "Z38", 1, 0, &["Z14"], &["SG14"])
1184    }
1185
1186    /// [83] Wenn STS+Z37+Z13+A04/A05/A06:E_0472 in dieser SG14 vorhanden
1187    // REVIEW: STS+Z37 (Sperren) with Status Z13 and Statusanlass (elements[2][0], DE9013) in {A04, A05, A06}. The Codeliste code E_0472 in elements[2][1] is not checked as the primary discriminator is the DE9013 Prüfschritt code. Message-wide search is used as group-scoped SG15 traversal is not available via documented helpers. (medium confidence)
1188    fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
1189        // HAND-EDITED: "in dieser/derselben SG14" — scoped to the enclosing group.
1190        let sts_segs = ctx.scoped_find_segments_in("SG14", "STS");
1191        let found = sts_segs.iter().any(|s| {
1192            s.elements
1193                .first()
1194                .and_then(|e| e.first())
1195                .is_some_and(|v| v == "Z37")
1196                && s.elements
1197                    .get(1)
1198                    .and_then(|e| e.first())
1199                    .is_some_and(|v| v == "Z13")
1200                && s.elements
1201                    .get(2)
1202                    .and_then(|e| e.first())
1203                    .is_some_and(|v| matches!(v.as_str(), "A04" | "A05" | "A06"))
1204        });
1205        ConditionResult::from(found)
1206    }
1207
1208    /// [84] Wenn STS+Z38+Z13+A02:E_0499 in dieser SG14 vorhanden
1209    // REVIEW: STS+Z38 (Entsperren) with Status Z13 and Statusanlass DE9013=A02 (Codeliste E_0499). Checks elements[0][0]=Z38, elements[1][0]=Z13, elements[2][0]=A02. Message-wide search used as SG14/SG15 child group traversal is not in documented helpers. (medium confidence)
1210    fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
1211        // HAND-EDITED: "in dieser/derselben SG14" — scoped to the enclosing group.
1212        let sts_segs = ctx.scoped_find_segments_in("SG14", "STS");
1213        let found = sts_segs.iter().any(|s| {
1214            s.elements
1215                .first()
1216                .and_then(|e| e.first())
1217                .is_some_and(|v| v == "Z38")
1218                && s.elements
1219                    .get(1)
1220                    .and_then(|e| e.first())
1221                    .is_some_and(|v| v == "Z13")
1222                && s.elements
1223                    .get(2)
1224                    .and_then(|e| e.first())
1225                    .is_some_and(|v| v == "A02")
1226        });
1227        ConditionResult::from(found)
1228    }
1229
1230    /// [85] Wenn STS+Z37+Z32+A01:E_0501 in dieser SG14 vorhanden
1231    // REVIEW: STS+Z37 (Sperren) with Status Z32 (Ablehnung) and Statusanlass DE9013=A01 (Codeliste E_0501). Checks elements[0][0]=Z37, elements[1][0]=Z32, elements[2][0]=A01. Message-wide search used as SG14/SG15 child group traversal is not in documented helpers. (medium confidence)
1232    fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
1233        // HAND-EDITED: "in dieser/derselben SG14" — scoped to the enclosing group.
1234        let sts_segs = ctx.scoped_find_segments_in("SG14", "STS");
1235        let found = sts_segs.iter().any(|s| {
1236            s.elements
1237                .first()
1238                .and_then(|e| e.first())
1239                .is_some_and(|v| v == "Z37")
1240                && s.elements
1241                    .get(1)
1242                    .and_then(|e| e.first())
1243                    .is_some_and(|v| v == "Z32")
1244                && s.elements
1245                    .get(2)
1246                    .and_then(|e| e.first())
1247                    .is_some_and(|v| v == "A01")
1248        });
1249        ConditionResult::from(found)
1250    }
1251
1252    /// [86] Wenn MP-ID in SG1 NAD+MS in der Rolle LF
1253    /// EXTERNAL: Requires context from outside the message.
1254    fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
1255        ctx.external.evaluate("sender_is_lf")
1256    }
1257
1258    /// [91] Wenn in diesem STS DE1131 = E_0472
1259    fn evaluate_91(&self, ctx: &EvaluationContext) -> ConditionResult {
1260        let segments = ctx.find_segments("STS");
1261        ConditionResult::from(segments.iter().any(|s| {
1262            s.elements
1263                .get(2)
1264                .and_then(|e| e.get(1))
1265                .map(|v| v == "E_0472")
1266                .unwrap_or(false)
1267        }))
1268    }
1269
1270    /// [92] Wenn in diesem STS DE1131 = E_0501
1271    fn evaluate_92(&self, ctx: &EvaluationContext) -> ConditionResult {
1272        let segments = ctx.find_segments("STS");
1273        ConditionResult::from(segments.iter().any(|s| {
1274            s.elements
1275                .get(2)
1276                .and_then(|e| e.get(1))
1277                .map(|v| v == "E_0501")
1278                .unwrap_or(false)
1279        }))
1280    }
1281
1282    /// [93] Wenn STS+Z37+Z13 vorhanden, dann sind nur Codes aus dem EBD-Cluster "gescheitert" möglich.
1283    fn evaluate_93(&self, ctx: &EvaluationContext) -> ConditionResult {
1284        ctx.has_qualified_value("STS", 0, "Z37", 1, 0, &["Z13"])
1285    }
1286
1287    /// [94] Wenn STS+Z37+Z14 vorhanden, dann sind nur Codes aus dem EBD-Cluster "erfolgreich" möglich.
1288    fn evaluate_94(&self, ctx: &EvaluationContext) -> ConditionResult {
1289        ctx.has_qualified_value("STS", 0, "Z37", 1, 0, &["Z14"])
1290    }
1291
1292    /// [95] Wenn in diesem STS DE1131 = E_0499
1293    fn evaluate_95(&self, ctx: &EvaluationContext) -> ConditionResult {
1294        let segments = ctx.find_segments("STS");
1295        ConditionResult::from(segments.iter().any(|s| {
1296            s.elements
1297                .get(2)
1298                .and_then(|e| e.get(1))
1299                .map(|v| v == "E_0499")
1300                .unwrap_or(false)
1301        }))
1302    }
1303
1304    /// [96] Wenn in diesem STS DE1131 = E_0487
1305    fn evaluate_96(&self, ctx: &EvaluationContext) -> ConditionResult {
1306        let segments = ctx.find_segments("STS");
1307        ConditionResult::from(segments.iter().any(|s| {
1308            s.elements
1309                .get(2)
1310                .and_then(|e| e.get(1))
1311                .map(|v| v == "E_0487")
1312                .unwrap_or(false)
1313        }))
1314    }
1315
1316    /// [97] Wenn STS+Z38+Z13 vorhanden, dann sind nur Codes aus dem EBD-Cluster "gescheitert" möglich.
1317    fn evaluate_97(&self, ctx: &EvaluationContext) -> ConditionResult {
1318        ctx.has_qualified_value("STS", 0, "Z38", 1, 0, &["Z13"])
1319    }
1320
1321    /// [98] Wenn STS+Z38+Z14 vorhanden, dann sind nur Codes aus dem EBD-Cluster "erfolgreich" möglich.
1322    fn evaluate_98(&self, ctx: &EvaluationContext) -> ConditionResult {
1323        ctx.has_qualified_value("STS", 0, "Z38", 1, 0, &["Z14"])
1324    }
1325
1326    /// [99] Wenn in diesem STS DE1131 = E_0487, dann ist nur der Code A01 möglich.
1327    fn evaluate_99(&self, ctx: &EvaluationContext) -> ConditionResult {
1328        let segments = ctx.find_segments("STS");
1329        ConditionResult::from(segments.iter().any(|s| {
1330            s.elements
1331                .get(2)
1332                .and_then(|e| e.get(1))
1333                .map(|v| v == "E_0487")
1334                .unwrap_or(false)
1335        }))
1336    }
1337
1338    /// [100] Wenn STS+Z43+Z47 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Änderung der Daten" möglich.
1339    fn evaluate_100(&self, ctx: &EvaluationContext) -> ConditionResult {
1340        ctx.has_qualified_value("STS", 0, "Z43", 1, 0, &["Z47"])
1341    }
1342
1343    /// [101] Wenn STS+Z43+Z48 vorhanden, dann sind nur Codes aus dem EBD-Cluster "keine Änderung der Daten" möglich.
1344    fn evaluate_101(&self, ctx: &EvaluationContext) -> ConditionResult {
1345        ctx.has_qualified_value("STS", 0, "Z43", 1, 0, &["Z48"])
1346    }
1347
1348    /// [107] Wenn STS+Z37+Z32 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
1349    fn evaluate_107(&self, ctx: &EvaluationContext) -> ConditionResult {
1350        ctx.has_qualified_value("STS", 0, "Z37", 1, 0, &["Z32"])
1351    }
1352
1353    /// [114] Wenn MP-ID in SG1 NAD+MS in der Rolle MSB
1354    /// EXTERNAL: Requires context from outside the message.
1355    fn evaluate_114(&self, ctx: &EvaluationContext) -> ConditionResult {
1356        ctx.external.evaluate("sender_is_msb")
1357    }
1358
1359    /// [115] Wenn MP-ID in SG1 NAD+MS in der Rolle NB
1360    /// EXTERNAL: Requires context from outside the message.
1361    fn evaluate_115(&self, ctx: &EvaluationContext) -> ConditionResult {
1362        ctx.external.evaluate("sender_is_nb")
1363    }
1364
1365    /// [117] Wenn MP-ID in SG1 NAD+MR in der Rolle MSB
1366    /// EXTERNAL: Requires context from outside the message.
1367    fn evaluate_117(&self, ctx: &EvaluationContext) -> ConditionResult {
1368        ctx.external.evaluate("recipient_is_msb")
1369    }
1370
1371    /// [118] Wenn in diesem STS DE1131 = E_0526
1372    fn evaluate_118(&self, ctx: &EvaluationContext) -> ConditionResult {
1373        ctx.has_qualified_value("STS", 0, "Z21", 2, 1, &["E_0526"])
1374    }
1375
1376    /// [119] Wenn in diesem STS DE1131 = E_0528
1377    fn evaluate_119(&self, ctx: &EvaluationContext) -> ConditionResult {
1378        ctx.has_qualified_value("STS", 0, "Z21", 2, 1, &["E_0528"])
1379    }
1380
1381    /// [120] Wenn in diesem STS DE1131 = E_0529
1382    fn evaluate_120(&self, ctx: &EvaluationContext) -> ConditionResult {
1383        ctx.has_qualified_value("STS", 0, "Z21", 2, 1, &["E_0529"])
1384    }
1385
1386    /// [121] Wenn in diesem STS DE1131 = E_0536
1387    fn evaluate_121(&self, ctx: &EvaluationContext) -> ConditionResult {
1388        ctx.has_qualified_value("STS", 0, "Z21", 2, 1, &["E_0536"])
1389    }
1390
1391    /// [129] Wenn STS+Z20+Z32+A99:E_0524 in dieser SG14 vorhanden
1392    // REVIEW: Check STS with Statuskategorie Z20, Status Z32, Prüfschritt A99 (elements[2][0]), and DE1131 E_0524 (elements[2][1]). Group-scoped to SG14 but falls back to message-wide search. (medium confidence)
1393    fn evaluate_129(&self, ctx: &EvaluationContext) -> ConditionResult {
1394        // HAND-EDITED: "in dieser/derselben SG14" — scoped to the enclosing group.
1395        let segments = ctx.scoped_find_segments_in("SG14", "STS");
1396        ConditionResult::from(segments.iter().any(|s| {
1397            s.elements
1398                .first()
1399                .and_then(|e| e.first())
1400                .map(|v| v == "Z20")
1401                .unwrap_or(false)
1402                && s.elements
1403                    .get(1)
1404                    .and_then(|e| e.first())
1405                    .map(|v| v == "Z32")
1406                    .unwrap_or(false)
1407                && s.elements
1408                    .get(2)
1409                    .and_then(|e| e.first())
1410                    .map(|v| v == "A99")
1411                    .unwrap_or(false)
1412                && s.elements
1413                    .get(2)
1414                    .and_then(|e| e.get(1))
1415                    .map(|v| v == "E_0524")
1416                    .unwrap_or(false)
1417        }))
1418    }
1419
1420    /// [130] Wenn STS+Z20+Z32+A99:E_0531 in dieser SG14 vorhanden
1421    // REVIEW: Check STS with Statuskategorie Z20, Status Z32, Prüfschritt A99 (elements[2][0]), and DE1131 E_0531 (elements[2][1]). Group-scoped to SG14 but falls back to message-wide search. (medium confidence)
1422    fn evaluate_130(&self, ctx: &EvaluationContext) -> ConditionResult {
1423        // HAND-EDITED: "in dieser/derselben SG14" — scoped to the enclosing group.
1424        let segments = ctx.scoped_find_segments_in("SG14", "STS");
1425        ConditionResult::from(segments.iter().any(|s| {
1426            s.elements
1427                .first()
1428                .and_then(|e| e.first())
1429                .map(|v| v == "Z20")
1430                .unwrap_or(false)
1431                && s.elements
1432                    .get(1)
1433                    .and_then(|e| e.first())
1434                    .map(|v| v == "Z32")
1435                    .unwrap_or(false)
1436                && s.elements
1437                    .get(2)
1438                    .and_then(|e| e.first())
1439                    .map(|v| v == "A99")
1440                    .unwrap_or(false)
1441                && s.elements
1442                    .get(2)
1443                    .and_then(|e| e.get(1))
1444                    .map(|v| v == "E_0531")
1445                    .unwrap_or(false)
1446        }))
1447    }
1448
1449    /// [131] Wenn STS+Z37+Z13+A04/A05/A06:E_1003 in dieser SG14 vorhanden
1450    // REVIEW: Check STS with Statuskategorie Z37 (Auftragstatus Sperren), Status Z13, DE9013 in {A04,A05,A06} (elements[2][0]), and DE1131 E_1003 (elements[2][1]). Group-scoped to SG14 but falls back to message-wide. (medium confidence)
1451    fn evaluate_131(&self, ctx: &EvaluationContext) -> ConditionResult {
1452        // HAND-EDITED: "in dieser/derselben SG14" — scoped to the enclosing group.
1453        let segments = ctx.scoped_find_segments_in("SG14", "STS");
1454        ConditionResult::from(segments.iter().any(|s| {
1455            s.elements
1456                .first()
1457                .and_then(|e| e.first())
1458                .map(|v| v == "Z37")
1459                .unwrap_or(false)
1460                && s.elements
1461                    .get(1)
1462                    .and_then(|e| e.first())
1463                    .map(|v| v == "Z13")
1464                    .unwrap_or(false)
1465                && s.elements
1466                    .get(2)
1467                    .and_then(|e| e.first())
1468                    .map(|v| matches!(v.as_str(), "A04" | "A05" | "A06"))
1469                    .unwrap_or(false)
1470                && s.elements
1471                    .get(2)
1472                    .and_then(|e| e.get(1))
1473                    .map(|v| v == "E_1003")
1474                    .unwrap_or(false)
1475        }))
1476    }
1477
1478    /// [132] Wenn STS+Z37+Z32+A01:E_1002 in dieser SG14 vorhanden
1479    // REVIEW: Check STS with Statuskategorie Z37 (Auftragstatus Sperren), Status Z32, Prüfschritt A01 (elements[2][0]), and DE1131 E_1002 (elements[2][1]). Group-scoped to SG14 but falls back to message-wide. (medium confidence)
1480    fn evaluate_132(&self, ctx: &EvaluationContext) -> ConditionResult {
1481        // HAND-EDITED: "in dieser/derselben SG14" — scoped to the enclosing group.
1482        let segments = ctx.scoped_find_segments_in("SG14", "STS");
1483        ConditionResult::from(segments.iter().any(|s| {
1484            s.elements
1485                .first()
1486                .and_then(|e| e.first())
1487                .map(|v| v == "Z37")
1488                .unwrap_or(false)
1489                && s.elements
1490                    .get(1)
1491                    .and_then(|e| e.first())
1492                    .map(|v| v == "Z32")
1493                    .unwrap_or(false)
1494                && s.elements
1495                    .get(2)
1496                    .and_then(|e| e.first())
1497                    .map(|v| v == "A01")
1498                    .unwrap_or(false)
1499                && s.elements
1500                    .get(2)
1501                    .and_then(|e| e.get(1))
1502                    .map(|v| v == "E_1002")
1503                    .unwrap_or(false)
1504        }))
1505    }
1506
1507    /// [133] Wenn in diesem STS DE1131 = E_1003
1508    fn evaluate_133(&self, ctx: &EvaluationContext) -> ConditionResult {
1509        ctx.has_qualified_value("STS", 0, "Z37", 2, 1, &["E_1003"])
1510    }
1511
1512    /// [134] Wenn in diesem STS DE1131 = E_1002
1513    fn evaluate_134(&self, ctx: &EvaluationContext) -> ConditionResult {
1514        ctx.has_qualified_value("STS", 0, "Z37", 2, 1, &["E_1002"])
1515    }
1516
1517    /// [135] Wenn in dieser SG15 in STS+Z21 DE9013 = A99
1518    fn evaluate_135(&self, ctx: &EvaluationContext) -> ConditionResult {
1519        // HAND-EDITED: "in dieser SG15 in STS+Z21 DE9013 = A99" — the enclosing SG15 only, when the scope
1520        // reaches one; the message as before otherwise.
1521        if let Some(scope) = ctx.enclosing_scope("SG15") {
1522            // As `has_qualified_value`: Unknown without any STS+Z21.
1523            let sts: Vec<_> = ctx
1524                .find_segments_at(scope, "STS")
1525                .into_iter()
1526                .filter(|s| s.get_element(0) == "Z21")
1527                .collect();
1528            if sts.is_empty() {
1529                return ConditionResult::Unknown;
1530            }
1531            return ConditionResult::from(sts.iter().any(|s| s.get_component(2, 0) == "A99"));
1532        }
1533        ctx.has_qualified_value("STS", 0, "Z21", 2, 0, &["A99"])
1534    }
1535
1536    /// [136] Wenn STS+Z38+Z13+A02:E_1005 in dieser SG14 vorhanden
1537    // REVIEW: Check STS with Statuskategorie Z38 (Auftragstatus Entsperren), Status Z13, Prüfschritt A02 (elements[2][0]), and DE1131 E_1005 (elements[2][1]). Group-scoped to SG14 but falls back to message-wide. (medium confidence)
1538    fn evaluate_136(&self, ctx: &EvaluationContext) -> ConditionResult {
1539        // HAND-EDITED: "in dieser/derselben SG14" — scoped to the enclosing group.
1540        let segments = ctx.scoped_find_segments_in("SG14", "STS");
1541        ConditionResult::from(segments.iter().any(|s| {
1542            s.elements
1543                .first()
1544                .and_then(|e| e.first())
1545                .map(|v| v == "Z38")
1546                .unwrap_or(false)
1547                && s.elements
1548                    .get(1)
1549                    .and_then(|e| e.first())
1550                    .map(|v| v == "Z13")
1551                    .unwrap_or(false)
1552                && s.elements
1553                    .get(2)
1554                    .and_then(|e| e.first())
1555                    .map(|v| v == "A02")
1556                    .unwrap_or(false)
1557                && s.elements
1558                    .get(2)
1559                    .and_then(|e| e.get(1))
1560                    .map(|v| v == "E_1005")
1561                    .unwrap_or(false)
1562        }))
1563    }
1564
1565    /// [137] Wenn in diesem STS DE1131 = E_1005
1566    fn evaluate_137(&self, ctx: &EvaluationContext) -> ConditionResult {
1567        ctx.has_qualified_value("STS", 0, "Z38", 2, 1, &["E_1005"])
1568    }
1569
1570    /// [138] Wenn in diesem STS DE1131 = E_1020
1571    fn evaluate_138(&self, ctx: &EvaluationContext) -> ConditionResult {
1572        ctx.has_qualified_value("STS", 0, "Z38", 2, 1, &["E_1020"])
1573    }
1574
1575    /// [139] Wenn in diesem STS DE1131 = E_1020, dann ist nur der Code A01 möglich.
1576    // REVIEW: Condition checks if DE1131=E_1020 in STS Z38 (Auftragstatus Entsperren). The clause 'dann ist nur der Code A01 möglich' documents that when this condition is true, only code A01 is valid for the dependent field — this is a downstream validation note, not an additional condition predicate. Implementation is identical to condition 138. (medium confidence)
1577    fn evaluate_139(&self, ctx: &EvaluationContext) -> ConditionResult {
1578        ctx.has_qualified_value("STS", 0, "Z38", 2, 1, &["E_1020"])
1579    }
1580
1581    /// [140] Wenn in diesem STS DE9013 &lt;&gt; A01
1582    // REVIEW: Checks if any STS segment has DE9013 (elements[2][0]) with a non-empty value that is not 'A01'. Medium confidence because 'in diesem STS' implies a segment-level context that may not be fully captured by message-wide search. (medium confidence)
1583    fn evaluate_140(&self, ctx: &EvaluationContext) -> ConditionResult {
1584        let sts_segments = ctx.find_segments("STS");
1585        ConditionResult::from(sts_segments.iter().any(|s| {
1586            s.elements
1587                .get(2)
1588                .and_then(|e| e.first())
1589                .is_some_and(|v| !v.is_empty() && v != "A01")
1590        }))
1591    }
1592
1593    /// [141] Wenn in diesem STS DE9013 = A01
1594    // REVIEW: Checks if any STS segment has DE9013 (elements[2][0]) equal to 'A01'. Medium confidence due to segment-level context ambiguity. (medium confidence)
1595    fn evaluate_141(&self, ctx: &EvaluationContext) -> ConditionResult {
1596        let sts_segments = ctx.find_segments("STS");
1597        ConditionResult::from(sts_segments.iter().any(|s| {
1598            s.elements
1599                .get(2)
1600                .and_then(|e| e.first())
1601                .is_some_and(|v| v == "A01")
1602        }))
1603    }
1604
1605    /// [142] Wenn in diesem STS DE1131 = E_0535
1606    fn evaluate_142(&self, ctx: &EvaluationContext) -> ConditionResult {
1607        let sts_segments = ctx.find_segments("STS");
1608        ConditionResult::from(sts_segments.iter().any(|s| {
1609            s.elements
1610                .get(2)
1611                .and_then(|e| e.get(1))
1612                .is_some_and(|v| v == "E_0535")
1613        }))
1614    }
1615
1616    /// [143] Wenn mehr als ein Grund vorliegt und die voranstehenden DE9013 dieses STS nicht ausreichen
1617    // REVIEW: STS Z41 (Grund der Privilegierung nach EnFG) supports up to 5 C556 groups (elements[2]-[6]). 'mehr als ein Grund' is indicated when elements[3][0] (second C556 DE9013) is non-empty, meaning the first slot alone was insufficient. Medium confidence because the exact threshold depends on which positional slot this condition guards. (medium confidence)
1618    fn evaluate_143(&self, ctx: &EvaluationContext) -> ConditionResult {
1619        let sts_segments = ctx.find_segments("STS");
1620        ConditionResult::from(sts_segments.iter().any(|s| {
1621            s.elements
1622                .get(3)
1623                .and_then(|e| e.first())
1624                .is_some_and(|v| !v.is_empty())
1625        }))
1626    }
1627
1628    /// [144] Wenn RFF+Z45 in dieser SG15 nicht vorhanden
1629    // REVIEW: Checks that in some SG15 instance (inside SG14 for IFTSTA Family B), RFF+Z13 (Prüfidentifikator, always present) is present but RFF+Z45 (Artikel-ID reference) is absent. Uses RFF+Z13 as the anchor since it is mandatory in every SG15. (medium confidence)
1630    fn evaluate_144(&self, ctx: &EvaluationContext) -> ConditionResult {
1631        // HAND-EDITED: "RFF+Z45 in dieser SG15 nicht vorhanden" — the enclosing SG15 only, when the scope
1632        // reaches one; the message as before otherwise.
1633        if let Some(scope) = ctx.enclosing_scope("SG15") {
1634            return ConditionResult::from(
1635                !ctx.find_segments_at(scope, "RFF")
1636                    .iter()
1637                    .any(|s| s.get_element(0) == "Z45"),
1638            );
1639        }
1640        ctx.any_group_has_qualifier_without("RFF", 0, "Z13", "RFF", 0, "Z45", &["SG14", "SG15"])
1641    }
1642
1643    /// [145] Wenn RFF+Z17 in dieser SG15 nicht vorhanden
1644    // REVIEW: Checks that in some SG15 instance, RFF+Z13 (mandatory Prüfidentifikator) is present but RFF+Z17 (Preisschlüsselstamm reference) is absent. (medium confidence)
1645    fn evaluate_145(&self, ctx: &EvaluationContext) -> ConditionResult {
1646        // HAND-EDITED: "RFF+Z17 in dieser SG15 nicht vorhanden" — the enclosing SG15 only, when the scope
1647        // reaches one; the message as before otherwise.
1648        if let Some(scope) = ctx.enclosing_scope("SG15") {
1649            return ConditionResult::from(
1650                !ctx.find_segments_at(scope, "RFF")
1651                    .iter()
1652                    .any(|s| s.get_element(0) == "Z17"),
1653            );
1654        }
1655        ctx.any_group_has_qualifier_without("RFF", 0, "Z13", "RFF", 0, "Z17", &["SG14", "SG15"])
1656    }
1657
1658    /// [146] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
1659    fn evaluate_146(&self, ctx: &EvaluationContext) -> ConditionResult {
1660        let com_segs = ctx.find_segments("COM");
1661        ConditionResult::from(com_segs.iter().any(|s| {
1662            s.elements
1663                .first()
1664                .and_then(|e| e.get(1))
1665                .is_some_and(|v| v == "EM")
1666        }))
1667    }
1668
1669    /// [147] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
1670    fn evaluate_147(&self, ctx: &EvaluationContext) -> ConditionResult {
1671        let com_segs = ctx.find_segments("COM");
1672        ConditionResult::from(com_segs.iter().any(|s| {
1673            s.elements
1674                .first()
1675                .and_then(|e| e.get(1))
1676                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
1677        }))
1678    }
1679
1680    /// [148] Wenn in dieser SG15 STS+Z43+Z48+A99:E_0595 vorhanden
1681    fn evaluate_148(&self, ctx: &EvaluationContext) -> ConditionResult {
1682        // HAND-EDITED: "in dieser/derselben SG15" — scoped to the enclosing group.
1683        let sts_segs = ctx.scoped_find_segments_in("SG15", "STS");
1684        ConditionResult::from(sts_segs.iter().any(|s| {
1685            s.elements
1686                .first()
1687                .and_then(|e| e.first())
1688                .is_some_and(|v| v == "Z43")
1689                && s.elements
1690                    .get(1)
1691                    .and_then(|e| e.first())
1692                    .is_some_and(|v| v == "Z48")
1693                && s.elements
1694                    .get(2)
1695                    .and_then(|e| e.first())
1696                    .is_some_and(|v| v == "A99")
1697                && s.elements
1698                    .get(2)
1699                    .and_then(|e| e.get(1))
1700                    .is_some_and(|v| v == "E_0595")
1701        }))
1702    }
1703
1704    /// [492] wenn MP-ID in NAD+MR aus Sparte Strom
1705    /// EXTERNAL: Requires context from outside the message.
1706    fn evaluate_492(&self, ctx: &EvaluationContext) -> ConditionResult {
1707        ctx.external.evaluate("recipient_is_strom")
1708    }
1709
1710    /// [493] wenn MP-ID in NAD+MR aus Sparte Gas
1711    /// EXTERNAL: Requires context from outside the message.
1712    fn evaluate_493(&self, ctx: &EvaluationContext) -> ConditionResult {
1713        ctx.external.evaluate("recipient_is_gas")
1714    }
1715
1716    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
1717    // REVIEW: The date must be the document creation time (DTM+137) or earlier. Applied to DTM+334 (Zeitpunkt der Statusvergabe) which is the most natural target in IFTSTA. Format 303 timestamps are lexicographically ordered so string comparison is valid. (medium confidence)
1718    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
1719        // The referenced date must be the document creation time or earlier (≤ DTM+137)
1720        let doc_segs = ctx.find_segments_with_qualifier("DTM", 0, "137");
1721        let doc_val = match doc_segs
1722            .first()
1723            .and_then(|s| s.elements.first())
1724            .and_then(|e| e.get(1))
1725        {
1726            Some(v) => v.as_str(),
1727            None => return ConditionResult::Unknown,
1728        };
1729        let status_segs = ctx.find_segments_with_qualifier("DTM", 0, "334");
1730        match status_segs
1731            .first()
1732            .and_then(|s| s.elements.first())
1733            .and_then(|e| e.get(1))
1734        {
1735            Some(val) => ConditionResult::from(val.as_str() <= doc_val),
1736            None => ConditionResult::False, // segment absent → condition not applicable
1737        }
1738    }
1739
1740    /// [495] Der Zeitpunkt muss ≤ dem Wert im DE2380 des DTM+137 sein
1741    // REVIEW: The timestamp must be ≤ DE2380 of DTM+137 (document creation date). Compares DTM+334 against DTM+137 using lexicographic ordering, valid for format 303 (YYYYMMDDhhmm) timestamps. (medium confidence)
1742    fn evaluate_495(&self, ctx: &EvaluationContext) -> ConditionResult {
1743        // The timestamp must be ≤ the value in DE2380 of DTM+137
1744        let doc_segs = ctx.find_segments_with_qualifier("DTM", 0, "137");
1745        let doc_val = match doc_segs
1746            .first()
1747            .and_then(|s| s.elements.first())
1748            .and_then(|e| e.get(1))
1749        {
1750            Some(v) => v.as_str(),
1751            None => return ConditionResult::Unknown,
1752        };
1753        let status_segs = ctx.find_segments_with_qualifier("DTM", 0, "334");
1754        match status_segs
1755            .first()
1756            .and_then(|s| s.elements.first())
1757            .and_then(|e| e.get(1))
1758        {
1759            Some(val) => ConditionResult::from(val.as_str() <= doc_val),
1760            None => ConditionResult::False, // segment absent → condition not applicable
1761        }
1762    }
1763
1764    /// [496] Der Zeitpunkt muss &gt; dem Wert im DE2380 des DTM+137 sein
1765    // REVIEW: The timestamp must be strictly greater than DE2380 of DTM+137. Inverse of condition 495. Applied to DTM+334 which must be after the document creation date in this usage context. (medium confidence)
1766    fn evaluate_496(&self, ctx: &EvaluationContext) -> ConditionResult {
1767        // The timestamp must be > the value in DE2380 of DTM+137
1768        let doc_segs = ctx.find_segments_with_qualifier("DTM", 0, "137");
1769        let doc_val = match doc_segs
1770            .first()
1771            .and_then(|s| s.elements.first())
1772            .and_then(|e| e.get(1))
1773        {
1774            Some(v) => v.as_str(),
1775            None => return ConditionResult::Unknown,
1776        };
1777        let status_segs = ctx.find_segments_with_qualifier("DTM", 0, "334");
1778        match status_segs
1779            .first()
1780            .and_then(|s| s.elements.first())
1781            .and_then(|e| e.get(1))
1782        {
1783            Some(val) => ConditionResult::from(val.as_str() > doc_val),
1784            None => ConditionResult::False, // segment absent → condition not applicable
1785        }
1786    }
1787
1788    /// [501] Hinweis: Aus QUOTES BGM DE1004
1789    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
1790        // Hinweis: Aus QUOTES BGM DE1004 — informational note about field origin, always applies
1791        ConditionResult::True
1792    }
1793
1794    /// [502] Hinweis: Aus REQOTE BGM DE1004
1795    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
1796        // Hinweis: Aus REQOTE BGM DE1004 — informational note about field origin, always applies
1797        ConditionResult::True
1798    }
1799
1800    /// [503] Hinweis: Auf Selbsteinbau eines iMS oder einer mME wird verzichtet
1801    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
1802        // Hinweis: Auf Selbsteinbau eines iMS oder einer mME wird verzichtet — informational note, always applies
1803        ConditionResult::True
1804    }
1805
1806    /// [504] Hinweis: Verwendung der ID des MaBiS-ZP
1807    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
1808        // Hinweis: Verwendung der ID des MaBiS-ZP — informational note, always applies
1809        ConditionResult::True
1810    }
1811
1812    /// [505] Hinweis: Verwendung der ID der Messlokation
1813    fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
1814        // Hinweis: Verwendung der ID der Messlokation — informational note, always applies
1815        ConditionResult::True
1816    }
1817
1818    /// [506] Hinweis: Verwendung der ID der Marktlokation
1819    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
1820        // Hinweis: Verwendung der ID der Marktlokation — informational note, always applies
1821        ConditionResult::True
1822    }
1823
1824    /// [510] Hinweis: Es ist neben der Information über die Ablehnung auch der unverändert gebliebene Datenstatus informell mitzugeben.
1825    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
1826        // Hinweis: Es ist neben der Information über die Ablehnung auch der unverändert gebliebene Datenstatus informell mitzugeben — informational note, always applies
1827        ConditionResult::True
1828    }
1829
1830    /// [512] Hinweis: Aus MSCONS BGM DE1004
1831    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
1832        // Hinweis: Aus MSCONS BGM DE1004 — informational note, always applies
1833        ConditionResult::True
1834    }
1835
1836    /// [519] Hinweis: Aus ORDERS BGM DE1004
1837    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
1838        // Hinweis: Aus ORDERS BGM DE1004 — informational note, always applies
1839        ConditionResult::True
1840    }
1841
1842    /// [520] Hinweis: Zeitpunkt, zu dem der Wechsel erfolgt, falls er zustande kommt
1843    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
1844        // Hinweis: Zeitpunkt, zu dem der Wechsel erfolgt, falls er zustande kommt — informational note, always applies
1845        ConditionResult::True
1846    }
1847
1848    /// [521] Hinweis: Zeitpunkt, ab dem der MSBN tatsächlich den Messstellenbetrieb übernimmt
1849    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
1850        // Hinweis: Zeitpunkt, ab dem der MSBN tatsächlich den Messstellenbetrieb übernimmt — informational note, always applies
1851        ConditionResult::True
1852    }
1853
1854    /// [522] Hinweis: Zeitpunkt, ab dem der gMSB den Messstellenbetrieb übernimmt
1855    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
1856        // Hinweis: Zeitpunkt, ab dem der gMSB den Messstellenbetrieb übernimmt — informational note, always applies
1857        ConditionResult::True
1858    }
1859
1860    /// [523] Hinweis: Wert aus BGM DE1004 der MSCONS, auf die sich die Statusangabe bezieht
1861    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
1862        // Hinweis: Wert aus BGM DE1004 der MSCONS, auf die sich die Statusangabe bezieht — informational note, always applies
1863        ConditionResult::True
1864    }
1865
1866    /// [524] Hinweis: Wert aus BGM DE1004 der MSCONS, die den Gegenvorschlag enthält
1867    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
1868        // Hinweis: Wert aus BGM DE1004 der MSCONS, die den Gegenvorschlag enthält — informational note, always applies
1869        ConditionResult::True
1870    }
1871
1872    /// [525] Hinweis: Je SG14 sind nur Statusinformationen zu einer MSCONS enthalten
1873    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
1874        // Hinweis: Je SG14 sind nur Statusinformationen zu einer MSCONS enthalten — informational note, always applies
1875        ConditionResult::True
1876    }
1877
1878    /// [530] Hinweis: Hier ist die Arbeit bzw. Leistung anzugeben, die der Sender der IFTSTA im Lieferschein für den von ihm genannten Zeitraum / Leistungsperiode erwartet hätte.
1879    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
1880        // Hinweis: Hier ist die Arbeit bzw. Leistung anzugeben, die der Sender der IFTSTA im Lieferschein für den von ihm genannten Zeitraum / Leistungsperiode erwartet hätte — informational note, always applies
1881        ConditionResult::True
1882    }
1883
1884    /// [531] Vom MSBN in Schritt 1 des SD verwendete Vorgangsnummer, damit der LF diese bei der Bestellung einer Konfiguration beim MSBN, unter dem Vorbehalt, dass der MSB-Wechsel an der Messlokation erfolgreic...
1885    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
1886        // Hinweis: Vom MSBN in Schritt 1 des SD verwendete Vorgangsnummer, damit der LF diese
1887        // bei der Bestellung einer Konfiguration beim MSBN verwenden kann — informational note
1888        // describing field semantics and usage context, always applies unconditionally
1889        ConditionResult::True
1890    }
1891
1892    /// [532] Hinweis: Verwendung der ID der Netzlokation
1893    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
1894        // Hinweis: Verwendung der ID der Netzlokation — informational note, always applies
1895        ConditionResult::True
1896    }
1897
1898    /// [533] Hinweis: Verwendung der ID der Steuerbaren Ressource
1899    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
1900        // Hinweis: Verwendung der ID der Steuerbaren Ressource — informational note, always applies
1901        ConditionResult::True
1902    }
1903
1904    /// [534] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
1905    fn evaluate_534(&self, _ctx: &EvaluationContext) -> ConditionResult {
1906        // Hinweis: Es darf nur eine Information im DE3148 übermittelt werden — informational note, always applies
1907        ConditionResult::True
1908    }
1909
1910    /// [535] Hinweis: Aus UTILMD IDE DE7402
1911    fn evaluate_535(&self, _ctx: &EvaluationContext) -> ConditionResult {
1912        // Hinweis: Aus UTILMD IDE DE7402 — informational note about value origin, always applies
1913        ConditionResult::True
1914    }
1915
1916    /// [902] Format: Wert darf nur positiv oder 0 sein
1917    // REVIEW: Format condition requiring value >= 0. Applied to QTY segment value (elements[0][1]) which is the standard location for quantity values in EDIFACT. Uses validate_numeric helper with >= 0.0. (medium confidence)
1918    fn evaluate_902(&self, ctx: &EvaluationContext) -> ConditionResult {
1919        ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">=", 0.0))
1920    }
1921
1922    /// [903] Format: Möglicher Wert: 1
1923    // REVIEW: Format condition requiring value == 1. Uses validate_numeric with == operator. Targets QTY segment value (elements[0][1]) as most common numeric value field in IFTSTA context. (medium confidence)
1924    fn evaluate_903(&self, ctx: &EvaluationContext) -> ConditionResult {
1925        ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, "==", 1.0))
1926    }
1927
1928    /// [906] Format: max. 3 Nachkommastellen
1929    // REVIEW: Format condition: max 3 decimal places. Applies to QTY segment value field (elements[0][1]). (medium confidence)
1930    fn evaluate_906(&self, ctx: &EvaluationContext) -> ConditionResult {
1931        ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 3))
1932    }
1933
1934    /// [911] Format: Mögliche Werte: 1 bis n, je Nachricht bei 1 beginnend und fortlaufend aufsteigend
1935    fn evaluate_911(&self, _ctx: &EvaluationContext) -> ConditionResult {
1936        // Hinweis: Mögliche Werte 1 bis n, je Nachricht bei 1 beginnend und fortlaufend aufsteigend
1937        // Informational note about sequence numbering — always applies
1938        ConditionResult::True
1939    }
1940
1941    /// [931] Format: ZZZ = +00
1942    // REVIEW: Format condition: timezone must be UTC (+00). Uses validate_timezone_utc on the DTM value field. Targets first DTM segment found. (medium confidence)
1943    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
1944        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
1945    }
1946
1947    /// [932] Format: HHMM = 2200
1948    // REVIEW: Format condition: HHMM must equal 2200. Uses validate_hhmm_equals. DTM+163 is a common time qualifier in IFTSTA for delivery end times. (medium confidence)
1949    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
1950        ctx.format_check_qualified("DTM", 0, "163", 0, 1, |val| {
1951            validate_hhmm_equals(val, "2200")
1952        })
1953    }
1954
1955    /// [933] Format: HHMM = 2300
1956    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
1957        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
1958    }
1959
1960    /// [934] Format: HHMM = 0400
1961    fn evaluate_934(&self, ctx: &EvaluationContext) -> ConditionResult {
1962        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0400"))
1963    }
1964
1965    /// [935] Format: HHMM = 0500
1966    fn evaluate_935(&self, ctx: &EvaluationContext) -> ConditionResult {
1967        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0500"))
1968    }
1969
1970    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
1971    // REVIEW: Checks if any COM segment value contains both '@' and '.' characters, validating email address format. COM segments carry communication details; email format requires both characters. This is the standard way to detect email COM values in EDIFACT. (medium confidence)
1972    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
1973        let com_segments = ctx.find_segments("COM");
1974        for seg in &com_segments {
1975            if let Some(value) = seg.elements.first().and_then(|e| e.first()) {
1976                if value.contains('@') && value.contains('.') {
1977                    return ConditionResult::True;
1978                }
1979            }
1980        }
1981        ConditionResult::False
1982    }
1983
1984    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
1985    // REVIEW: Checks if any COM segment value starts with '+' and is followed only by digits — the international phone number format (E.164). This is the standard pattern for phone number validation in German energy market EDIFACT messages. (medium confidence)
1986    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
1987        let com_segments = ctx.find_segments("COM");
1988        for seg in &com_segments {
1989            if let Some(value) = seg.elements.first().and_then(|e| e.first()) {
1990                if value.starts_with('+')
1991                    && value.len() > 1
1992                    && value[1..].chars().all(|c| c.is_ascii_digit())
1993                {
1994                    return ConditionResult::True;
1995                }
1996            }
1997        }
1998        ConditionResult::False
1999    }
2000
2001    /// [950] Format: Marktlokations-ID
2002    fn evaluate_950(&self, ctx: &EvaluationContext) -> ConditionResult {
2003        ctx.format_check_qualified("LOC", 0, "Z16", 1, 0, validate_malo_id)
2004    }
2005
2006    /// [951] Format: Zählpunktbezeichnung
2007    // REVIEW: Format condition validating Zählpunktbezeichnung (33 alphanumeric chars). In IFTSTA, Zählpunkt is typically associated with LOC+Z19 (Messlokation/Zählpunkt). Uses validate_zahlpunkt helper. Medium confidence because the exact LOC qualifier for Zählpunkt in IFTSTA may vary — could also be a different qualifier depending on context. (medium confidence)
2008    fn evaluate_951(&self, ctx: &EvaluationContext) -> ConditionResult {
2009        ctx.format_check_qualified("LOC", 0, "Z19", 1, 0, validate_zahlpunkt)
2010    }
2011
2012    /// [960] Format: Netzlokations-ID
2013    // HAND-EDITED: a Netzlokations-ID is "E" + ten alphanumerics (`E1688117482`),
2014    // not MaLo-shaped; `validate_malo_id` rejected every real one (#173).
2015    fn evaluate_960(&self, ctx: &EvaluationContext) -> ConditionResult {
2016        ctx.format_check_qualified("LOC", 0, "Z18", 1, 0, validate_nelo_id)
2017    }
2018
2019    /// [961] Format: SR-ID
2020    // REVIEW: SR-ID (Steuerbare Ressource ID) uses the same 11-digit Luhn check digit format as Marktlokations-ID. LOC+Z19 is the standard qualifier for SteuerbareRessource. Element 1, component 0 holds the location ID. (medium confidence)
2021    fn evaluate_961(&self, ctx: &EvaluationContext) -> ConditionResult {
2022        ctx.format_check_qualified("LOC", 0, "Z19", 1, 0, validate_malo_id)
2023    }
2024}