automapper-validation 0.8.0

AHB condition expression parsing, evaluation, and EDIFACT validation
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
// <auto-generated>
// Generated by automapper-generator generate-conditions
// AHB: xml-migs-and-ahbs/FV2610/UTILMD_AHB_Strom_2_2_Fehlerkorrektur_20260629_.xml
// Generated: 2026-04-22T18:25:39Z
// Delegates unchanged conditions to UtilmdStromConditionEvaluatorFV2604 (FV2604).
// </auto-generated>

#![allow(clippy::style, clippy::complexity)]

#[allow(unused_imports)]
use crate::eval::format_validators::*;
use crate::eval::{ConditionEvaluator, ConditionResult, EvaluationContext};
use crate::generated::fv2604::UtilmdStromConditionEvaluatorFV2604;

/// Condition evaluator for UTILMD_Strom FV2610.
///
/// Delegates to [`UtilmdStromConditionEvaluatorFV2604`] for conditions whose AHB description is
/// unchanged from that version. Local overrides: [23, 24, 41, 63, 67, 79, 80, 81, 82, 158, 159, 181, 185, 186, 187, 203, 263, 272, 274, 275, 276, 353, 354, 381, 382, 383, 422, 423, 424, 531, 555, 571, 701, 2002].
pub struct UtilmdStromConditionEvaluatorFV2610 {
    fallback: UtilmdStromConditionEvaluatorFV2604,
}

impl Default for UtilmdStromConditionEvaluatorFV2610 {
    fn default() -> Self {
        Self {
            fallback: UtilmdStromConditionEvaluatorFV2604::default(),
        }
    }
}

/// Condition IDs whose implementation differs from the fallback.
const FV2610_OVERRIDES: &[u32] = &[
    23, 24, 41, 63, 67, 79, 80, 81, 82, 158, 159, 181, 185, 186, 187, 203, 263, 272, 274, 275, 276,
    353, 354, 381, 382, 383, 422, 423, 424, 531, 555, 571, 701, 2002,
];

impl ConditionEvaluator for UtilmdStromConditionEvaluatorFV2610 {
    fn message_type(&self) -> &str {
        "UTILMD_Strom"
    }

    fn format_version(&self) -> &str {
        "FV2610"
    }

    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
        match condition {
            23 => self.evaluate_23(ctx),
            24 => self.evaluate_24(ctx),
            41 => self.evaluate_41(ctx),
            63 => self.evaluate_63(ctx),
            67 => self.evaluate_67(ctx),
            79 => self.evaluate_79(ctx),
            80 => self.evaluate_80(ctx),
            81 => self.evaluate_81(ctx),
            82 => self.evaluate_82(ctx),
            158 => self.evaluate_158(ctx),
            159 => self.evaluate_159(ctx),
            181 => self.evaluate_181(ctx),
            203 => self.evaluate_203(ctx),
            263 => self.evaluate_263(ctx),
            353 => self.evaluate_353(ctx),
            354 => self.evaluate_354(ctx),
            381 => self.evaluate_381(ctx),
            382 => self.evaluate_382(ctx),
            383 => self.evaluate_383(ctx),
            422 => self.evaluate_422(ctx),
            423 => self.evaluate_423(ctx),
            424 => self.evaluate_424(ctx),
            531 => self.evaluate_531(ctx),
            555 => self.evaluate_555(ctx),
            571 => self.evaluate_571(ctx),
            701 => self.evaluate_701(ctx),
            2002 => self.evaluate_2002(ctx),
            185 => self.evaluate_185(ctx),
            186 => self.evaluate_186(ctx),
            187 => self.evaluate_187(ctx),
            272 => self.evaluate_272(ctx),
            274 => self.evaluate_274(ctx),
            275 => self.evaluate_275(ctx),
            276 => self.evaluate_276(ctx),
            other => self.fallback.evaluate(other, ctx),
        }
    }

    fn is_external(&self, condition: u32) -> bool {
        matches!(
            condition,
            181 | 185 | 186 | 187 | 274 | 275 | 276 | 353 | 354 | 381 | 382 | 383 | 422 | 423 | 424
        ) || (!FV2610_OVERRIDES.contains(&condition) && self.fallback.is_external(condition))
    }

    fn is_known(&self, condition: u32) -> bool {
        FV2610_OVERRIDES.contains(&condition) || self.fallback.is_known(condition)
    }
}

