// <auto-generated>
// Generated by automapper-generator generate-conditions
// AHB: xml-migs-and-ahbs/FV2604/MSCONS_AHB_3_1g_20251001.xml
// Generated: 2026-03-12T11:36:06Z
// </auto-generated>
// LLM-produced bodies aren't idiomatic Rust — silence stylistic lints at
// file scope so `cargo clippy -D warnings` stays green. Correctness,
// suspicious, and perf categories stay on so real bugs still surface.
#![allow(clippy::style, clippy::complexity)]
#[allow(unused_imports)]
use crate::eval::format_validators::*;
use crate::eval::{ConditionEvaluator, ConditionResult, EvaluationContext};
/// Generated condition evaluator for MSCONS FV2604.
pub struct MsconsConditionEvaluatorFV2604 {
// External condition IDs that require runtime context.
external_conditions: std::collections::HashSet<u32>,
}
impl Default for MsconsConditionEvaluatorFV2604 {
fn default() -> Self {
let mut external_conditions = std::collections::HashSet::new();
external_conditions.insert(1);
external_conditions.insert(22);
external_conditions.insert(32);
external_conditions.insert(33);
external_conditions.insert(35);
external_conditions.insert(36);
external_conditions.insert(42);
external_conditions.insert(67);
external_conditions.insert(77);
external_conditions.insert(80);
external_conditions.insert(81);
external_conditions.insert(97);
external_conditions.insert(117);
external_conditions.insert(118);
external_conditions.insert(126);
external_conditions.insert(127);
external_conditions.insert(138);
external_conditions.insert(141);
external_conditions.insert(492);
external_conditions.insert(493);
Self {
external_conditions,
}
}
}
impl ConditionEvaluator for MsconsConditionEvaluatorFV2604 {
fn message_type(&self) -> &str {
"MSCONS"
}
fn format_version(&self) -> &str {
"FV2604"
}
fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
match condition {
1 => self.evaluate_1(ctx),
2 => self.evaluate_2(ctx),
11 => self.evaluate_11(ctx),
12 => self.evaluate_12(ctx),
17 => self.evaluate_17(ctx),
18 => self.evaluate_18(ctx),
22 => self.evaluate_22(ctx),
23 => self.evaluate_23(ctx),
24 => self.evaluate_24(ctx),
27 => self.evaluate_27(ctx),
28 => self.evaluate_28(ctx),
32 => self.evaluate_32(ctx),
33 => self.evaluate_33(ctx),
35 => self.evaluate_35(ctx),
36 => self.evaluate_36(ctx),
38 => self.evaluate_38(ctx),
42 => self.evaluate_42(ctx),
45 => self.evaluate_45(ctx),
46 => self.evaluate_46(ctx),
48 => self.evaluate_48(ctx),
49 => self.evaluate_49(ctx),
50 => self.evaluate_50(ctx),
51 => self.evaluate_51(ctx),
62 => self.evaluate_62(ctx),
67 => self.evaluate_67(ctx),
68 => self.evaluate_68(ctx),
69 => self.evaluate_69(ctx),
70 => self.evaluate_70(ctx),
71 => self.evaluate_71(ctx),
72 => self.evaluate_72(ctx),
73 => self.evaluate_73(ctx),
77 => self.evaluate_77(ctx),
78 => self.evaluate_78(ctx),
79 => self.evaluate_79(ctx),
80 => self.evaluate_80(ctx),
81 => self.evaluate_81(ctx),
83 => self.evaluate_83(ctx),
84 => self.evaluate_84(ctx),
85 => self.evaluate_85(ctx),
86 => self.evaluate_86(ctx),
87 => self.evaluate_87(ctx),
88 => self.evaluate_88(ctx),
90 => self.evaluate_90(ctx),
91 => self.evaluate_91(ctx),
92 => self.evaluate_92(ctx),
93 => self.evaluate_93(ctx),
94 => self.evaluate_94(ctx),
95 => self.evaluate_95(ctx),
96 => self.evaluate_96(ctx),
97 => self.evaluate_97(ctx),
98 => self.evaluate_98(ctx),
99 => self.evaluate_99(ctx),
100 => self.evaluate_100(ctx),
101 => self.evaluate_101(ctx),
108 => self.evaluate_108(ctx),
111 => self.evaluate_111(ctx),
113 => self.evaluate_113(ctx),
117 => self.evaluate_117(ctx),
118 => self.evaluate_118(ctx),
119 => self.evaluate_119(ctx),
121 => self.evaluate_121(ctx),
125 => self.evaluate_125(ctx),
126 => self.evaluate_126(ctx),
127 => self.evaluate_127(ctx),
128 => self.evaluate_128(ctx),
129 => self.evaluate_129(ctx),
130 => self.evaluate_130(ctx),
131 => self.evaluate_131(ctx),
132 => self.evaluate_132(ctx),
133 => self.evaluate_133(ctx),
134 => self.evaluate_134(ctx),
135 => self.evaluate_135(ctx),
138 => self.evaluate_138(ctx),
141 => self.evaluate_141(ctx),
142 => self.evaluate_142(ctx),
143 => self.evaluate_143(ctx),
144 => self.evaluate_144(ctx),
145 => self.evaluate_145(ctx),
146 => self.evaluate_146(ctx),
147 => self.evaluate_147(ctx),
148 => self.evaluate_148(ctx),
149 => self.evaluate_149(ctx),
150 => self.evaluate_150(ctx),
151 => self.evaluate_151(ctx),
152 => self.evaluate_152(ctx),
153 => self.evaluate_153(ctx),
154 => self.evaluate_154(ctx),
490 => self.evaluate_490(ctx),
491 => self.evaluate_491(ctx),
492 => self.evaluate_492(ctx),
493 => self.evaluate_493(ctx),
494 => self.evaluate_494(ctx),
495 => self.evaluate_495(ctx),
501 => self.evaluate_501(ctx),
502 => self.evaluate_502(ctx),
506 => self.evaluate_506(ctx),
509 => self.evaluate_509(ctx),
510 => self.evaluate_510(ctx),
511 => self.evaluate_511(ctx),
512 => self.evaluate_512(ctx),
513 => self.evaluate_513(ctx),
514 => self.evaluate_514(ctx),
515 => self.evaluate_515(ctx),
516 => self.evaluate_516(ctx),
518 => self.evaluate_518(ctx),
519 => self.evaluate_519(ctx),
520 => self.evaluate_520(ctx),
522 => self.evaluate_522(ctx),
523 => self.evaluate_523(ctx),
524 => self.evaluate_524(ctx),
525 => self.evaluate_525(ctx),
526 => self.evaluate_526(ctx),
528 => self.evaluate_528(ctx),
529 => self.evaluate_529(ctx),
530 => self.evaluate_530(ctx),
531 => self.evaluate_531(ctx),
532 => self.evaluate_532(ctx),
535 => self.evaluate_535(ctx),
538 => self.evaluate_538(ctx),
541 => self.evaluate_541(ctx),
543 => self.evaluate_543(ctx),
544 => self.evaluate_544(ctx),
545 => self.evaluate_545(ctx),
546 => self.evaluate_546(ctx),
547 => self.evaluate_547(ctx),
551 => self.evaluate_551(ctx),
553 => self.evaluate_553(ctx),
554 => self.evaluate_554(ctx),
556 => self.evaluate_556(ctx),
557 => self.evaluate_557(ctx),
558 => self.evaluate_558(ctx),
559 => self.evaluate_559(ctx),
560 => self.evaluate_560(ctx),
566 => self.evaluate_566(ctx),
567 => self.evaluate_567(ctx),
568 => self.evaluate_568(ctx),
569 => self.evaluate_569(ctx),
570 => self.evaluate_570(ctx),
571 => self.evaluate_571(ctx),
572 => self.evaluate_572(ctx),
573 => self.evaluate_573(ctx),
574 => self.evaluate_574(ctx),
575 => self.evaluate_575(ctx),
576 => self.evaluate_576(ctx),
577 => self.evaluate_577(ctx),
578 => self.evaluate_578(ctx),
579 => self.evaluate_579(ctx),
580 => self.evaluate_580(ctx),
581 => self.evaluate_581(ctx),
902 => self.evaluate_902(ctx),
904 => self.evaluate_904(ctx),
905 => self.evaluate_905(ctx),
906 => self.evaluate_906(ctx),
907 => self.evaluate_907(ctx),
908 => self.evaluate_908(ctx),
909 => self.evaluate_909(ctx),
910 => self.evaluate_910(ctx),
917 => self.evaluate_917(ctx),
918 => self.evaluate_918(ctx),
922 => self.evaluate_922(ctx),
925 => self.evaluate_925(ctx),
931 => self.evaluate_931(ctx),
932 => self.evaluate_932(ctx),
933 => self.evaluate_933(ctx),
934 => self.evaluate_934(ctx),
935 => self.evaluate_935(ctx),
937 => self.evaluate_937(ctx),
939 => self.evaluate_939(ctx),
940 => self.evaluate_940(ctx),
950 => self.evaluate_950(ctx),
951 => self.evaluate_951(ctx),
960 => self.evaluate_960(ctx),
2001 => self.evaluate_2001(ctx),
2002 => self.evaluate_2002(ctx),
2003 => self.evaluate_2003(ctx),
_ => ConditionResult::Unknown,
}
}
fn is_external(&self, condition: u32) -> bool {
self.external_conditions.contains(&condition)
}
fn is_known(&self, condition: u32) -> bool {
matches!(
condition,
1 | 2
| 11
| 12
| 17
| 18
| 22
| 23
| 24
| 27
| 28
| 32
| 33
| 35
| 36
| 38
| 42
| 45
| 46
| 48
| 49
| 50
| 51
| 62
| 67
| 68
| 69
| 70
| 71
| 72
| 73
| 77
| 78
| 79
| 80
| 81
| 83
| 84
| 85
| 86
| 87
| 88
| 90
| 91
| 92
| 93
| 94
| 95
| 96
| 97
| 98
| 99
| 100
| 101
| 108
| 111
| 113
| 117
| 118
| 119
| 121
| 125
| 126
| 127
| 128
| 129
| 130
| 131
| 132
| 133
| 134
| 135
| 138
| 141
| 142
| 143
| 144
| 145
| 146
| 147
| 148
| 149
| 150
| 151
| 152
| 153
| 154
| 490
| 491
| 492
| 493
| 494
| 495
| 501
| 502
| 506
| 509
| 510
| 511
| 512
| 513
| 514
| 515
| 516
| 518
| 519
| 520
| 522
| 523
| 524
| 525
| 526
| 528
| 529
| 530
| 531
| 532
| 535
| 538
| 541
| 543
| 544
| 545
| 546
| 547
| 551
| 553
| 554
| 556
| 557
| 558
| 559
| 560
| 566
| 567
| 568
| 569
| 570
| 571
| 572
| 573
| 574
| 575
| 576
| 577
| 578
| 579
| 580
| 581
| 902
| 904
| 905
| 906
| 907
| 908
| 909
| 910
| 917
| 918
| 922
| 925
| 931
| 932
| 933
| 934
| 935
| 937
| 939
| 940
| 950
| 951
| 960
| 2001
| 2002
| 2003
)
}
}
impl MsconsConditionEvaluatorFV2604 {
/// [23] Wenn UNH DE0070 mit 1 vorhanden
fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
let segs = ctx.find_segments("UNH");
match segs.first() {
Some(unh) => match unh.elements.get(3).and_then(|e| e.first()) {
Some(val) => ConditionResult::from(val == "1"),
None => ConditionResult::False,
},
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [24] Bei Aufteilung, in der Nachricht mit der höchsten Übermittlungsnummer
// REVIEW: "Bei Aufteilung" = UNH S010 is present (splitting indicator exists). "höchste Übermittlungsnummer" = the last message in the sequence, identified by DE0073 (elements[3][1]) == "F" (final). For non-split messages S010 is absent → False. Confidence medium because EDIFACT S010 usage in MSCONS may vary. (medium confidence)
fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
let unh_segs = ctx.find_segments("UNH");
match unh_segs.first() {
Some(unh) => {
match unh.elements.get(3) {
Some(s010) => {
// S010 present = splitting in use.
// DE0073 (components[1]) == "F" = final/last message in split sequence
let is_final = s010.get(1).is_some_and(|v| v == "F");
ConditionResult::from(is_final)
}
None => ConditionResult::False, // No S010 = no splitting
}
}
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [130] Wenn innerhalb desselben LIN-Segments neben diesem Segment (SG10 DTM+7 Nutzungszeitpunkt) noch das SG10 DTM+60 (Ausführungs- / Änderungszeitpunkt) oder das SG10 DTM+9 (Ablesedatum) vorhanden, dar...
// REVIEW: Iterates each SG10 instance (child of SG6 in SG5). For each SG10 containing DTM+7, checks co-located DTM+60 or DTM+9. Parses CCYYMMDDHHMM via Julian Day Number to total minutes, computes absolute difference, then selects threshold based on DST transition: MEZ→MESZ (winter→summer) = 23h, MESZ→MEZ (summer→winter) = 25h, no transition = 24h. Uses is_mesz_utc on the earlier and later timestamps to detect the transition direction. Group path SG5→SG6→SG10 assumed from MSCONS structure; medium confidence because SG path and S010 semantics need verification. (medium confidence)
fn evaluate_130(&self, ctx: &EvaluationContext) -> ConditionResult {
fn parse_to_minutes(s: &str) -> Option<i64> {
let s = if s.len() >= 12 {
&s[..12]
} else {
return None;
};
if !s.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
let year: i64 = s[0..4].parse().ok()?;
let month: i64 = s[4..6].parse().ok()?;
let day: i64 = s[6..8].parse().ok()?;
let hour: i64 = s[8..10].parse().ok()?;
let minute: i64 = s[10..12].parse().ok()?;
let a = (14 - month) / 12;
let y = year + 4800 - a;
let m_adj = month + 12 * a - 3;
let jdn = day + (153 * m_adj + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045;
Some(jdn * 1440 + hour * 60 + minute)
}
let nav = match ctx.navigator() {
Some(n) => n,
None => return ConditionResult::Unknown,
};
// HAND-EDITED: SG10 is nested under SG9 (LIN) in MSCONS — path is
// SG5.SG6.SG9.SG10, not SG5.SG6.SG10. Flattened iteration for clarity.
let sg10_count = nav.group_instance_count(&["SG5", "SG6", "SG9", "SG10"]);
for si in 0..sg10_count {
let dtms = nav.find_segments_in_group("DTM", &["SG5", "SG6", "SG9", "SG10"], si);
let t7 = dtms
.iter()
.find(|d| {
d.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "7")
})
.and_then(|d| d.elements.first().and_then(|e| e.get(1)))
.cloned();
if let Some(t7_val) = t7 {
let t7_mins = match parse_to_minutes(&t7_val) {
Some(v) => v,
None => continue,
};
for &other_qual in &["60", "9"] {
let t_other = dtms
.iter()
.find(|d| {
d.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == other_qual)
})
.and_then(|d| d.elements.first().and_then(|e| e.get(1)))
.cloned();
if let Some(t_other_val) = t_other {
let t_other_mins = match parse_to_minutes(&t_other_val) {
Some(v) => v,
None => continue,
};
let diff = (t7_mins - t_other_mins).abs();
let (min_str, max_str) = if t7_mins <= t_other_mins {
(t7_val.as_str(), t_other_val.as_str())
} else {
(t_other_val.as_str(), t7_val.as_str())
};
let min_in_mesz = matches!(is_mesz_utc(min_str), ConditionResult::True);
let max_in_mesz = matches!(is_mesz_utc(max_str), ConditionResult::True);
// MEZ→MESZ (winter→summer, March): threshold < 23h
// MESZ→MEZ (summer→winter, October): threshold < 25h
// Same timezone: threshold < 24h
let threshold = if !min_in_mesz && max_in_mesz {
23 * 60
} else if min_in_mesz && !max_in_mesz {
25 * 60
} else {
24 * 60
};
if diff >= threshold {
return ConditionResult::False;
}
}
}
}
}
ConditionResult::True
}
/// [151] Wenn BGM+Z27 (Bewegungsdaten im Kalenderjahr vor Lieferbeginn) vorhanden.
fn evaluate_151(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("BGM", 0, "Z27")
}
/// [152] Wenn BGM+7 (Prozessdatenbericht) vorhanden.
fn evaluate_152(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("BGM", 0, "7")
}
/// [153] Wenn SG9 PIA+5+7-0?:33.86.0 vorhanden
fn evaluate_153(&self, ctx: &EvaluationContext) -> ConditionResult {
// PIA+5+7-0?:33.86.0 — elements[0][0]="5", elements[1][0]="7-0:33.86.0" (EDIFACT ?-escape for colon)
ctx.has_segment_matching_in_group(
"PIA",
&[(0, 0, "5"), (1, 0, "7-0:33.86.0")],
&["SG5", "SG9"],
)
}
/// [154] Wenn SG9 PIA+5+7-0?:33.47.0 vorhanden
fn evaluate_154(&self, ctx: &EvaluationContext) -> ConditionResult {
// PIA+5+7-0?:33.47.0 — elements[0][0]="5", elements[1][0]="7-0:33.47.0" (EDIFACT ?-escape for colon)
ctx.has_segment_matching_in_group(
"PIA",
&[(0, 0, "5"), (1, 0, "7-0:33.47.0")],
&["SG5", "SG9"],
)
}
/// [578] Hinweis: Wenn es sich um die Übermittlung des Leistungswertes für die Netzentgelte mit Jahresleistungspreissystem handelt.
fn evaluate_578(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Übermittlung des Leistungswertes für Netzentgelte mit Jahresleistungspreissystem — informational note, always applies
ConditionResult::True
}
/// [579] Hinweis: Wenn es sich um die Übermittlung des Leistungswertes für die Netzentgelte mit Monatsleistungspreissystem handelt.
fn evaluate_579(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Übermittlung des Leistungswertes für Netzentgelte mit Monatsleistungspreissystem — informational note, always applies
ConditionResult::True
}
/// [580] Hinweis: Wenn es sich um die Übermittlung des Leistungswertes für die Netzentgelte mit Tagesleistungspreissystem handelt.
fn evaluate_580(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Übermittlung des Leistungswertes für Netzentgelte mit Tagesleistungspreissystem — informational note, always applies
ConditionResult::True
}
/// [581] Hinweis: Wenn es sich um die Übermittlung des Monatsmaximum gemäß WiM handelt.
fn evaluate_581(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wenn es sich um die Übermittlung des Monatsmaximum gemäß WiM handelt.
// Informational note, always applies
ConditionResult::True
}
/// [1] Sofern per ORDERS angefordert
/// EXTERNAL: Requires context from outside the message.
fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("requested_via_orders")
}
/// [2] Wenn das Zeitintervall zwischen ersten SG10 DTM+163 und letzten SG10 DTM+164 mindestens einen Monat umfasst
// REVIEW: Parses year+month from DTM+163 (first) and DTM+164 (last) in format 303 (YYYYMMDDHHmm). Computes calendar-month difference. 'Mindestens einen Monat' interpreted as end month > start month (calendar boundary). Medium confidence due to ambiguity between calendar-month vs. 30-day interpretation. (medium confidence)
fn evaluate_2(&self, ctx: &EvaluationContext) -> ConditionResult {
let dtm_163 = ctx.find_segments_with_qualifier("DTM", 0, "163");
let dtm_164 = ctx.find_segments_with_qualifier("DTM", 0, "164");
let first_start = dtm_163
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
.map(|s| s.as_str());
let last_end = dtm_164
.last()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
.map(|s| s.as_str());
match (first_start, last_end) {
(Some(start), Some(end)) if start.len() >= 6 && end.len() >= 6 => {
let start_year: u32 = start[0..4].parse().unwrap_or(0);
let start_month: u32 = start[4..6].parse().unwrap_or(0);
let end_year: u32 = end[0..4].parse().unwrap_or(0);
let end_month: u32 = end[4..6].parse().unwrap_or(0);
let start_total = start_year * 12 + start_month;
let end_total = end_year * 12 + end_month;
ConditionResult::from(end_total >= start_total + 1)
}
_ => ConditionResult::Unknown,
}
}
/// [11] Wenn SG9 PIA+5+7-0?:52.0.22/7-0?:54.0.16/7-0?:54.0.20/7-0?:54.0.22
fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
let pia_segments = ctx.find_segments_with_qualifier("PIA", 0, "5");
let obis_values = ["7-0:52.0.22", "7-0:54.0.16", "7-0:54.0.20", "7-0:54.0.22"];
ConditionResult::from(pia_segments.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| obis_values.contains(&v.as_str()))
}))
}
/// [12] Wenn nicht SG9 PIA+5+7-0?:52.0.22/7-0?:54.0.16/7-0?:54.0.20/7-0?:54.0.22
fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
let pia_segments = ctx.find_segments_with_qualifier("PIA", 0, "5");
let obis_values = ["7-0:52.0.22", "7-0:54.0.16", "7-0:54.0.20", "7-0:54.0.22"];
ConditionResult::from(!pia_segments.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| obis_values.contains(&v.as_str()))
}))
}
/// [17] Wenn nicht SG9 PIA+5+1-b?:9.99.0 (b= Kanal: Wert gemäß Codeliste der OBIS-Kennzahlen und Medien)
// REVIEW: Checks absence of PIA+5 with OBIS matching pattern '1-{channel}:9.99.0'. The 'b' is a variable channel, so pattern-matching on prefix '1-' and suffix ':9.99.0' is used. Negated because condition is 'Wenn NICHT'. Medium confidence due to pattern-based matching of OBIS code. (medium confidence)
fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
let pia_segments = ctx.find_segments_with_qualifier("PIA", 0, "5");
let has_match = pia_segments.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v.starts_with("1-") && v.ends_with(":9.99.0"))
});
ConditionResult::from(!has_match)
}
/// [18] Wenn SG9 PIA+5+1-b?:9.99.0 (b= Kanal: Wert gemäß Codeliste der OBIS-Kennzahlen und Medien)
// REVIEW: Affirmative version of condition 17. True when any PIA+5 has OBIS code matching the '1-{channel}:9.99.0' pattern (combined heat/power or special metering). Medium confidence due to pattern matching. (medium confidence)
fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
let pia_segments = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(pia_segments.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v.starts_with("1-") && v.ends_with(":9.99.0"))
}))
}
/// [22] Wenn Aufteilung vorhanden
/// EXTERNAL: Requires context from outside the message.
fn evaluate_22(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("message_splitting")
}
/// [27] Wenn SG9 PIA+5+1-1?:1.9.0 vorhanden
fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
let pia_segments = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(pia_segments.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v == "1-1:1.9.0")
}))
}
/// [28] Wenn SG9 PIA+5+1-1?:1.9.0 nicht vorhanden
fn evaluate_28(&self, ctx: &EvaluationContext) -> ConditionResult {
let pia_segments = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(!pia_segments.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v == "1-1:1.9.0")
}))
}
/// [32] wenn MP-ID in SG2 NAD+MS in der Rolle NB
/// EXTERNAL: Requires context from outside the message.
fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("sender_is_nb")
}
/// [33] wenn MP-ID in SG2 NAD+MR in der Rolle LF
/// EXTERNAL: Requires context from outside the message.
fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_lf")
}
/// [35] wenn MP-ID in SG2 NAD+MS in der Rolle MSB
/// EXTERNAL: Requires context from outside the message.
fn evaluate_35(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("sender_is_msb")
}
/// [36] wenn MP-ID in SG2 NAD+MR in der Rolle NB
/// EXTERNAL: Requires context from outside the message.
fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_nb")
}
/// [38] wenn in SG6 LOC+172 DE3225 die ID der Messlokation angegeben ist
fn evaluate_38(&self, ctx: &EvaluationContext) -> ConditionResult {
// Messlokation = 33-char alphanumeric Zählpunktbezeichnung. A non-empty
// value that doesn't match (e.g. an 11-digit MaLo-ID) must be False so
// the AHB's XOR branches against condition [119] stay mutually exclusive.
let loc_segments = ctx.find_segments_with_qualifier("LOC", 0, "172");
match loc_segments.first() {
Some(loc) => match loc.elements.get(1).and_then(|e| e.first()) {
Some(v) if !v.is_empty() => validate_zahlpunkt(v),
_ => ConditionResult::False,
},
None => ConditionResult::False,
}
}
/// [42] Wenn MP-ID in SG2 NAD+MR in der Rolle MSB
/// EXTERNAL: Requires context from outside the message.
fn evaluate_42(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_msb")
}
/// [45] Wenn SG9 PIA+5+7-b:99.41.16 (b=Kanal: Wert gemäß Codeliste der OBIS-Kennzahlen und Medien) vorhanden
fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
let pias = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(pias.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v.starts_with("7-") && v.ends_with(":99.41.16"))
}))
}
/// [46] Wenn Wert in SG6 LOC+172 DE3225 genau 11 Stellen
fn evaluate_46(&self, ctx: &EvaluationContext) -> ConditionResult {
let locs = ctx.find_segments_with_qualifier("LOC", 0, "172");
if locs.is_empty() {
return ConditionResult::Unknown;
}
ConditionResult::from(locs.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v.len() == 11)
}))
}
/// [48] Wenn SG9 PIA+5+7-0?:52.0.22
fn evaluate_48(&self, ctx: &EvaluationContext) -> ConditionResult {
let pias = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(pias.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v == "7-0:52.0.22")
}))
}
/// [49] Wenn SG9 PIA+5+7-b?:70.16.16/7-b?:70.16.20/7-b?:70.16.22 (b=Kanal: Wert gemäß Codeliste der OBIS-Kennzahlen und Medien) vorhanden
fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
let pias = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(pias.iter().any(|s| {
s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
v.starts_with("7-")
&& (v.ends_with(":70.16.16")
|| v.ends_with(":70.16.20")
|| v.ends_with(":70.16.22"))
})
}))
}
/// [50] Wenn SG9 PIA+5+7-b?:70.18.16/7-b?:70.18.20/7-b?:70.18.22 (b=Kanal: Wert gemäß Codeliste der OBIS-Kennzahlen und Medien) vorhanden
fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
let pias = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(pias.iter().any(|s| {
s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
v.starts_with("7-")
&& (v.ends_with(":70.18.16")
|| v.ends_with(":70.18.20")
|| v.ends_with(":70.18.22"))
})
}))
}
/// [51] Wenn SG9 PIA+5+7-0?:33.86.0 vorhanden ist, darf mittels Wiederholung SG9 LIN in derselben Nachricht das SG9 PIA+5+7-0?:52.0.22/7-0?:54.0.16/7-0?:54.0.20/7-0?:54.0.22 nicht mehr angegeben werden
// REVIEW: Mutual-exclusion constraint: if 7-0:33.86.0 is present, none of the listed 52/54.x codes may appear. Returns True when constraint is satisfied (i.e. 33.86.0 absent, or 33.86.0 present but excluded codes absent). Returns True when 33.86.0 is not present because the constraint simply does not apply. (medium confidence)
fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
let pias = ctx.find_segments_with_qualifier("PIA", 0, "5");
let has_3386 = pias.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v == "7-0:33.86.0")
});
if !has_3386 {
return ConditionResult::True;
}
let excluded = ["7-0:52.0.22", "7-0:54.0.16", "7-0:54.0.20", "7-0:54.0.22"];
ConditionResult::from(!pias.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| excluded.contains(&v.as_str()))
}))
}
/// [62] Wenn Wert in SG6 LOC+172 DE3225 genau 33 Stellen
fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
let locs = ctx.find_segments_with_qualifier("LOC", 0, "172");
if locs.is_empty() {
return ConditionResult::Unknown;
}
ConditionResult::from(locs.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v.len() == 33)
}))
}
/// [67] Wenn es sich um die Referenz auf eine ORDERS handelt
/// EXTERNAL: Requires context from outside the message.
// REVIEW: Checks whether the current MSCONS transaction references an ORDERS message. While RFF segments can carry cross-message references, the specific qualifier that marks an ORDERS reference is not provided in the segment structure reference, and determining whether a reference points to an ORDERS vs. another message type typically requires external business context about the originating transaction. (medium confidence)
fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("is_orders_reference")
}
/// [68] Wenn BGM+7 vorhanden
fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("BGM", 0, "7")
}
/// [69] Wenn BGM+Z28 vorhanden
fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("BGM", 0, "Z28")
}
/// [70] Wenn BGM+BK vorhanden
fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("BGM", 0, "BK")
}
/// [71] Wenn BGM+Z39 vorhanden
fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("BGM", 0, "Z39")
}
/// [72] Wenn SG9 PIA+5+1-b?:1.6.0/1-b?:3.6.0/1-b?:4.6.0/1-66?:13.6.0/1-66?:14.6.0 (b=Kanal: Wert gemäß Codeliste der OBIS-Kennzahlen und Medien) vorhanden
fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
let pias = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(pias.iter().any(|s| {
s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
(v.starts_with("1-")
&& (v.ends_with(":1.6.0") || v.ends_with(":3.6.0") || v.ends_with(":4.6.0")))
|| v == "1-66:13.6.0"
|| v == "1-66:14.6.0"
})
}))
}
/// [73] Wenn SG9 PIA+5+1-b?:1.9.e/1-b?:3.9.0/1-b?:4.9.0/1-66?:13.9.0/1-66?:14.9.0 (b=Kanal: Wert gemäß Codeliste der OBIS-Kennzahlen und Medien, e=Tarif: Wert gemäß Codeliste der OBIS-Kennzahlen und Me...
fn evaluate_73(&self, ctx: &EvaluationContext) -> ConditionResult {
let pias = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(pias.iter().any(|s| {
s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
(v.starts_with("1-") && v.contains(":1.9."))
|| (v.starts_with("1-") && v.ends_with(":3.9.0"))
|| (v.starts_with("1-") && v.ends_with(":4.9.0"))
|| v == "1-66:13.9.0"
|| v == "1-66:14.9.0"
})
}))
}
/// [77] Wenn MP-ID in SG2 NAD+MR der RB HKN-R
/// EXTERNAL: Requires context from outside the message.
fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external
.evaluate("recipient_is_hkn_registration_authority")
}
/// [78] Wenn SG9 PIA+5+1-66?:13.6.0/1-66?:14.6.0/1-66?:13.9.0/1-66?:14.9.0 vorhanden
// REVIEW: PIA+5 means elements[0][0]='5'. DE7140 (OBIS code) is elements[1][0]. EDIFACT '?:' escape sequences are resolved by the parser to literal ':', so '1-66?:13.6.0' becomes '1-66:13.6.0'. Checks any SG9 group instance for PIA+5 with one of the four OBIS codes. Group path SG6->SG9 is approximate based on MSCONS structure. (medium confidence)
fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualified_value(
"PIA",
0,
"5",
1,
0,
&["1-66:13.6.0", "1-66:14.6.0", "1-66:13.9.0", "1-66:14.9.0"],
&["SG6", "SG9"],
)
}
/// [79] Wenn SG9 PIA+5+1-66?:13.6.0/1-66?:14.6.0/1-66?:13.9.0/1-66?:14.9.0 nicht vorhanden
// REVIEW: Logical negation of condition 78. True when none of the specified OBIS codes appears in any SG9 PIA+5 segment. (medium confidence)
fn evaluate_79(&self, ctx: &EvaluationContext) -> ConditionResult {
match ctx.any_group_has_qualified_value(
"PIA",
0,
"5",
1,
0,
&["1-66:13.6.0", "1-66:14.6.0", "1-66:13.9.0", "1-66:14.9.0"],
&["SG6", "SG9"],
) {
ConditionResult::True => ConditionResult::False,
ConditionResult::False => ConditionResult::True,
ConditionResult::Unknown => ConditionResult::Unknown,
}
}
/// [80] Wenn MP-ID in SG2 NAD+MR in der Rolle ÜNB
/// EXTERNAL: Requires context from outside the message.
fn evaluate_80(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_uenb")
}
/// [81] Wenn MP-ID in SG2 NAD+MS in der Rolle ÜNB
/// EXTERNAL: Requires context from outside the message.
fn evaluate_81(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("sender_is_uenb")
}
/// [83] Wenn in derselben SG9 LIN die Angabe STS+10+Z38 nicht vorhanden
// REVIEW: STS 'Grundlage der Energiemenge' has elements[0][0]='10' (Statuskategorie) and elements[1][0]=status code. STS lives in SG10 (child of SG9). Condition is True when NO STS with category 10 and status Z38 exists within SG9/SG10 scope. Using any_group_has_qualified_value on SG9->SG10 path then negating. Group scope is approximate — a precise check would traverse SG9 instances and their SG10 children. (medium confidence)
fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
match ctx.any_group_has_qualified_value("STS", 0, "10", 1, 0, &["Z38"], &["SG9", "SG10"]) {
ConditionResult::True => ConditionResult::False,
ConditionResult::False => ConditionResult::True,
ConditionResult::Unknown => ConditionResult::Unknown,
}
}
/// [84] Wenn in derselben SG9 LIN die Angabe STS+10+Z39 nicht vorhanden
// REVIEW: Same pattern as condition 83 but for status code Z39. True when no STS+10+Z39 is present in any SG10 within the SG9 scope. (medium confidence)
fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
match ctx.any_group_has_qualified_value("STS", 0, "10", 1, 0, &["Z39"], &["SG9", "SG10"]) {
ConditionResult::True => ConditionResult::False,
ConditionResult::False => ConditionResult::True,
ConditionResult::Unknown => ConditionResult::Unknown,
}
}
/// [85] Wenn in derselben SG9 LIN die Angabe STS+10+Z36 nicht vorhanden
// REVIEW: Same pattern as condition 83 but for status code Z36. True when no STS+10+Z36 is present in any SG10 within the SG9 scope. (medium confidence)
fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
match ctx.any_group_has_qualified_value("STS", 0, "10", 1, 0, &["Z36"], &["SG9", "SG10"]) {
ConditionResult::True => ConditionResult::False,
ConditionResult::False => ConditionResult::True,
ConditionResult::Unknown => ConditionResult::Unknown,
}
}
/// [86] Wenn in derselben SG9 LIN die Angabe STS+10+Z37 nicht vorhanden
// REVIEW: Same pattern as condition 83 but for status code Z37. True when no STS+10+Z37 is present in any SG10 within the SG9 scope. (medium confidence)
fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
match ctx.any_group_has_qualified_value("STS", 0, "10", 1, 0, &["Z37"], &["SG9", "SG10"]) {
ConditionResult::True => ConditionResult::False,
ConditionResult::False => ConditionResult::True,
ConditionResult::Unknown => ConditionResult::Unknown,
}
}
/// [87] Wenn der Wert in DTM+163 DE2380 derselben SG6 LOC+172 mit demselben Wert in SG9 PIA+5 DE7140 der früheste angegebene Zeitpunkt ist
// REVIEW: Checks that DTM+163 in SG6 (with LOC+172) equals the minimum DTM+163 across all SG10 instances beneath that SG6's SG9 children. Medium confidence because the SG9→SG10 child navigation uses a local child instance index (si) passed to the grandchild API, which may behave differently depending on navigator implementation details for two-level child traversal. (medium confidence)
fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
let nav = match ctx.navigator() {
Some(n) => n,
None => return ConditionResult::Unknown,
};
let sg6_count = nav.group_instance_count(&["SG5", "SG6"]);
for gi in 0..sg6_count {
let locs = nav.find_segments_in_group("LOC", &["SG5", "SG6"], gi);
let has_loc_172 = locs.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "172")
});
if !has_loc_172 {
continue;
}
let sg6_dtms = nav.find_segments_in_group("DTM", &["SG5", "SG6"], gi);
let sg6_val = match sg6_dtms
.iter()
.find(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "163")
})
.and_then(|s| s.elements.first().and_then(|e| e.get(1)))
.filter(|v| !v.is_empty())
.cloned()
{
Some(v) => v,
None => continue,
};
let sg9_count = nav.child_group_instance_count(&["SG5", "SG6"], gi, "SG9");
let mut sg10_values: Vec<String> = Vec::new();
for si in 0..sg9_count {
let sg10_count = nav.child_group_instance_count(&["SG5", "SG6", "SG9"], si, "SG10");
for ti in 0..sg10_count {
let dtms = nav.find_segments_in_child_group(
"DTM",
&["SG5", "SG6", "SG9"],
si,
"SG10",
ti,
);
for dtm in &dtms {
if dtm
.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "163")
{
if let Some(val) = dtm.elements.first().and_then(|e| e.get(1)) {
if !val.is_empty() {
sg10_values.push(val.clone());
}
}
}
}
}
}
if let Some(min_val) = sg10_values.iter().min() {
if &sg6_val != min_val {
return ConditionResult::False;
}
}
}
ConditionResult::True
}
/// [88] Wenn der Wert in DTM+164 DE2380 derselben SG6 LOC+172 mit demselben Wert in SG9 PIA+5 DE7140 der späteste angegebene Zeitpunkt ist
// REVIEW: Mirror of condition 87 but for DTM+164 (Ende Messperiode) — checks that the SG6 transmission period end equals the maximum DTM+164 across all SG10 measurement instances under that SG6. Same navigation caveat applies for two-level SG9→SG10 child traversal. (medium confidence)
fn evaluate_88(&self, ctx: &EvaluationContext) -> ConditionResult {
let nav = match ctx.navigator() {
Some(n) => n,
None => return ConditionResult::Unknown,
};
let sg6_count = nav.group_instance_count(&["SG5", "SG6"]);
for gi in 0..sg6_count {
let locs = nav.find_segments_in_group("LOC", &["SG5", "SG6"], gi);
let has_loc_172 = locs.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "172")
});
if !has_loc_172 {
continue;
}
let sg6_dtms = nav.find_segments_in_group("DTM", &["SG5", "SG6"], gi);
let sg6_val = match sg6_dtms
.iter()
.find(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "164")
})
.and_then(|s| s.elements.first().and_then(|e| e.get(1)))
.filter(|v| !v.is_empty())
.cloned()
{
Some(v) => v,
None => continue,
};
let sg9_count = nav.child_group_instance_count(&["SG5", "SG6"], gi, "SG9");
let mut sg10_values: Vec<String> = Vec::new();
for si in 0..sg9_count {
let sg10_count = nav.child_group_instance_count(&["SG5", "SG6", "SG9"], si, "SG10");
for ti in 0..sg10_count {
let dtms = nav.find_segments_in_child_group(
"DTM",
&["SG5", "SG6", "SG9"],
si,
"SG10",
ti,
);
for dtm in &dtms {
if dtm
.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "164")
{
if let Some(val) = dtm.elements.first().and_then(|e| e.get(1)) {
if !val.is_empty() {
sg10_values.push(val.clone());
}
}
}
}
}
}
if let Some(max_val) = sg10_values.iter().max() {
if &sg6_val != max_val {
return ConditionResult::False;
}
}
}
ConditionResult::True
}
/// [90] Wenn BGM+Z41 vorhanden
fn evaluate_90(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("BGM", 0, "Z41")
}
/// [91] Wenn BGM+Z42 vorhanden
fn evaluate_91(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("BGM", 0, "Z42")
}
/// [92] Wenn SG10 QTY DE6063 mit Wert 67 vorhanden
fn evaluate_92(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualifier("QTY", 0, "67", &["SG9", "SG10"])
}
/// [93] Wenn SG10 QTY DE6063 mit Wert 220 vorhanden
fn evaluate_93(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualifier("QTY", 0, "220", &["SG9", "SG10"])
}
/// [94] Wenn SG10 QTY DE6063 mit Wert 201 vorhanden
fn evaluate_94(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualifier("QTY", 0, "201", &["SG9", "SG10"])
}
/// [95] Wenn SG10 QTY DE6063 mit Wert 20 vorhanden
fn evaluate_95(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualifier("QTY", 0, "20", &["SG5", "SG10"])
}
/// [96] Wenn SG10 QTY DE6063 mit Wert Z18 vorhanden
fn evaluate_96(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualifier("QTY", 0, "Z18", &["SG5", "SG10"])
}
/// [97] Wenn es sich um die Übermittlung eines Wertes aufgrund der Umstellung der Gasqualität handelt
/// EXTERNAL: Requires context from outside the message.
// REVIEW: Whether this transmission is caused by a gas quality changeover (H-Gas/L-Gas conversion) cannot be determined from EDIFACT message content alone — it is a business context condition. (medium confidence)
fn evaluate_97(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("gas_quality_change_transmission")
}
/// [98] Wenn SG9 PIA+5+SOL:Z08 vorhanden
fn evaluate_98(&self, ctx: &EvaluationContext) -> ConditionResult {
let pias = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(pias.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v == "SOL")
&& s.elements
.get(1)
.and_then(|e| e.get(1))
.is_some_and(|v| v == "Z08")
}))
}
/// [99] Wenn SG9 PIA+5+WID:Z08 vorhanden
fn evaluate_99(&self, ctx: &EvaluationContext) -> ConditionResult {
let pias = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(pias.iter().any(|s| {
s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v == "WID")
&& s.elements
.get(1)
.and_then(|e| e.get(1))
.is_some_and(|v| v == "Z08")
}))
}
/// [100] Wenn in derselben SG9 LIN das PIA+5+AUA:Z08 vorhanden
fn evaluate_100(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualified_value("PIA", 0, "5", 1, 0, &["AUA"], &["SG5", "SG9"])
}
/// [101] Wenn in derselben SG9 LIN das PIA+5+FPA:Z08 vorhanden
fn evaluate_101(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualified_value("PIA", 0, "5", 1, 0, &["FPA"], &["SG5", "SG9"])
}
/// [108] wenn SG9 PIA+5+7-b?:99.41.16/7-b?:99.42.16 (b=Kanal: Wert gemäß Codeliste der OBIS-Kennzahlen und Medien) vorhanden
// REVIEW: Checks PIA+5 for OBIS codes matching pattern '7-{channel}:99.41.16' or '7-{channel}:99.42.16' (Wirkenergie-Kennzahlen). The '?' in AHB notation is the EDIFACT release character escaping the colon; parsed segments will contain literal colon. Channel variable 'b' matched via starts_with("7-"). (medium confidence)
fn evaluate_108(&self, ctx: &EvaluationContext) -> ConditionResult {
let pias = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(pias.iter().any(|s| {
let obis = match s.elements.get(1).and_then(|e| e.first()) {
Some(v) => v.as_str(),
None => return false,
};
let code_list = s
.elements
.get(1)
.and_then(|e| e.get(1))
.map(|v| v.as_str())
.unwrap_or("");
code_list == "Z08"
&& obis.starts_with("7-")
&& (obis.contains(":99.41.16") || obis.contains(":99.42.16"))
}))
}
/// [111] Wenn SG10 DTM+9 DE2379 in demselben Segment mit Wert 303 vorhanden
fn evaluate_111(&self, ctx: &EvaluationContext) -> ConditionResult {
ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "303"))
}
/// [113] wenn SG7 RFF+AGK (Konfigurations-ID) vorhanden
fn evaluate_113(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("RFF", 0, "AGK")
}
/// [117] Nur MP-ID aus Sparte Strom
/// EXTERNAL: Requires context from outside the message.
fn evaluate_117(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("marktpartner_is_strom")
}
/// [118] Nur MP-ID aus Sparte Gas
/// EXTERNAL: Requires context from outside the message.
fn evaluate_118(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("marktpartner_is_gas")
}
/// [119] wenn in SG6 LOC+172 DE3225 die ID der Marktlokation angegeben ist
fn evaluate_119(&self, ctx: &EvaluationContext) -> ConditionResult {
// Marktlokation = 11-digit MaLo-ID with BDEW check digit. A non-empty
// value that doesn't match (e.g. a 33-char Zählpunkt) must be False so
// the AHB's XOR branches against condition [38] stay mutually exclusive.
let locs = ctx.find_segments_with_qualifier("LOC", 0, "172");
match locs.first() {
Some(loc) => match loc.elements.get(1).and_then(|e| e.first()) {
Some(v) if !v.is_empty() => validate_malo_id(v),
_ => ConditionResult::False,
},
None => ConditionResult::False,
}
}
/// [121] wenn BGM+Z43 (Redispatch Ausfallarbeitüberführungszeitreihe) vorhanden
fn evaluate_121(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("BGM", 0, "Z43")
}
/// [125] wenn SG9 PIA+5+7-0?:52.0.22/7-b?:53.0.16/7-b?:55.0.16/7-b?:55.0.20/7-b?:55.0.22 (b=Kanal: Wert gemäß Codeliste der OBIS-Kennzahlen und Medien) vorhanden
// REVIEW: Checks PIA+5 for specific OBIS code patterns: '7-0:52.0.22' (fixed channel 0), '7-{b}:53.0.16', '7-{b}:55.0.16', '7-{b}:55.0.20', '7-{b}:55.0.22' (variable channel). These are Blindenergie/Leistungsmessungs-Kennzahlen. Uses contains() to match the OBIS suffix after the channel variable. (medium confidence)
fn evaluate_125(&self, ctx: &EvaluationContext) -> ConditionResult {
let pias = ctx.find_segments_with_qualifier("PIA", 0, "5");
ConditionResult::from(pias.iter().any(|s| {
let obis = match s.elements.get(1).and_then(|e| e.first()) {
Some(v) => v.as_str(),
None => return false,
};
let code_list = s
.elements
.get(1)
.and_then(|e| e.get(1))
.map(|v| v.as_str())
.unwrap_or("");
code_list == "Z08"
&& obis.starts_with("7-")
&& (obis.contains(":52.0.22")
|| obis.contains(":53.0.16")
|| obis.contains(":55.0.16")
|| obis.contains(":55.0.20")
|| obis.contains(":55.0.22"))
}))
}
/// [126] wenn Plausibilisierungshinweise vorliegen
/// EXTERNAL: Requires context from outside the message.
fn evaluate_126(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("plausibility_notes_present")
}
/// [127] wenn ein Korrekturgrund anzugeben ist
/// EXTERNAL: Requires context from outside the message.
fn evaluate_127(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("correction_reason_required")
}
/// [128] Wenn es sich um eine Ablesung handelt, welche keine Ablesung aufgrund der Änderung an der Messtechnik oder deren Konfiguration ist (z.B. Kundenablesung).
// REVIEW: A regular reading (e.g. Kundenablesung) is distinguished from a meter-change reading by the absence of DTM+60 (Ausführungs-/Änderungszeitpunkt) in the same SG10. DTM+9 presence confirms it is a reading at all; DTM+60 absence confirms it is not due to a meter/config change. Group path SG9→SG10 matches MSCONS LIN-group context; fallback to message-wide if no navigator. (medium confidence)
fn evaluate_128(&self, ctx: &EvaluationContext) -> ConditionResult {
// True when SG10 has a reading date (DTM+9, Ablesedatum) but no device change timestamp (DTM+60),
// indicating a regular reading not caused by meter change or configuration change
ctx.any_group_has_qualifier_without("DTM", 0, "9", "DTM", 0, "60", &["SG9", "SG10"])
}
/// [129] Wenn es sich um eine Ablesung aufgrund der Änderung an der Messtechnik oder deren Konfiguration handelt (z.B. Gerätewechsel).
// REVIEW: A meter-change reading is identified by the co-occurrence of DTM+9 (Ablesedatum) and DTM+60 (Ausführungs-/Änderungszeitpunkt) within the same SG10 instance. Uses direct navigator iteration to check co-occurrence within a single group instance (any_group_has_co_occurrence would have ambiguity for same-tag different qualifiers). Falls back to message-wide check without navigator. (medium confidence)
fn evaluate_129(&self, ctx: &EvaluationContext) -> ConditionResult {
// True when SG10 contains both DTM+9 (Ablesedatum) and DTM+60 (Ausführungs-/Änderungszeitpunkt),
// indicating a reading caused by a meter change or configuration change (e.g. Gerätewechsel)
let nav = match ctx.navigator() {
Some(n) => n,
None => {
let has9 = ctx.has_qualifier("DTM", 0, "9");
let has60 = ctx.has_qualifier("DTM", 0, "60");
return match (has9, has60) {
(ConditionResult::True, ConditionResult::True) => ConditionResult::True,
(ConditionResult::Unknown, _) | (_, ConditionResult::Unknown) => {
ConditionResult::Unknown
}
_ => ConditionResult::False,
};
}
};
let sg10_count = nav.group_instance_count(&["SG9", "SG10"]);
for i in 0..sg10_count {
let dtms = nav.find_segments_in_group("DTM", &["SG9", "SG10"], i);
let has9 = dtms.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "9")
});
let has60 = dtms.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "60")
});
if has9 && has60 {
return ConditionResult::True;
}
}
ConditionResult::False
}
/// [131] wenn RFF+AGK (Konfigurations-ID) nicht vorhanden
fn evaluate_131(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.lacks_qualifier("RFF", 0, "AGK")
}
/// [132] wenn LOC+172 (Identifikationsangabe) DE3225 nicht vorhanden
// REVIEW: Checks if LOC+172 is absent entirely, or if DE3225 (elements[1][0], the Bezeichnung) is empty/missing within the LOC+172 segment. Returns True in either case as the condition is 'DE3225 nicht vorhanden'. (medium confidence)
fn evaluate_132(&self, ctx: &EvaluationContext) -> ConditionResult {
let locs = ctx.find_segments_with_qualifier("LOC", 0, "172");
match locs.first() {
None => ConditionResult::True,
Some(loc) => {
let de3225_absent = loc
.elements
.get(1)
.and_then(|e| e.first())
.map(|v| v.is_empty())
.unwrap_or(true);
ConditionResult::from(de3225_absent)
}
}
}
/// [133] Wenn innerhalb desselben LIN-Segments neben diesem Segment (SG10 DTM+7 Nutzungszeitpunkt) noch das SG10 DTM+9 (Ablesedatum) mit dem Code 102 im DE2379 vorhanden ist, darf der Wert der Differenz zwi...
// REVIEW: Checks that when DTM+7 (Nutzungszeitpunkt, format 303 CCYYMMDDHHMM) and DTM+9 (Ablesedatum, format 102 CCYYMMDD) co-exist in the same SG10 instance, the CCYYMMDD date part of DTM+7 must equal or be at most 1 calendar day different from DTM+9. Uses a Julian Day Number formula for correct cross-month/year date arithmetic. MSCONS SG10 path assumed as SG5.SG6.SG10 based on the tx_group=SG5 and SG6 being the measurement period container; medium confidence due to path uncertainty. (medium confidence)
fn evaluate_133(&self, ctx: &EvaluationContext) -> ConditionResult {
let nav = match ctx.navigator() {
Some(n) => n,
None => return ConditionResult::Unknown,
};
fn date_to_days(s: &str) -> Option<i64> {
if s.len() < 8 {
return None;
}
let y: i64 = s[..4].parse().ok()?;
let m: i64 = s[4..6].parse().ok()?;
let d: i64 = s[6..8].parse().ok()?;
if m < 1 || m > 12 || d < 1 || d > 31 {
return None;
}
let a = (14 - m) / 12;
let yy = y + 4800 - a;
let mm = m + 12 * a - 3;
Some(d + (153 * mm + 2) / 5 + 365 * yy + yy / 4 - yy / 100 + yy / 400 - 32045)
}
let sg10_count = nav.group_instance_count(&["SG5", "SG6", "SG9", "SG10"]);
if sg10_count == 0 {
return ConditionResult::Unknown;
}
for i in 0..sg10_count {
let dtms = nav.find_segments_in_group("DTM", &["SG5", "SG6", "SG9", "SG10"], i);
let dtm7_val = dtms
.iter()
.find(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "7")
})
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
.cloned();
let dtm9_val = dtms
.iter()
.find(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "9")
&& s.elements
.first()
.and_then(|e| e.get(2))
.is_some_and(|v| v == "102")
})
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
.cloned();
let (v7, v9) = match (dtm7_val, dtm9_val) {
(Some(v7), Some(v9)) if !v7.is_empty() && !v9.is_empty() => (v7, v9),
_ => continue,
};
let date7 = if v7.len() >= 8 { &v7[..8] } else { continue };
let date9 = if v9.len() >= 8 { &v9[..8] } else { v9.as_str() };
let days7 = match date_to_days(date7) {
Some(d) => d,
None => return ConditionResult::Unknown,
};
let days9 = match date_to_days(date9) {
Some(d) => d,
None => return ConditionResult::Unknown,
};
let diff = (days7 - days9).abs();
if diff > 1 {
return ConditionResult::False;
}
}
ConditionResult::True
}
/// [134] Wenn SG10 DTM+9 DE2379 in demselben Segment mit Wert 102 vorhanden
fn evaluate_134(&self, ctx: &EvaluationContext) -> ConditionResult {
ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "102"))
}
/// [135] Der Wert an der Stelle CCYYMMDD muss ≤ dem Wert an der Stelle CCYYMMDD im DE2380 des DTM+137 sein
// HAND-EDITED: subject is the current DTM's DE2380 (via ctx.resolved_value), not a
// guessed DTM+9. Compares the CCYYMMDD prefix (first 8 chars) lexicographically,
// which matches both format 303 (CCYYMMDDhhmm+ZZ) and 102 (CCYYMMDD) prefixes.
fn evaluate_135(&self, ctx: &EvaluationContext) -> ConditionResult {
let current = match ctx.resolved_value {
Some(v) if v.len() >= 8 => &v[..8],
_ => return ConditionResult::Unknown,
};
let threshold: String = match ctx
.find_segments_with_qualifier("DTM", 0, "137")
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
.filter(|v| v.len() >= 8)
{
Some(v) => v.chars().take(8).collect(),
None => return ConditionResult::Unknown,
};
ConditionResult::from(current <= threshold.as_str())
}
/// [138] Wenn es sich um eine Korrekturenergiemenge auf einen Wert aus einem iMS handelt
/// EXTERNAL: Requires context from outside the message.
fn evaluate_138(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("is_correction_energy_from_ims")
}
/// [141] Wenn MP-ID in SG2 NAD+MR in der Rolle MGV
/// EXTERNAL: Requires context from outside the message.
fn evaluate_141(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_mgv")
}
/// [142] wenn im DE3155 in demselben COM der Code EM vorhanden ist
fn evaluate_142(&self, ctx: &EvaluationContext) -> ConditionResult {
let coms = ctx.find_segments("COM");
ConditionResult::from(coms.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.get(1))
.is_some_and(|v| v == "EM")
}))
}
/// [143] wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
fn evaluate_143(&self, ctx: &EvaluationContext) -> ConditionResult {
let coms = ctx.find_segments("COM");
ConditionResult::from(coms.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.get(1))
.is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
}))
}
/// [144] Wenn Wert in SG7 RFF+AGK DE1154 (Konfigurations-ID) vorhanden
fn evaluate_144(&self, ctx: &EvaluationContext) -> ConditionResult {
let rffs = ctx.find_segments_with_qualifier("RFF", 0, "AGK");
match rffs.first() {
None => ConditionResult::False,
Some(rff) => {
let has_value = rff
.elements
.first()
.and_then(|e| e.get(1))
.map(|v| !v.is_empty())
.unwrap_or(false);
ConditionResult::from(has_value)
}
}
}
/// [145] Wenn in derselben S9 LIN das SG10 DTM+163 (Beginn Messperiode) nicht vorhanden ist.
// REVIEW: Checks absence of DTM+163 (Beginn Messperiode) in the message. The condition references the same SG10 context within the enclosing LIN group; without the exact MSCONS group path this is a message-wide approximation. Note: DTM+163 also appears in SG6 (Übertragungszeitraum), so this may yield false-negatives for the SG10-scoped intent. (medium confidence)
fn evaluate_145(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.lacks_qualifier("DTM", 0, "163")
}
/// [146] Wenn es bei dem zu übermittelnden Wert um einen Wert zu einem Zeitpunkt handelt.
// REVIEW: A point-in-time value (Wert zu einem Zeitpunkt) is signalled by the presence of DTM+7 (Nutzungszeitpunkt) in SG10. If this DTM qualifier is present, the transmitted value represents a single point in time rather than an interval. (medium confidence)
fn evaluate_146(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("DTM", 0, "7")
}
/// [147] Wenn in derselben S9 LIN das SG10 DTM+7 (Nutzungszeitpunkt) nicht vorhanden ist.
// REVIEW: Checks absence of DTM+7 (Nutzungszeitpunkt) in the message, approximating the SG10-scoped check within the enclosing LIN group. Absence means the value is not a point-in-time value. (medium confidence)
fn evaluate_147(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.lacks_qualifier("DTM", 0, "7")
}
/// [148] Wenn es bei dem zu übermittelnden Wert um einen Wert in einem Zeitintervall handelt.
// REVIEW: An interval value (Wert in einem Zeitintervall) is signalled by the presence of DTM+163 (Beginn Messperiode) in SG10. If Beginn Messperiode is present, the value spans a time interval rather than being a single point. (medium confidence)
fn evaluate_148(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("DTM", 0, "163")
}
/// [149] Wenn in derselben S9 LIN das SG10 DTM+163 (Beginn Messperiode) vorhanden ist.
// REVIEW: Checks presence of DTM+163 (Beginn Messperiode) — the direct inverse of condition 145. Message-wide approximation of the SG10-scoped intent within the enclosing LIN group. (medium confidence)
fn evaluate_149(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("DTM", 0, "163")
}
/// [150] Wenn BGM+Z69 (Redispatch tägliche Ausfallarbeitsüberführungszeitreihe) vorhanden.
fn evaluate_150(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualifier("BGM", 0, "Z69")
}
/// [490] wenn Wert in diesem DE, an der Stelle CCYYMMDDHHMM ein Zeitpunkt aus dem angegeben Zeitraum der Tabelle Kapitel 3.5 „Übersicht gesetzliche deutsche Sommerzeit (MESZ)“ der Spalten: „Sommerzei...
fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
match ctx.resolved_value {
Some(val) => is_mesz_utc(val),
None => ConditionResult::False,
}
}
/// [491] wenn Wert in diesem DE, an der Stelle CCYYMMDDHHMM ein Zeitpunkt aus dem angegeben Zeitraum der Tabelle Kapitel 3.6 „Übersicht gesetzliche deutsche Zeit (MEZ)“ der Spalten: „Winterzeit (MEZ)...
// REVIEW: Checks whether the datetime value falls within German winter time (MEZ, UTC+1) using the is_mez_utc helper. Medium confidence because 'diesem DE' is context-dependent — without knowing the exact field the condition is applied to in the AHB table, we check the first DTM value available. The is_mez_utc helper applies the EU DST rule to determine MEZ vs MESZ. (medium confidence)
fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
match ctx.resolved_value {
Some(val) => is_mez_utc(val),
None => ConditionResult::False,
}
}
/// [492] wenn MP-ID in NAD+MR aus Sparte Strom
/// EXTERNAL: Requires context from outside the message.
fn evaluate_492(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_electricity_sector")
}
/// [493] wenn MP-ID in NAD+MR aus Sparte Gas
/// EXTERNAL: Requires context from outside the message.
fn evaluate_493(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_gas_sector")
}
/// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt.
// HAND-EDITED: subject is the current DTM's DE2380 (via ctx.resolved_value), not a
// guessed other qualifier. Lexicographic compare is valid for fixed-width
// CCYYMMDDHHMM+ZZ (format 303) and CCYYMMDD (format 102) when compared prefix-wise.
fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
let current = match ctx.resolved_value {
Some(v) if !v.is_empty() => v,
_ => return ConditionResult::Unknown,
};
let threshold = match ctx
.find_segments_with_qualifier("DTM", 0, "137")
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(v) if !v.is_empty() => v.clone(),
_ => return ConditionResult::Unknown,
};
ConditionResult::from(current <= threshold.as_str())
}
/// [495] Der Zeitpunkt muss ≤ dem Wert im DE2380 des DTM+137 sein
// HAND-EDITED: subject is the current DTM's DE2380 (via ctx.resolved_value), not a
// guessed other qualifier. Matches the invoic_conditions pattern.
fn evaluate_495(&self, ctx: &EvaluationContext) -> ConditionResult {
let current = match ctx.resolved_value {
Some(v) if !v.is_empty() => v,
_ => return ConditionResult::Unknown,
};
let threshold = match ctx
.find_segments_with_qualifier("DTM", 0, "137")
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(v) if !v.is_empty() => v.clone(),
_ => return ConditionResult::Unknown,
};
ConditionResult::from(current <= threshold.as_str())
}
/// [501] Hinweis: Es sind nur die Werte erlaubt, die in der EDI@Energy Codeliste der OBIS-Kennzahlen und Medien mit dem entsprechenden Prüfidentifikator versehen sind.
fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Informational note about allowed OBIS codes — always applies unconditionally
ConditionResult::True
}
/// [502] Hinweis: Einmal für die Energiemenge von Beginn des Kalenderjahres bis zum Lieferbeginn und bis zu zweimal für die zwei höchsten Monatsleistungswerte (wegen KAV) von Beginn des Kalenderjahres bi...
fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Informational note about cardinality (once for energy from start of calendar year,
// up to twice for two highest monthly peak values due to KAV) — always applies unconditionally
ConditionResult::True
}
/// [506] Hinweis: Nur bei Einspeisemengen und bei Gas zur stündlichen Energiedatenübermittlung
fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Informational note — only for feed-in quantities and gas hourly energy data transmission
// This is an informational annotation, not a boolean predicate — always applies
ConditionResult::True
}
/// [509] Hinweis: Falls es sich um eine Korrekturenergiemenge handelt, ist hier die Referenz auf die MSCONS anzugeben, in der der Zählerstand vorab übermittelt wurde.
fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Informational note — for correction energy amounts, reference the prior MSCONS
// in which the meter reading was transmitted. Informational annotation, always applies.
ConditionResult::True
}
/// [510] Hinweis: Verwendung der ID der Messlokation
fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der ID der Messlokation — informational note, always applies
ConditionResult::True
}
/// [511] Hinweis: Verwendung der ID des MaBiS-ZP
fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der ID des MaBiS-ZP — informational note, always applies
ConditionResult::True
}
/// [512] Hinweis: Verwendung der Bilanzkreisbezeichnung
fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der Bilanzkreisbezeichnung — informational note, always applies
ConditionResult::True
}
/// [513] Hinweis: Verwendung der Bezeichnung des Bilanzierungsgebietes
fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der Bezeichnung des Bilanzierungsgebietes — informational note, always applies
ConditionResult::True
}
/// [514] Hinweis: Verwendung der ID der Marktlokation
fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der ID der Marktlokation — informational note, always applies
ConditionResult::True
}
/// [515] Hinweis: Verwendung der Profilbezeichnung
fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der Profilbezeichnung — informational note, always applies
ConditionResult::True
}
/// [516] Hinweis: Verwendung der Bezeichnung der Profilschar
fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der Bezeichnung der Profilschar — informational note, always applies
ConditionResult::True
}
/// [518] Hinweis: Verwendung der ID der Tranche
fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der ID der Tranche — informational note, always applies
ConditionResult::True
}
/// [519] Hinweis: Nur wenn der gemessene Lastgang der Messlokation nicht dem Lastgang der Marktlokation 1:1 entspricht.
fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Nur wenn der gemessene Lastgang der Messlokation nicht dem Lastgang der Marktlokation 1:1 entspricht — informational note, always applies
ConditionResult::True
}
/// [520] Hinweis: Wenn es sich um eine 1:1 Beziehung zwischen Messlokation und Marktlokation handelt und der gemessene Lastgang der Messlokation dem Lastgang der Marktlokation 1:1 entspricht, oder wenn der ...
fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wenn es sich um eine 1:1 Beziehung zwischen Messlokation und Marktlokation handelt... — informational note, always applies
ConditionResult::True
}
/// [522] Hinweis: Nur für die Übermittlung der Korrekturenergiemengen im Zeitintervall zwischen zwei Messwerten.
fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Nur für die Übermittlung der Korrekturenergiemengen im Zeitintervall zwischen zwei Messwerten.
ConditionResult::True
}
/// [523] Hinweis: Nur für die Übermittlung der Energiemenge im Zeitintervall zwischen zwei Messwerten vor der Netznutzungsabrechnung.
fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Nur für die Übermittlung der Energiemenge im Zeitintervall zwischen zwei Messwerten vor der Netznutzungsabrechnung.
ConditionResult::True
}
/// [524] Hinweis: Nur, wenn es sich um die Übermittlung von Abrechnungsbrennwert und Z-Zahl für den vom Lieferanten über eine Geschäftsdatenanfrage angeforderten Zeitraum handelt.
fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Nur, wenn es sich um die Übermittlung von Abrechnungsbrennwert und Z-Zahl für den vom Lieferanten über eine Geschäftsdatenanfrage angeforderten Zeitraum handelt.
ConditionResult::True
}
/// [525] Hinweis: Nur für die Übermittlung der Energiemenge im Zeitintervall für eine Marktlokation ohne Messlokation (Pauschalanlage) wenn eines der Ereignisse aus Kapitel 4.2 eingetreten ist.
fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Nur für die Übermittlung der Energiemenge im Zeitintervall für eine Marktlokation ohne Messlokation (Pauschalanlage) wenn eines der Ereignisse aus Kapitel 4.2 eingetreten ist.
ConditionResult::True
}
/// [526] Hinweis: Wert aus BGM+Z24 DE1004 der ORDERS mit der die Allokationsliste bestellt wurde.
fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wert aus BGM+Z24 DE1004 der ORDERS mit der die Allokationsliste bestellt wurde.
ConditionResult::True
}
/// [528] Hinweis: Wert aus BGM+Z28 DE1004 der ORDERS mit der die Anforderung von Messwerten erfolgt ist.
fn evaluate_528(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wert aus BGM+Z28 DE1004 der ORDERS mit der die Anforderung von Messwerten erfolgt ist.
// Informational note, always applies
ConditionResult::True
}
/// [529] Hinweis: Wert aus BGM+7 DE1004 der ORDERS mit der die Anforderung von Messwerten erfolgt ist.
fn evaluate_529(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wert aus BGM+7 DE1004 der ORDERS mit der die Anforderung von Messwerten erfolgt ist.
// Informational note, always applies
ConditionResult::True
}
/// [530] Hinweis: Wert aus SG4 IDE+24 DE7402 der UTILMD mit dem der Sender der MSCONS die vorherigen Stammdaten mittels UTILMD übermittelt hat.
fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wert aus SG4 IDE+24 DE7402 der UTILMD mit dem der Sender der MSCONS die vorherigen Stammdaten mittels UTILMD übermittelt hat.
// Informational note, always applies
ConditionResult::True
}
/// [531] Hinweis: Wert aus BGM+7 DE1004 der MSCONS mit der der Zählerstand übermittelt wurde.
fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wert aus BGM+7 DE1004 der MSCONS mit der der Zählerstand übermittelt wurde.
// Informational note, always applies
ConditionResult::True
}
/// [532] Hinweis: Wert aus BGM+7/Z27/Z28/270/Z41/Z42/Z85 DE1004 der MSCONS Nachricht, die storniert wird
fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wert aus BGM+7/Z27/Z28/270/Z41/Z42/Z85 DE1004 der MSCONS Nachricht, die storniert wird
// Informational note, always applies
ConditionResult::True
}
/// [535] Hinweis: Verwendung der ID des Netzkoppelpunktes Strom/Gas
fn evaluate_535(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der ID des Netzkoppelpunktes Strom/Gas — informational note, always applies
ConditionResult::True
}
/// [538] Hinweis: Die Referenz auf die ORDERS ist nur dann anzugeben, wenn diese Werte vom Empfänger auch ursprünglich mittels ORDERS angefragt wurden.
fn evaluate_538(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Die Referenz auf die ORDERS ist nur dann anzugeben, wenn diese Werte vom Empfänger auch ursprünglich mittels ORDERS angefragt wurden — informational note, always applies
ConditionResult::True
}
/// [541] Hinweis: Ein Korrekturgrund ist anzugeben, wenn: 1. ein bereits an den MP übermittelter vorläufiger Wert nach Stornierung durch einen Ersatzwert ersetzt wird, oder 2. ein bereits an den MP über...
fn evaluate_541(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Ein Korrekturgrund ist anzugeben in bestimmten Ersatzwert/Stornierung-Szenarien — informational note, always applies
ConditionResult::True
}
/// [543] Hinweis: Wert aus BGM+Z23 DE1004 der ORDERS mit der die bilanzierte Menge bestellt wurde.
fn evaluate_543(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wert aus BGM+Z23 DE1004 der ORDERS mit der die bilanzierte Menge bestellt wurde — informational note, always applies
ConditionResult::True
}
/// [544] Hinweis: Bei einer Mengenaufteilung (z. B. Aufgrund einer Abgrenzung) für SG6 LOC+172 muss für den frühesten angegebenen Zeitpunkt zum Beginn des Zeitintervalls (über alle Wiederholungen der L...
fn evaluate_544(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Bei einer Mengenaufteilung für SG6 LOC+172 muss für den frühesten Zeitpunkt zu jeder OBIS-Kennziffer ein Zählerstand vorhanden sein — informational note, always applies
ConditionResult::True
}
/// [545] Hinweis: Bei einer Mengenaufteilung (z. B. Aufgrund einer Abgrenzung) für SG6 LOC+172 muss für den spätesten angegebenen Zeitpunkt zum Ende des Zeitintervalls (über alle Wiederholungen der LIN-...
fn evaluate_545(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: informational note about Mengenaufteilung and Zählerstand requirement — always applies
ConditionResult::True
}
/// [546] Hinweis: Eine Referenz auf die Stammdatenänderung des Gerätewechsels ist immer anzugeben, wenn diese dem Sender vorliegt.
fn evaluate_546(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: informational note about referencing master data changes for device replacements — always applies
ConditionResult::True
}
/// [547] Hinweis: Der Code 270 ist nur zu nutzen, wenn ein Lieferschein, der vor dem 1.4.2021 erstellt wurde, storniert wird.
fn evaluate_547(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: informational note — code 270 only for cancelling delivery notes created before 1.4.2021 — always applies
ConditionResult::True
}
/// [551] Hinweis: Ein Korrekturgrund ist anzugeben, wenn: 1. ein bereits an den MP übermittelter vorläufiger Wert durch einen Ersatzwert ersetzt wird, oder 2. ein bereits an den MP übermittelter Ersatzw...
fn evaluate_551(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: informational note describing when a Korrekturgrund must be provided — always applies
ConditionResult::True
}
/// [553] Hinweis: Wert aus BGM+Z34 DE1004 der ORDERS mit der die Reklamation von Werten erfolgt ist
fn evaluate_553(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: informational note — value from BGM+Z34 DE1004 of the ORDERS used for value complaint — always applies
ConditionResult::True
}
/// [554] Hinweis: Verwendung der ID der Technischen Ressource
fn evaluate_554(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der ID der Technischen Ressource — informational note, always applies
ConditionResult::True
}
/// [556] Hinweis: Wert aus BGM+Z45 DE1004 der ORDERS mit der die Anforderung der Ausfallarbeit durch den anfNB erfolgt ist.
fn evaluate_556(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wert aus BGM+Z45 DE1004 der ORDERS — informational note, always applies
ConditionResult::True
}
/// [557] Hinweis: Die Referenz auf die ursprüngliche MSCONS ist anzugeben, wenn es sich um die Übermittlung eines Gegenvorschlags durch den BTR handelt.
fn evaluate_557(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Referenz auf ursprüngliche MSCONS bei Gegenvorschlag — informational note, always applies
ConditionResult::True
}
/// [558] Hinweis: Wert aus BGM+Z45 DE1004 der MSCONS auf die sich die Übermittlung des Gegenvorschlags durch den BTR bezieht.
fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wert aus BGM+Z45 DE1004 der MSCONS bei Gegenvorschlag — informational note, always applies
ConditionResult::True
}
/// [559] Hinweis: Ein Korrekturgrund ist anzugeben, wenn: 1. ein bereits an den MP übermittelter vorläufiger Wert nach Stornierung durch einen Ersatzwert ersetzt wird, oder 2. ein bereits an den MP über...
fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Korrekturgrund bei Ersatz/Stornierung von Werten — informational note, always applies
ConditionResult::True
}
/// [560] Hinweis: Ein Korrekturgrund ist anzugeben, wenn: 1. ein bereits an den MP übermittelter vorläufiger Wert durch einen Ersatzwert ersetzt wird, oder 2. ein bereits an den MP übermittelter Ersatzw...
fn evaluate_560(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Ein Korrekturgrund ist anzugeben, wenn ein vorläufiger/Ersatz/wahrer Wert ersetzt wird — informational note, always applies
ConditionResult::True
}
/// [566] Hinweis: Es sind nur die Werte erlaubt, die im vorherigen Stammdatenaustausch zu diesem Meldepunkt vom MSB zum Zeitpunkt übermittelt wurden.
fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Es sind nur die Werte erlaubt, die im vorherigen Stammdatenaustausch übermittelt wurden — informational note, always applies
ConditionResult::True
}
/// [567] Hinweis: Es ist die Konfigurations-ID anzugeben, die im vorherigen Stammdatenaustausch kommuniziert wurde.
fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Es ist die Konfigurations-ID anzugeben, die im vorherigen Stammdatenaustausch kommuniziert wurde — informational note, always applies
ConditionResult::True
}
/// [568] Hinweis: Verwendung ist nur zulässig, wenn es sich um 1:n Beziehung zwischen Markt- und Messlokation handelt und auf Ebene der Messlokation unterschiedliche Ersatzwertbildungsverfahren verwendet u...
fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung nur zulässig bei 1:n Beziehung zwischen Markt- und Messlokation mit unterschiedlichen Ersatzwertbildungsverfahren — informational note, always applies
ConditionResult::True
}
/// [569] Hinweis: Bei mehreren Zählerständen einer Messlokation (z. B. HT/NT) ist diese Zeitangabe zu nutzen und eine Wiederholung das SG9 LIN durchzuführen.
fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Bei mehreren Zählerständen einer Messlokation (z. B. HT/NT) ist diese Zeitangabe zu nutzen und SG9 LIN zu wiederholen — informational note, always applies
ConditionResult::True
}
/// [570] Hinweis: Verwendung ist nur zulässig, wenn es sich um 1:n Beziehung zwischen Markt- und Messlokation handelt und auf Ebene der Messlokation unterschiedliche Gründe für die Ersatzwertbildung vorl...
fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung ist nur zulässig, wenn es sich um 1:n Beziehung zwischen Markt- und Messlokation handelt und auf Ebene der Messlokation unterschiedliche Gründe für die Ersatzwertbildung vorliegen und kommuniziert wurden.
ConditionResult::True
}
/// [571] Hinweis: Verwendung ist nur zulässig, wenn es sich um 1:n Beziehung handelt und auf Ebene der Netzkopplungspunkte unterschiedliche Gründe für die Ersatzwertbildung vorliegen und kommuniziert wur...
fn evaluate_571(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung ist nur zulässig, wenn es sich um 1:n Beziehung handelt und auf Ebene der Netzkopplungspunkte unterschiedliche Gründe für die Ersatzwertbildung vorliegen und kommuniziert wurden.
ConditionResult::True
}
/// [572] Hinweis: Verwendung ist nur zulässig, wenn es sich um 1:n Beziehung handelt und auf Ebene der Netzkopplungspunkte unterschiedliche Ersatzwertbildungsverfahren vorliegen und kommuniziert wurden.
fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung ist nur zulässig, wenn es sich um 1:n Beziehung handelt und auf Ebene der Netzkopplungspunkte unterschiedliche Ersatzwertbildungsverfahren vorliegen und kommuniziert wurden.
ConditionResult::True
}
/// [573] Hinweis: Eine Energiemenge in der Sparte Gas ist gemäß DVGW G685 Arbeitsblatt 4 Kapitel 5.3 auf ganze Kilowattstunden zu runden.
fn evaluate_573(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Eine Energiemenge in der Sparte Gas ist gemäß DVGW G685 Arbeitsblatt 4 Kapitel 5.3 auf ganze Kilowattstunden zu runden.
ConditionResult::True
}
/// [574] Hinweis: Wert aus BGM DE1004 der ORDERS mit der die Bestellung der Werte nach Typ 2 erfolg ist
fn evaluate_574(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wert aus BGM DE1004 der ORDERS mit der die Bestellung der Werte nach Typ 2 erfolg ist
ConditionResult::True
}
/// [575] Hinweis: Verwendung der ID der Netzlokation
fn evaluate_575(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der ID der Netzlokation — informational note, always applies
ConditionResult::True
}
/// [576] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Es darf nur eine Information im DE3148 übermittelt werden — informational note, always applies
ConditionResult::True
}
/// [577] Hinweis: Dieser Code ist auch zu verwenden, wenn aufgrund der Beendigung einer Messlokation (Stilllegung) die Beendigung der Marktlokation (Stilllegung) zu unterschiedlichen Zeitpunkten erfolgt, da...
fn evaluate_577(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Informational note about Messlokation/Marktlokation termination timing — always applies
ConditionResult::True
}
/// [902] Format: Möglicher Wert: ≥ 0
fn evaluate_902(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">=", 0.0))
}
/// [904] Format: genau 16 Stellen
// REVIEW: Format condition requiring exactly 16 characters. Applied to QTY value (most common numeric format condition target in MSCONS). Confidence is medium because the exact target segment/element is not specified in the condition text — QTY is the most likely candidate in MSCONS context. (medium confidence)
fn evaluate_904(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_exact_length(val, 16))
}
/// [905] Format: max. 3 Stellen
fn evaluate_905(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_max_integer_digits(val, 3))
}
/// [906] Format: max. 3 Nachkommastellen
fn evaluate_906(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 3))
}
/// [907] Format: max. 4 Nachkommastellen
fn evaluate_907(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 4))
}
/// [908] Format: Mögliche Werte: 1 bis n
fn evaluate_908(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">=", 1.0))
}
/// [909] Format: Mögliche Werte: 0 bis n
fn evaluate_909(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">=", 0.0))
}
/// [910] Format: Möglicher Wert: < 0 oder ≥ 0
fn evaluate_910(&self, ctx: &EvaluationContext) -> ConditionResult {
let qty_segments = ctx.find_segments("QTY");
let val = qty_segments
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
.map(|s| s.as_str())
.unwrap_or("");
if val.is_empty() {
return ConditionResult::Unknown;
}
match val.parse::<f64>() {
Ok(_) => ConditionResult::True,
Err(_) => ConditionResult::False,
}
}
/// [917] Format: max. 4 Vorkommastellen
fn evaluate_917(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_max_integer_digits(val, 4))
}
/// [918] Format: Zeichen aus dem über UNOC definierten Zeichensatz, wobei von den Buchstaben nur Großbuchstaben erlaubt sind.
// REVIEW: UNOC charset requires uppercase-only alphabetics. Checks all alphabetic characters in FTX text fields (C108, elements[3], components 0..4) are uppercase. Non-alphabetic characters (digits, punctuation) are permitted. Medium confidence — UNOC validation approximated as uppercase-only alphabetic check without full ISO 8859-1 codepoint enumeration. (medium confidence)
fn evaluate_918(&self, ctx: &EvaluationContext) -> ConditionResult {
let ftx_segments = ctx.find_segments("FTX");
for seg in &ftx_segments {
if let Some(c108) = seg.elements.get(3) {
for component in c108 {
let s = component.as_str();
if s.is_empty() {
continue;
}
if !s.chars().all(|c| !c.is_alphabetic() || c.is_uppercase()) {
return ConditionResult::False;
}
}
}
}
ConditionResult::True
}
/// [922] Format: TR-ID
// REVIEW: Format: TR-ID — validates that the value is a valid Transaktionsreferenz-ID (1–35 alphanumeric characters) using validate_tr_id. In MSCONS, the document/transaction ID is typically found in BGM elements[1][0] (the document number composite). Medium confidence because the exact DE this format condition is applied to depends on the AHB field context, which could alternatively be RFF or another reference segment. (medium confidence)
fn evaluate_922(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("BGM", 1, 0, validate_tr_id)
}
/// [925] Format: max. 5 Nachkommastellen
fn evaluate_925(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 5))
}
/// [931] Format: ZZZ = +00
fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("DTM", 0, 1, validate_timezone_utc)
}
/// [932] Format: HHMM = 2200
fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
}
/// [933] Format: HHMM = 2300
fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
}
/// [934] Format: HHMM = 0400
fn evaluate_934(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0400"))
}
/// [935] Format: HHMM = 0500
fn evaluate_935(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0500"))
}
/// [937] Format: keine Nachkommastelle
fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 0))
}
/// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("COM", 0, 0, validate_email)
}
/// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("COM", 0, 0, validate_phone)
}
/// [950] Format: Marktlokations-ID
fn evaluate_950(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check_qualified("LOC", 0, "Z16", 1, 0, validate_malo_id)
}
/// [951] Format: Zählpunktbezeichnung
// REVIEW: Format: Zählpunktbezeichnung — 33 alphanumeric characters. In MSCONS, Zählpunktbezeichnung typically appears in LOC+Z19. Using validate_zahlpunkt helper. (medium confidence)
fn evaluate_951(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check_qualified("LOC", 0, "Z19", 1, 0, validate_zahlpunkt)
}
/// [960] Format: Netzlokations-ID
// HAND-EDITED: a Netzlokations-ID is "E" + ten alphanumerics (`E1688117482`),
// not MaLo-shaped; `validate_malo_id` rejected every real one (#173).
fn evaluate_960(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check_qualified("LOC", 0, "Z17", 1, 0, validate_nelo_id)
}
/// [2001] Segmentgruppe ist nur einmal je UNH anzugeben
fn evaluate_2001(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Segmentgruppe ist nur einmal je UNH anzugeben — cardinality annotation, always applies
ConditionResult::True
}
/// [2002] Segmentgruppe ist bis zu drei Mal je SG5 NAD+DP anzugeben
fn evaluate_2002(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Segmentgruppe ist bis zu drei Mal je SG5 NAD+DP anzugeben — cardinality annotation, always applies
ConditionResult::True
}
/// [2003] Segmentgruppe ist genau zwei Mal je SG9 LIN anzugeben
fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Segmentgruppe ist genau zwei Mal je SG9 LIN anzugeben — cardinality annotation, always applies
ConditionResult::True
}
}