impl UtilmdStromConditionEvaluatorFV2610 {
    /// [23] Wenn in dieser SG4 das STS+E01++A05 (Status der Antwort) vorhanden
    fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.has_segment_matching_in_group("STS", &[(0, 0, "E01"), (2, 0, "A05")], &["SG4"])
    }

    /// [24] Wenn in dieser SG4 das STS+E01++A25 (Status der Antwort) vorhanden
    fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.has_segment_matching_in_group("STS", &[(0, 0, "E01"), (2, 0, "A25")], &["SG4"])
    }

    /// [41] Es ist eine Produktpaket-ID aus dem DE1050 von einem SG8 SEQ+Z79 (Bestandteil eines Produktpakets) zu nennen
    // REVIEW: Cross-reference validation: the current field must contain a Produktpaket-ID from DE1050 (elements[1][0]) of some SG8 SEQ+Z79. Collects all valid Produktpaket-IDs across SG8 instances and checks the resolved field value is among them. Medium confidence: 'zu nennen' interpreted as a cross-reference constraint; if it is purely informational the body should return True unconditionally. (medium confidence)
    fn evaluate_41(&self, ctx: &EvaluationContext) -> ConditionResult {
        let nav = match ctx.navigator() {
            Some(n) => n,
            None => return ConditionResult::Unknown,
        };
        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
        let mut valid_ids: std::collections::HashSet<String> = std::collections::HashSet::new();
        for i in 0..sg8_count {
            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
            for seq in &seq_segs {
                if seq
                    .elements
                    .first()
                    .and_then(|e| e.first())
                    .is_some_and(|v| v == "Z79")
                {
                    if let Some(val) = seq.elements.get(1).and_then(|e| e.first()) {
                        if !val.is_empty() {
                            valid_ids.insert(val.clone());
                        }
                    }
                }
            }
        }
        if valid_ids.is_empty() {
            return ConditionResult::Unknown;
        }
        if let Some(current_val) = ctx.resolved_value {
            return ConditionResult::from(valid_ids.contains(current_val));
        }
        ConditionResult::Unknown
    }

    /// [63] Wenn in dieser SG4 das STS+E01++A15 (Status der Antwort) vorhanden
    fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.has_segment_matching_in_group("STS", &[(0, 0, "E01"), (2, 0, "A15")], &["SG4"])
    }

    /// [67] Wenn in keinem SG8 SEQ+Z79 (Bestandteil eine Produktpakets) CCI+Z66/CAV+ZH9 (Produkteigenschaft/ Code der Produkteigenschaft) der Code 9991000002933 (Ruhende Marktlokation ausprägen) vorhanden ist.
    // REVIEW: Absence check: returns True when NO SG8 with SEQ+Z79 has an SG10 child containing CCI+Z66 (Produkteigenschaft) co-occurring with CAV bearing value 9991000002933 (Ruhende Marktlokation ausprägen). Returns False as soon as the disqualifying combination is found. Medium confidence: CAV element layout for ZH9/9991000002933 is ambiguous without the MIG XML — implementation checks both ZH9+9991000002933 as a composite and 9991000002933 as a bare first component. (medium confidence)
    fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
        let nav = match ctx.navigator() {
            Some(n) => n,
            None => return ConditionResult::Unknown,
        };
        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
        for i in 0..sg8_count {
            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
            let has_z79 = seq_segs.iter().any(|s| {
                s.elements
                    .first()
                    .and_then(|e| e.first())
                    .is_some_and(|v| v == "Z79")
            });
            if !has_z79 {
                continue;
            }
            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
            for j in 0..sg10_count {
                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
                let has_cci_z66 = ccis.iter().any(|s| {
                    s.elements
                        .first()
                        .and_then(|e| e.first())
                        .is_some_and(|v| v == "Z66")
                });
                if has_cci_z66 {
                    let cavs =
                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
                    let has_target = cavs.iter().any(|s| {
                        let first_elem = s.elements.first();
                        let qualifier = first_elem.and_then(|e| e.first()).map(|v| v.as_str());
                        let value = first_elem.and_then(|e| e.get(1)).map(|v| v.as_str());
                        (qualifier == Some("ZH9") && value == Some("9991000002933"))
                            || qualifier == Some("9991000002933")
                    });
                    if has_target {
                        return ConditionResult::False;
                    }
                }
            }
        }
        ConditionResult::True
    }

    /// [79] Wenn SG4 STS+7++Z33 (Auszug wegen Stilllegung) vorhanden
    fn evaluate_79(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.has_segment_matching_in_group("STS", &[(0, 0, "7"), (2, 0, "Z33")], &["SG4"])
    }

    /// [80] Wenn in derselben SG8 SEQ+Z61 (Produkt-Daten der Steuerbaren Ressource) das PIA+5 (Produkt-Daten der Steuerbaren Ressource) nicht vorhanden
    fn evaluate_80(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.any_group_has_qualifier_without("SEQ", 0, "Z61", "PIA", 0, "5", &["SG4", "SG8"])
    }

    /// [81] Wenn in derselben SG8 SEQ+Z61/ ZB3/ ZB4 (Produkt-Daten der Steuerbaren Ressource) das SG10 CCI+11 (Details zum Produkt der Steuerbaren Ressource) nicht vorhanden
    // REVIEW: Multi-qualifier SEQ check (Z61/ZB3/ZB4) combined with SG10 child absence requires the navigator. Returns True when any SG8 with a qualifying SEQ has no SG10 child with CCI+11. Medium confidence: three-way qualifier match and SG10 absence via child group iteration. (medium confidence)
    fn evaluate_81(&self, ctx: &EvaluationContext) -> ConditionResult {
        let nav = match ctx.navigator() {
            Some(n) => n,
            None => return ConditionResult::Unknown,
        };
        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
        for i in 0..sg8_count {
            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
            let has_qualifying_seq = seq_segs.iter().any(|s| {
                s.elements
                    .first()
                    .and_then(|e| e.first())
                    .is_some_and(|v| matches!(v.as_str(), "Z61" | "ZB3" | "ZB4"))
            });
            if !has_qualifying_seq {
                continue;
            }
            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
            let has_cci_11 = (0..sg10_count).any(|j| {
                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
                ccis.iter().any(|s| {
                    s.elements
                        .first()
                        .and_then(|e| e.first())
                        .is_some_and(|v| v == "11")
                })
            });
            if !has_cci_11 {
                return ConditionResult::True;
            }
        }
        ConditionResult::False
    }

    /// [82] Wenn in der selben SG8 (Daten der technischen Einrichtungen) das SG10 CCI+Z63 (Information zu technischen Einrichtungen) CAV+ZH7 (Technische Einrichtungen vorhanden) vorhanden
    // REVIEW: Checks if any SG10 child of any SG8 contains both CCI+Z63 (Information zu technischen Einrichtungen) and CAV+ZH7 (Technische Einrichtungen vorhanden) in the same SG10 instance. Both qualifiers checked at elements[0][0] following the CAV+qualifier pattern from Example 10. Medium confidence: no parent SG8 qualifier filter specified; CCI element position assumed from established pattern. (medium confidence)
    fn evaluate_82(&self, ctx: &EvaluationContext) -> ConditionResult {
        let nav = match ctx.navigator() {
            Some(n) => n,
            None => return ctx.has_qualifier("CAV", 0, "ZH7"),
        };
        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
        for i in 0..sg8_count {
            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
            for j in 0..sg10_count {
                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
                let has_cci_z63 = ccis.iter().any(|s| {
                    s.elements
                        .first()
                        .and_then(|e| e.first())
                        .is_some_and(|v| v == "Z63")
                });
                if has_cci_z63 {
                    let cavs =
                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
                    if cavs.iter().any(|s| {
                        s.elements
                            .first()
                            .and_then(|e| e.first())
                            .is_some_and(|v| v == "ZH7")
                    }) {
                        return ConditionResult::True;
                    }
                }
            }
        }
        ConditionResult::False
    }

    /// [158] Wenn in derselben SG8 SEQ+Z57/ ZD9 (OBIS-Daten der Netzlokation) das PIA+5 (OBIS-Daten der Netzlokation) nicht vorhanden
    fn evaluate_158(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.any_group_has_qualifier_without("SEQ", 0, "Z57", "PIA", 0, "5", &["SG4", "SG8"])
            .or(ctx.any_group_has_qualifier_without(
                "SEQ",
                0,
                "ZD9",
                "PIA",
                0,
                "5",
                &["SG4", "SG8"],
            ))
    }

    /// [159] Wenn in derselben SG8 SEQ+Z57/ ZD9 (OBIS-Daten der Netzlokation) das SG10 CCI+11 (OBIS-Daten der Netzlokation) nicht vor...
    fn evaluate_159(&self, ctx: &EvaluationContext) -> ConditionResult {
        let with_cci = |seq: &str| {
            ctx.filtered_parent_child_has_qualifier(
                &["SG4", "SG8"],
                "SEQ",
                0,
                seq,
                "SG10",
                "CCI",
                0,
                "11",
            )
        };
        with_cci("Z57").or(with_cci("ZD9")).negate()
    }

    /// [181] Wenn der Versender datenschutzrechtliche Voraussetzungen geschaffen hat, um die Daten bereitstellen zu dürfen
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_181(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("sender_has_data_protection_prerequisites")
    }

    /// [203] Wenn STS+7++E06 / Z39 / ZC6 / ZC7/ ZT6/ ZT7 / Z02 / ZZD vorhanden
    fn evaluate_203(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.has_qualified_value(
            "STS",
            0,
            "7",
            2,
            0,
            &["E06", "Z39", "ZC6", "ZC7", "ZT6", "ZT7", "Z02", "ZZD"],
        )
    }

    /// [263] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z07 (Verbrauch) vorhanden
    fn evaluate_263(&self, ctx: &EvaluationContext) -> ConditionResult {
        let nav = match ctx.navigator() {
            Some(n) => n,
            None => return ctx.has_qualified_value("CCI", 0, "Z30", 2, 0, &["Z07"]),
        };
        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
        for i in 0..sg8_count {
            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
            let has_z01 = seq_segs.iter().any(|s| {
                s.elements
                    .first()
                    .and_then(|e| e.first())
                    .is_some_and(|v| v == "Z01")
            });
            if !has_z01 {
                continue;
            }
            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
            for j in 0..sg10_count {
                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
                for cci in &ccis {
                    let elem0 = cci
                        .elements
                        .first()
                        .and_then(|e| e.first())
                        .map(|s| s.as_str());
                    let elem2 = cci
                        .elements
                        .get(2)
                        .and_then(|e| e.first())
                        .map(|s| s.as_str());
                    if elem0 == Some("Z30") && elem2 == Some("Z07") {
                        return ConditionResult::True;
                    }
                }
            }
        }
        ConditionResult::False
    }

    /// [353] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in derselben Zeile sowohl in der Spalte „Lokation/NeLo“ als auch in der ...
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_353(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("verwendungszweck_code_valid_nelocation_nb")
    }

    /// [354] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in derselben Zeile sowohl in der Spalte „Lokation/NeLo“ als auch in der ...
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_354(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("verwendungszweck_code_valid_nelocation_lf")
    }

    /// [381] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in derselben Zeile sowohl in der Spalte „Lokation/MaLo“ als auch in der ...
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_381(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("verwendungszweck_code_valid_malocation_nb")
    }

    /// [382] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in derselben Zeile sowohl in der Spalte „Lokation/MaLo“ als auch in der ...
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_382(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("verwendungszweck_code_valid_malocation_lf")
    }

    /// [383] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in derselben Zeile sowohl in der Spalte „Lokation/MaLo“ als auch in der ...
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_383(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("verwendungszweck_code_valid_malocation_uenb")
    }

    /// [422] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in derselben Zeile sowohl in der Spalte „Lokation/Tranche“ als auch in d...
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_422(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("verwendungszweck_code_valid_tranche_nb")
    }

    /// [423] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in derselben Zeile sowohl in der Spalte „Lokation/Tranche“ als auch in d...
    /// EXTERNAL: Requires context from outside the message.
    // REVIEW: References external code list from Kapitel 2 (Codeliste der Verwendungszwecke) filtered by 'Lokation/Tranche' and 'Verwendbar für Marktrolle/LF' columns, plus OBIS-Kennzahl matching — all three criteria require external business context unavailable in the EDIFACT message alone (medium confidence)
    fn evaluate_423(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external.evaluate("verwendungszweck_valid_for_lf")
    }

    /// [424] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in derselben Zeile sowohl in der Spalte „Lokation/Tranche“ als auch in d...
    /// EXTERNAL: Requires context from outside the message.
    // REVIEW: References external code list from Kapitel 2 (Codeliste der Verwendungszwecke) filtered by 'Lokation/Tranche' and 'Verwendbar für Marktrolle/ÜNB' columns, plus OBIS-Kennzahl matching — all three criteria require external business context unavailable in the EDIFACT message alone (medium confidence)
    fn evaluate_424(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external.evaluate("verwendungszweck_valid_for_uenb")
    }

    /// [531] Hinweis: Es ist das Jahr anzugeben in dem die nächste Netznutzungsabrechnung erfolgt. Diese Information ist zu der Zeitraum-ID anzugeben, welche SG6 RFF+Z49 (Verwendungszeitraum der Daten: Gültig...
    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
        // Hinweis: year of next Netznutzungsabrechnung to be stated for the Zeitraum-ID in SG6 RFF+Z49 — informational note, always applies
        ConditionResult::True
    }

    /// [555] Hinweis: Es sind die Produktpaket-ID aus der Anmeldung zu nennen, welche nicht umgesetzt werden konnten
    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
        // Hinweis: Produktpaket-IDs from the Anmeldung that could not be implemented — informational note, always applies
        ConditionResult::True
    }

    /// [571] Hinweis: Es sind alle erforderlichen Produkte aus der Anmeldung des LF des im selben SG8 genannten Produktpaket-ID zu nennen welche nicht umgesetzt werden konnten
    fn evaluate_571(&self, _ctx: &EvaluationContext) -> ConditionResult {
        // Hinweis: all required products from LF Anmeldung for the Produktpaket-ID in the same SG8 that could not be implemented — informational note, always applies
        ConditionResult::True
    }

    /// [701] Hinweis: Es ist die Malo-ID der Tranche zu nennen, der die Technische Ressource zugeordnet ist
    fn evaluate_701(&self, _ctx: &EvaluationContext) -> ConditionResult {
        // Hinweis: MaLo-ID of the Tranche to which the Technische Ressource is assigned — informational note, always applies
        ConditionResult::True
    }

    /// [2002] Für jede Produktpaket-ID im SG8 SEQ+Z79 (Bestandteil eines Produktpakets) DE1050 genau einmal anzugeben
    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
        let nav = match ctx.navigator() {
            Some(n) => n,
            None => return ConditionResult::Unknown,
        };
        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
        let mut seen = std::collections::HashSet::new();
        for i in 0..sg8_count {
            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
            for seq in &seq_segs {
                if seq
                    .elements
                    .first()
                    .and_then(|e| e.first())
                    .is_some_and(|v| v == "Z79")
                {
                    if let Some(val) = seq.elements.get(1).and_then(|e| e.first()) {
                        if !val.is_empty() && !seen.insert(val.clone()) {
                            return ConditionResult::False;
                        }
                    }
                }
            }
        }
        if seen.is_empty() {
            ConditionResult::Unknown
        } else {
            ConditionResult::True
        }
    }

    /// [185] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in ders...
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_185(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("verwendungszweck_code_valid_malocation_nb_produkt")
    }

    /// [186] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in ders...
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_186(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("verwendungszweck_code_valid_malocation_lf_produkt")
    }

    /// [187] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in ders...
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_187(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("verwendungszweck_code_valid_malocation_uenb_produkt")
    }

    /// [274] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in ders...
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_274(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("verwendungszweck_code_valid_tranche_nb_produkt")
    }

    /// [275] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in ders...
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_275(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("verwendungszweck_code_valid_tranche_lf_produkt")
    }

    /// [276] Zulässig sind ausschließlich Codes aus der Spalte „Code“ der Codeliste der Verwendungszwecke (Kapitel 2), sofern in ders...
    /// EXTERNAL: Requires context from outside the message.
    fn evaluate_276(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external
            .evaluate("verwendungszweck_code_valid_tranche_uenb_produkt")
    }

    /// [272] Wenn im selben Segment im DE2379 der Code 106 vorhanden ist
    fn evaluate_272(&self, ctx: &EvaluationContext) -> ConditionResult {
        ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "106"))
    }
}