// <auto-generated>
// Generated by automapper-generator generate-conditions
// AHB: xml-migs-and-ahbs/FV2510/IFTSTA_AHB_2_0h_Fehlerkorrektur_20250623.xml
// Generated: 2026-03-12T10:40:40Z
// </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 IFTSTA FV2510.
pub struct IftstaConditionEvaluatorFV2510 {
// External condition IDs that require runtime context.
external_conditions: std::collections::HashSet<u32>,
}
impl Default for IftstaConditionEvaluatorFV2510 {
fn default() -> Self {
let mut external_conditions = std::collections::HashSet::new();
external_conditions.insert(1);
external_conditions.insert(7);
external_conditions.insert(8);
external_conditions.insert(10);
external_conditions.insert(16);
external_conditions.insert(17);
external_conditions.insert(20);
external_conditions.insert(26);
external_conditions.insert(27);
external_conditions.insert(28);
external_conditions.insert(29);
external_conditions.insert(72);
external_conditions.insert(76);
external_conditions.insert(86);
external_conditions.insert(114);
external_conditions.insert(115);
external_conditions.insert(117);
external_conditions.insert(492);
external_conditions.insert(493);
Self {
external_conditions,
}
}
}
impl ConditionEvaluator for IftstaConditionEvaluatorFV2510 {
fn message_type(&self) -> &str {
"IFTSTA"
}
fn format_version(&self) -> &str {
"FV2510"
}
fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
match condition {
1 => self.evaluate_1(ctx),
3 => self.evaluate_3(ctx),
4 => self.evaluate_4(ctx),
5 => self.evaluate_5(ctx),
6 => self.evaluate_6(ctx),
7 => self.evaluate_7(ctx),
8 => self.evaluate_8(ctx),
10 => self.evaluate_10(ctx),
16 => self.evaluate_16(ctx),
17 => self.evaluate_17(ctx),
18 => self.evaluate_18(ctx),
19 => self.evaluate_19(ctx),
20 => self.evaluate_20(ctx),
23 => self.evaluate_23(ctx),
26 => self.evaluate_26(ctx),
27 => self.evaluate_27(ctx),
28 => self.evaluate_28(ctx),
29 => self.evaluate_29(ctx),
30 => self.evaluate_30(ctx),
31 => self.evaluate_31(ctx),
32 => self.evaluate_32(ctx),
33 => self.evaluate_33(ctx),
43 => self.evaluate_43(ctx),
44 => self.evaluate_44(ctx),
45 => self.evaluate_45(ctx),
46 => self.evaluate_46(ctx),
47 => self.evaluate_47(ctx),
48 => self.evaluate_48(ctx),
49 => self.evaluate_49(ctx),
50 => self.evaluate_50(ctx),
51 => self.evaluate_51(ctx),
52 => self.evaluate_52(ctx),
53 => self.evaluate_53(ctx),
54 => self.evaluate_54(ctx),
55 => self.evaluate_55(ctx),
56 => self.evaluate_56(ctx),
57 => self.evaluate_57(ctx),
58 => self.evaluate_58(ctx),
59 => self.evaluate_59(ctx),
60 => self.evaluate_60(ctx),
61 => self.evaluate_61(ctx),
62 => self.evaluate_62(ctx),
63 => self.evaluate_63(ctx),
64 => self.evaluate_64(ctx),
65 => self.evaluate_65(ctx),
66 => self.evaluate_66(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),
76 => self.evaluate_76(ctx),
77 => self.evaluate_77(ctx),
78 => self.evaluate_78(ctx),
79 => self.evaluate_79(ctx),
80 => self.evaluate_80(ctx),
83 => self.evaluate_83(ctx),
84 => self.evaluate_84(ctx),
85 => self.evaluate_85(ctx),
86 => self.evaluate_86(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),
103 => self.evaluate_103(ctx),
107 => self.evaluate_107(ctx),
114 => self.evaluate_114(ctx),
115 => self.evaluate_115(ctx),
117 => self.evaluate_117(ctx),
118 => self.evaluate_118(ctx),
119 => self.evaluate_119(ctx),
120 => self.evaluate_120(ctx),
121 => self.evaluate_121(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),
136 => self.evaluate_136(ctx),
137 => self.evaluate_137(ctx),
138 => self.evaluate_138(ctx),
139 => self.evaluate_139(ctx),
140 => self.evaluate_140(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),
496 => self.evaluate_496(ctx),
501 => self.evaluate_501(ctx),
502 => self.evaluate_502(ctx),
503 => self.evaluate_503(ctx),
504 => self.evaluate_504(ctx),
505 => self.evaluate_505(ctx),
506 => self.evaluate_506(ctx),
510 => self.evaluate_510(ctx),
512 => self.evaluate_512(ctx),
519 => self.evaluate_519(ctx),
520 => self.evaluate_520(ctx),
521 => self.evaluate_521(ctx),
522 => self.evaluate_522(ctx),
523 => self.evaluate_523(ctx),
524 => self.evaluate_524(ctx),
525 => self.evaluate_525(ctx),
530 => self.evaluate_530(ctx),
531 => self.evaluate_531(ctx),
532 => self.evaluate_532(ctx),
533 => self.evaluate_533(ctx),
534 => self.evaluate_534(ctx),
535 => self.evaluate_535(ctx),
537 => self.evaluate_537(ctx),
538 => self.evaluate_538(ctx),
902 => self.evaluate_902(ctx),
903 => self.evaluate_903(ctx),
906 => self.evaluate_906(ctx),
911 => self.evaluate_911(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),
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),
961 => self.evaluate_961(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 | 3
| 4
| 5
| 6
| 7
| 8
| 10
| 16
| 17
| 18
| 19
| 20
| 23
| 26
| 27
| 28
| 29
| 30
| 31
| 32
| 33
| 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
| 76
| 77
| 78
| 79
| 80
| 83
| 84
| 85
| 86
| 91
| 92
| 93
| 94
| 95
| 96
| 97
| 98
| 99
| 100
| 101
| 103
| 107
| 114
| 115
| 117
| 118
| 119
| 120
| 121
| 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
| 490
| 491
| 492
| 493
| 494
| 495
| 496
| 501
| 502
| 503
| 504
| 505
| 506
| 510
| 512
| 519
| 520
| 521
| 522
| 523
| 524
| 525
| 530
| 531
| 532
| 533
| 534
| 535
| 537
| 538
| 902
| 903
| 906
| 911
| 931
| 932
| 933
| 934
| 935
| 939
| 940
| 950
| 951
| 960
| 961
)
}
}
impl IftstaConditionEvaluatorFV2510 {
/// [6] Wenn für das 3-Tupel (MaBiS-ZP, Betrachtungszeitraum, Version der Summenzeitreihe) dem BIKO der Prüfstatus vorliegt, so ist dieser immer zusammen mit dem Datenstatus zu übertragen.
// REVIEW: The condition states that when BIKO has a Prüfstatus for the 3-tuple (MaBiS-ZP, Betrachtungszeitraum, Version), it must always be transmitted together with the Datenstatus. Interpreted as: this condition is True when STS with Z03 category (Prüfstatus zur Summenzeitreihe) is present in the message, triggering the co-transmission requirement. The BIKO role context is implicit in the message structure. (medium confidence)
fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
// Condition is True when STS+Z03 (Prüfstatus zur Summenzeitreihe) is present,
// signalling that Datenstatus must also be transmitted alongside it.
ctx.has_qualifier("STS", 0, "Z03")
}
/// [7] Wenn der Datenstatus "Abgerechnete Daten" bzw. "Abgerechnete Daten Korrektur-BKA" nicht vorhanden.
/// EXTERNAL: Requires the authoritative IFTSTA Datenstatus codelist to map "Abgerechnete Daten"/"…-BKA" to STS+Z04 DE1131 codes.
fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external
.evaluate("iftsta_datenstatus_abgerechnet_absent")
}
/// [8] Wenn der Datenstatus einer NZR vom BIKO an NB nicht vorhanden.
/// EXTERNAL: Requires context from outside the message.
fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("datenstatus_nzr_biko_to_nb_missing")
}
/// [23] Wenn SG15 STS+Z25+Z31+A06 / A09 / A10 / A11 / A13 / A14 / A15 / A16 vorhanden.
fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
// STS+Z25+Z31+A06/A09/A10/A11/A13/A14/A15/A16
// Per segment reference for 'Status des Lieferscheins':
// elements[0][0] = Z25 (Statuskategorie)
// elements[1][0] = Z31 (Status, Code)
// elements[2][0] = Statusanlaß code (A06 etc.)
let target_causes = ["A06", "A09", "A10", "A11", "A13", "A14", "A15", "A16"];
let segs = ctx.find_segments("STS");
if segs.is_empty() {
return ConditionResult::Unknown;
}
let found = segs.iter().any(|seg| {
seg.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "Z25")
&& seg
.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v == "Z31")
&& seg
.elements
.get(2)
.and_then(|e| e.first())
.is_some_and(|v| target_causes.contains(&v.as_str()))
});
ConditionResult::from(found)
}
/// [103] Wenn in diesem STS DE1131 nicht vorhanden
// REVIEW: DE1131 (Codeliste, Code) appears at elements[2][1] in the majority of STS variants per the segment structure reference (C556, second component). Condition is True when that position is absent or empty. The 'in diesem STS' scope cannot be fully resolved without a current-segment concept in EvaluationContext, so this checks all STS segments. Note: STS+Z04 has DE1131 at elements[1][1] instead — this edge case is not covered. (medium confidence)
fn evaluate_103(&self, ctx: &EvaluationContext) -> ConditionResult {
// DE1131 (Codeliste, Code) is at elements[2][1] (C556 component [1]) in most STS variants.
// Condition is True when DE1131 is absent or empty in the relevant STS.
let segs = ctx.find_segments("STS");
if segs.is_empty() {
return ConditionResult::Unknown;
}
for seg in &segs {
let de1131 = seg.elements.get(2).and_then(|e| e.get(1));
if de1131.map(|v| v.is_empty()).unwrap_or(true) {
return ConditionResult::True;
}
}
ConditionResult::False
}
/// [149] Wenn in dieser SG14 STS DE1131 <> E_0278
// REVIEW: Checks DE1131 (elements[2][1]) in STS segments within SG14. Returns True if any STS has DE1131 present and not equal to E_0278, False if all present values equal E_0278, Unknown if no STS has DE1131 set. Uses navigator for group-scoped check with message-wide fallback. (medium confidence)
fn evaluate_149(&self, ctx: &EvaluationContext) -> ConditionResult {
let nav = match ctx.navigator() {
Some(n) => n,
None => {
let segs = ctx.find_segments("STS");
let mut found_any = false;
for seg in &segs {
if let Some(val) = seg.elements.get(2).and_then(|e| e.get(1)) {
if !val.is_empty() {
found_any = true;
if val != "E_0278" {
return ConditionResult::True;
}
}
}
}
return if found_any {
ConditionResult::False
} else {
ConditionResult::Unknown
};
}
};
let sg14_count = nav.group_instance_count(&["SG14"]);
let mut found_any = false;
for i in 0..sg14_count {
let sts_segs = nav.find_segments_in_group("STS", &["SG14"], i);
for seg in &sts_segs {
if let Some(val) = seg.elements.get(2).and_then(|e| e.get(1)) {
if !val.is_empty() {
found_any = true;
if val != "E_0278" {
return ConditionResult::True;
}
}
}
}
}
if found_any {
ConditionResult::False
} else {
ConditionResult::Unknown
}
}
/// [150] Wenn in dieser SG14 STS DE1131 <> E_0281
// REVIEW: Identical pattern to condition 149 but checks DE1131 != E_0281. Returns True if any STS in SG14 has DE1131 present and not equal to E_0281, False if all equal E_0281, Unknown if absent. (medium confidence)
fn evaluate_150(&self, ctx: &EvaluationContext) -> ConditionResult {
let nav = match ctx.navigator() {
Some(n) => n,
None => {
let segs = ctx.find_segments("STS");
let mut found_any = false;
for seg in &segs {
if let Some(val) = seg.elements.get(2).and_then(|e| e.get(1)) {
if !val.is_empty() {
found_any = true;
if val != "E_0281" {
return ConditionResult::True;
}
}
}
}
return if found_any {
ConditionResult::False
} else {
ConditionResult::Unknown
};
}
};
let sg14_count = nav.group_instance_count(&["SG14"]);
let mut found_any = false;
for i in 0..sg14_count {
let sts_segs = nav.find_segments_in_group("STS", &["SG14"], i);
for seg in &sts_segs {
if let Some(val) = seg.elements.get(2).and_then(|e| e.get(1)) {
if !val.is_empty() {
found_any = true;
if val != "E_0281" {
return ConditionResult::True;
}
}
}
}
}
if found_any {
ConditionResult::False
} else {
ConditionResult::Unknown
}
}
/// [151] Wenn STS+Z20+Z32+A99:E_0278 in dieser SG14 vorhanden
fn evaluate_151(&self, ctx: &EvaluationContext) -> ConditionResult {
// STS+Z20+Z32+A99:E_0278: elements[0][0]=Z20, elements[1][0]=Z32, elements[2][0]=A99, elements[2][1]=E_0278
ctx.has_segment_matching_in_group(
"STS",
&[
(0, 0, "Z20"),
(1, 0, "Z32"),
(2, 0, "A99"),
(2, 1, "E_0278"),
],
&["SG14"],
)
}
/// [152] Wenn STS+Z20+Z32+A99:E_0281 in dieser SG14 vorhanden
fn evaluate_152(&self, ctx: &EvaluationContext) -> ConditionResult {
// STS+Z20+Z32+A99:E_0281: elements[0][0]=Z20, elements[1][0]=Z32, elements[2][0]=A99, elements[2][1]=E_0281
ctx.has_segment_matching_in_group(
"STS",
&[
(0, 0, "Z20"),
(1, 0, "Z32"),
(2, 0, "A99"),
(2, 1, "E_0281"),
],
&["SG14"],
)
}
/// [153] Wenn in diesem STS DE1131 = E_0286
fn evaluate_153(&self, ctx: &EvaluationContext) -> ConditionResult {
// DE1131 is at elements[2][1] in STS (C556 Statusanlaß component 1)
ctx.has_segment_matching("STS", &[(2, 1, "E_0286")])
}
/// [154] Wenn STS+Z15+Z13+A99:E_0286 in dieser SG14 vorhanden
// REVIEW: Checks for STS in SG14/SG15 with Statuskategorie Z15 (Status des Umbaus der Messlokation), Status Z13, and Statusanlaß A99:E_0286. Uses has_segment_matching_in_group with authoritative element indices from the segment reference: elements[0][0]=Z15, elements[1][0]=Z13, elements[2][0]=A99 (DE9013 Prüfschritt), elements[2][1]=E_0286 (DE1131 Codeliste). Medium confidence because A99 is not listed in the abbreviated reference for this STS variant (reference shows Z74/Z75), so it may be an additional valid code from the full BDEW codelist. (medium confidence)
fn evaluate_154(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_segment_matching_in_group(
"STS",
&[
(0, 0, "Z15"),
(1, 0, "Z13"),
(2, 0, "A99"),
(2, 1, "E_0286"),
],
&["SG14", "SG15"],
)
}
/// [537] Hinweis: Der Code ist nötig, da dieser Anwendungsfall auch in allen Use-Cases zur Anwendung kommt, in denen sich die in diesem Segment zu übertragende Information nicht anhand eines Entscheidungs...
fn evaluate_537(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Der Code ist nötig, da dieser Anwendungsfall auch in allen Use-Cases zur Anwendung kommt, in denen sich die in diesem Segment zu übertragende Information nicht anhand eines Entscheidungsbaus ergibt.
ConditionResult::True
}
/// [538] Hinweis: An dieser Stelle wird der Code aus dem EBD übermittelt
fn evaluate_538(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: An dieser Stelle wird der Code aus dem EBD übermittelt
ConditionResult::True
}
/// [1] Wenn die Übermittlung nicht codierbarer Informationen nötig ist.
/// EXTERNAL: Requires context from outside the message.
// REVIEW: Whether transmission of non-codeable information is necessary is a business context decision that cannot be determined from the EDIFACT message content alone. It depends on whether the sending system has information that cannot be expressed via defined code lists. (medium confidence)
fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("non_codeable_info_needed")
}
/// [3] Wenn SG7 STS+Z01 nicht vorhanden.
fn evaluate_3(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.lacks_qualifier("STS", 0, "Z01")
}
/// [4] Wenn SG7 STS+Z02 nicht vorhanden.
fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.lacks_qualifier("STS", 0, "Z02")
}
/// [5] Wenn SG7 STS+Z03 nicht vorhanden.
fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.lacks_qualifier("STS", 0, "Z03")
}
/// [10] Wenn Meldung des BKV/NB/ÜNB nach Frist eingeht.
/// EXTERNAL: Requires context from outside the message.
fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("message_after_deadline")
}
/// [16] Wenn MP-ID in SG1 NAD+MR in der Rolle BKV
/// EXTERNAL: Requires context from outside the message.
fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_bkv")
}
/// [17] Wenn Meldung des BKV auf falscher Aggregationsebene eingeht.
/// EXTERNAL: Requires context from outside the message.
fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("bkv_wrong_aggregation_level")
}
/// [18] Wenn SG15 STS+Z19 nicht vorhanden.
fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.lacks_qualifier("STS", 0, "Z19")
}
/// [19] Wenn SG15 STS+Z24 nicht vorhanden.
fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.lacks_qualifier("STS", 0, "Z24")
}
/// [20] Wenn MP-ID in SG1 NAD+MR in der Rolle LF
/// EXTERNAL: Requires context from outside the message.
fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_lf")
}
/// [26] Wenn MP-ID in SG1 NAD+MR in der Rolle NB
/// EXTERNAL: Requires context from outside the message.
fn evaluate_26(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_nb")
}
/// [27] Nur MP-ID aus Sparte Strom
/// EXTERNAL: Requires context from outside the message.
fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("mp_id_is_electricity_sector")
}
/// [28] Nur MP-ID aus Sparte Gas
/// EXTERNAL: Requires context from outside the message.
fn evaluate_28(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("mp_id_is_gas_sector")
}
/// [29] Wenn MP-ID in SG1 NAD+MR in der Rolle ÜNB
/// EXTERNAL: Requires context from outside the message.
fn evaluate_29(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_uenb")
}
/// [30] Wenn in dieser SG15 STS+Z20+Z32+A07:E_0207 vorhanden.
// REVIEW: Checks STS segment for Statuskategorie Z20 (elements[0][0]), Status Z32 (elements[1][0]), Prüfschritt A07 (elements[2][0]), and Codeliste E_0207 (elements[2][1]). Uses message-wide low-level access since no group-scoped helper supports simultaneous multi-field checks on a single segment. The 'A07' code is taken from the AHB shorthand notation at DE9013 position per the segment structure reference. (medium confidence)
fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
let found = ctx.find_segments("STS").into_iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "Z20")
&& s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v == "Z32")
&& s.elements
.get(2)
.and_then(|e| e.first())
.is_some_and(|v| v == "A07")
&& s.elements
.get(2)
.and_then(|e| e.get(1))
.is_some_and(|v| v == "E_0207")
});
ConditionResult::from(found)
}
/// [31] Wenn in dieser SG16 in QTY in DE6411 KWH/K3 vorhanden.
fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
let found = ctx.find_segments("QTY").into_iter().any(|s| {
let unit = s
.elements
.first()
.and_then(|e| e.get(2))
.map(|v| v.as_str());
unit == Some("KWH") || unit == Some("K3")
});
ConditionResult::from(found)
}
/// [32] Wenn in dieser SG16 in QTY in DE6411 KWT/K5 vorhanden.
fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
let found = ctx.find_segments("QTY").into_iter().any(|s| {
let unit = s
.elements
.first()
.and_then(|e| e.get(2))
.map(|v| v.as_str());
unit == Some("KWT") || unit == Some("K5")
});
ConditionResult::from(found)
}
/// [33] Wenn in dieser SG16 DTM+163 vorhanden.
fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualifier("DTM", 0, "163", &["SG16"])
}
/// [43] Wenn STS+Z01+Z07 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
fn evaluate_43(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z01", 1, 0, &["Z07"])
}
/// [44] Wenn STS+Z01+Z08 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
fn evaluate_44(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z01", 1, 0, &["Z08"])
}
/// [45] Wenn STS+Z03+Z07 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z03", 1, 0, &["Z07"])
}
/// [46] Wenn STS+Z03+Z08 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
fn evaluate_46(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z03", 1, 0, &["Z08"])
}
/// [47] Es sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
fn evaluate_47(&self, _ctx: &EvaluationContext) -> ConditionResult {
ConditionResult::True
}
/// [48] Es sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
fn evaluate_48(&self, _ctx: &EvaluationContext) -> ConditionResult {
ConditionResult::True
}
/// [49] Wenn STS+Z25+Z30 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z25", 1, 0, &["Z30"])
}
/// [50] Wenn STS+Z25+Z31 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z25", 1, 0, &["Z31"])
}
/// [51] Es sind nur Codes aus dem EBD-Cluster "Abweisung" möglich.
fn evaluate_51(&self, _ctx: &EvaluationContext) -> ConditionResult {
ConditionResult::True
}
/// [52] Wenn STS+Z27+Z32 vorhanden
fn evaluate_52(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z27", 1, 0, &["Z32"])
}
/// [53] Wenn STS+Z28+Z32 vorhanden
fn evaluate_53(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z28", 1, 0, &["Z32"])
}
/// [54] Wenn STS+Z29+Z32 vorhanden
fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z29", 1, 0, &["Z32"])
}
/// [55] Wenn STS+Z30+Z32 vorhanden
fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z30", 1, 0, &["Z32"])
}
/// [56] Wenn in dieser SG15 STS das SG15 RFF+ACW nicht identisch mit dem SG15 RFF+ACW der SG15 STS+Z27 ist
// REVIEW: Cross-SG15 comparison: find SG15 instances with STS+Z27, collect their RFF+ACW values, then check if any SG15's RFF+ACW differs. Requires low-level navigator access. Medium confidence because the exact group path (SG14→SG15) is inferred from IFTSTA Family B structure. (medium confidence)
fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
// True when this SG15's RFF+ACW value differs from the RFF+ACW of the SG15 containing STS+Z27
let nav = match ctx.navigator() {
Some(n) => n,
None => return ConditionResult::Unknown,
};
let sg15_path: &[&str] = &["SG14", "SG15"];
let sg15_count = nav.group_instance_count(sg15_path);
// Collect ACW reference values from SG15 instances that have STS with Z27
let mut ref_acw: Vec<String> = Vec::new();
for i in 0..sg15_count {
let sts_segs = nav.find_segments_in_group("STS", sg15_path, i);
let has_z27 = sts_segs.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "Z27")
});
if has_z27 {
let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
for rff in &rff_segs {
if rff
.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "ACW")
{
if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
if !val.is_empty() {
ref_acw.push(val.clone());
}
}
}
}
}
}
if ref_acw.is_empty() {
return ConditionResult::Unknown;
}
// Check if any SG15 has an ACW value not present in the Z27 SG15 ACW values
for i in 0..sg15_count {
let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
for rff in &rff_segs {
if rff
.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "ACW")
{
if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
if !val.is_empty() && !ref_acw.contains(val) {
return ConditionResult::True;
}
}
}
}
}
ConditionResult::False
}
/// [57] Wenn in dieser SG15 STS das SG15 RFF+ACW nicht identisch mit dem SG15 RFF+ACW der SG15 STS+Z28 ist
// REVIEW: Same cross-SG15 comparison pattern as condition 56, but referencing STS+Z28 (Status des Fahrplananteils) instead of Z27. (medium confidence)
fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
// True when this SG15's RFF+ACW value differs from the RFF+ACW of the SG15 containing STS+Z28
let nav = match ctx.navigator() {
Some(n) => n,
None => return ConditionResult::Unknown,
};
let sg15_path: &[&str] = &["SG14", "SG15"];
let sg15_count = nav.group_instance_count(sg15_path);
let mut ref_acw: Vec<String> = Vec::new();
for i in 0..sg15_count {
let sts_segs = nav.find_segments_in_group("STS", sg15_path, i);
let has_z28 = sts_segs.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "Z28")
});
if has_z28 {
let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
for rff in &rff_segs {
if rff
.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "ACW")
{
if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
if !val.is_empty() {
ref_acw.push(val.clone());
}
}
}
}
}
}
if ref_acw.is_empty() {
return ConditionResult::Unknown;
}
for i in 0..sg15_count {
let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
for rff in &rff_segs {
if rff
.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "ACW")
{
if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
if !val.is_empty() && !ref_acw.contains(val) {
return ConditionResult::True;
}
}
}
}
}
ConditionResult::False
}
/// [58] Wenn in dieser SG15 STS das SG15 RFF+ACW nicht identisch mit dem SG15 RFF+ACW der SG15 STS+Z29 ist
// REVIEW: Same cross-SG15 comparison pattern as condition 56, but referencing STS+Z29 (Status des Gegenvorschlags der Ausfallarbeit). (medium confidence)
fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
// True when this SG15's RFF+ACW value differs from the RFF+ACW of the SG15 containing STS+Z29
let nav = match ctx.navigator() {
Some(n) => n,
None => return ConditionResult::Unknown,
};
let sg15_path: &[&str] = &["SG14", "SG15"];
let sg15_count = nav.group_instance_count(sg15_path);
let mut ref_acw: Vec<String> = Vec::new();
for i in 0..sg15_count {
let sts_segs = nav.find_segments_in_group("STS", sg15_path, i);
let has_z29 = sts_segs.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "Z29")
});
if has_z29 {
let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
for rff in &rff_segs {
if rff
.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "ACW")
{
if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
if !val.is_empty() {
ref_acw.push(val.clone());
}
}
}
}
}
}
if ref_acw.is_empty() {
return ConditionResult::Unknown;
}
for i in 0..sg15_count {
let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
for rff in &rff_segs {
if rff
.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "ACW")
{
if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
if !val.is_empty() && !ref_acw.contains(val) {
return ConditionResult::True;
}
}
}
}
}
ConditionResult::False
}
/// [59] Wenn in dieser SG15 STS das SG15 RFF+ACW nicht identisch mit dem SG15 RFF+ACW der SG15 STS+Z30 ist
// REVIEW: Same cross-SG15 comparison pattern as condition 56, but referencing STS+Z30 (Status des Gegenvorschlags des Fahrplananteils). (medium confidence)
fn evaluate_59(&self, ctx: &EvaluationContext) -> ConditionResult {
// True when this SG15's RFF+ACW value differs from the RFF+ACW of the SG15 containing STS+Z30
let nav = match ctx.navigator() {
Some(n) => n,
None => return ConditionResult::Unknown,
};
let sg15_path: &[&str] = &["SG14", "SG15"];
let sg15_count = nav.group_instance_count(sg15_path);
let mut ref_acw: Vec<String> = Vec::new();
for i in 0..sg15_count {
let sts_segs = nav.find_segments_in_group("STS", sg15_path, i);
let has_z30 = sts_segs.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "Z30")
});
if has_z30 {
let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
for rff in &rff_segs {
if rff
.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "ACW")
{
if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
if !val.is_empty() {
ref_acw.push(val.clone());
}
}
}
}
}
}
if ref_acw.is_empty() {
return ConditionResult::Unknown;
}
for i in 0..sg15_count {
let rff_segs = nav.find_segments_in_group("RFF", sg15_path, i);
for rff in &rff_segs {
if rff
.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "ACW")
{
if let Some(val) = rff.elements.first().and_then(|e| e.get(1)) {
if !val.is_empty() && !ref_acw.contains(val) {
return ConditionResult::True;
}
}
}
}
}
ConditionResult::False
}
/// [60] Wenn zusätzlich zum Fahrplananteil auch die Ausfallarbeit zu identischem Wert aus RFF+ACW nicht o.k. ist
// REVIEW: Checks whether any SG15 with STS+Z27+Z32 (Ausfallarbeit rejected) shares the same RFF+ACW reference value as an SG15 with STS+Z28+Z32 (Fahrplananteil rejected), within the same SG14 transaction group. Assumes SG15 is a child group of SG14 (consistent with IFTSTA Family B tx_group=SG14). (medium confidence)
fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
{
let nav = match ctx.navigator() {
Some(n) => n,
None => return ConditionResult::Unknown,
};
let sg14_count = nav.group_instance_count(&["SG14"]);
for i in 0..sg14_count {
let sg15_count = nav.child_group_instance_count(&["SG14"], i, "SG15");
let mut z27_z32_acw: Vec<String> = Vec::new();
let mut z28_z32_acw: Vec<String> = Vec::new();
for j in 0..sg15_count {
let stses = nav.find_segments_in_child_group("STS", &["SG14"], i, "SG15", j);
let rffs = nav.find_segments_in_child_group("RFF", &["SG14"], i, "SG15", j);
let acw_val = rffs
.iter()
.find(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "ACW")
})
.and_then(|s| s.elements.first().and_then(|e| e.get(1)).cloned());
let Some(acw) = acw_val else { continue };
for sts in &stses {
let cat = sts
.elements
.first()
.and_then(|e| e.first())
.map(|s| s.as_str());
let status = sts
.elements
.get(1)
.and_then(|e| e.first())
.map(|s| s.as_str());
match (cat, status) {
(Some("Z27"), Some("Z32")) => z27_z32_acw.push(acw.clone()),
(Some("Z28"), Some("Z32")) => z28_z32_acw.push(acw.clone()),
_ => {}
}
}
}
if z27_z32_acw.iter().any(|v| z28_z32_acw.contains(v)) {
return ConditionResult::True;
}
}
ConditionResult::False
}
}
/// [61] Wenn zusätzlich zur Ausfallarbeit auch der Fahrplananteil zu identischem Wert aus RFF+ACW nicht o.k. ist
// REVIEW: Mirror of condition 60: checks whether any SG15 with STS+Z28+Z32 (Fahrplananteil rejected) shares the same RFF+ACW as an SG15 with STS+Z27+Z32 (Ausfallarbeit rejected). The two conditions are semantically equivalent but expressed from different perspectives (the AHB uses them on different fields). Same SG14→SG15 navigator approach. (medium confidence)
fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
{
let nav = match ctx.navigator() {
Some(n) => n,
None => return ConditionResult::Unknown,
};
let sg14_count = nav.group_instance_count(&["SG14"]);
for i in 0..sg14_count {
let sg15_count = nav.child_group_instance_count(&["SG14"], i, "SG15");
let mut z27_z32_acw: Vec<String> = Vec::new();
let mut z28_z32_acw: Vec<String> = Vec::new();
for j in 0..sg15_count {
let stses = nav.find_segments_in_child_group("STS", &["SG14"], i, "SG15", j);
let rffs = nav.find_segments_in_child_group("RFF", &["SG14"], i, "SG15", j);
let acw_val = rffs
.iter()
.find(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "ACW")
})
.and_then(|s| s.elements.first().and_then(|e| e.get(1)).cloned());
let Some(acw) = acw_val else { continue };
for sts in &stses {
let cat = sts
.elements
.first()
.and_then(|e| e.first())
.map(|s| s.as_str());
let status = sts
.elements
.get(1)
.and_then(|e| e.first())
.map(|s| s.as_str());
match (cat, status) {
(Some("Z27"), Some("Z32")) => z27_z32_acw.push(acw.clone()),
(Some("Z28"), Some("Z32")) => z28_z32_acw.push(acw.clone()),
_ => {}
}
}
}
if z28_z32_acw.iter().any(|v| z27_z32_acw.contains(v)) {
return ConditionResult::True;
}
}
ConditionResult::False
}
}
/// [62] Wenn STS+Z27+Z30 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z27", 1, 0, &["Z30"])
}
/// [63] Wenn STS+Z27+Z32 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z27", 1, 0, &["Z32"])
}
/// [64] Wenn STS+Z28+Z30 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
fn evaluate_64(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z28", 1, 0, &["Z30"])
}
/// [65] Wenn STS+Z28+Z32 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z28", 1, 0, &["Z32"])
}
/// [66] Wenn STS+Z29+Z30 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z29", 1, 0, &["Z30"])
}
/// [67] Wenn STS+Z29+Z32 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z29", 1, 0, &["Z32"])
}
/// [68] Wenn STS+Z30+Z30 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Zustimmung" möglich.
fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z30", 1, 0, &["Z30"])
}
/// [69] Wenn STS+Z30+Z32 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z30", 1, 0, &["Z32"])
}
/// [70] Wenn zusätzlich zum Gegenvorschlag des Fahrplananteils auch der Gegenvorschlag der Ausfallarbeit zu identischem Wert aus RFF+ACW nicht o.k. ist
// REVIEW: Condition 70 means: additionally to Fahrplananteil counter-proposal (STS Z30 Z32) being rejected, the Ausfallarbeit counter-proposal (STS Z29 Z32) is also rejected. The 'identischem Wert aus RFF+ACW' (same RFF+ACW reference) cross-group correlation is simplified — checks that both rejection STS types are present in the message. Full cross-SG14 value matching would require navigator group enumeration not fully supported by current helpers. (medium confidence)
fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
match ctx.has_qualified_value("STS", 0, "Z30", 1, 0, &["Z32"]) {
ConditionResult::True => ctx.has_qualified_value("STS", 0, "Z29", 1, 0, &["Z32"]),
ConditionResult::False => ConditionResult::False,
ConditionResult::Unknown => ConditionResult::Unknown,
}
}
/// [71] Wenn zusätzlich zum Gegenvorschlag der Ausfallarbeit auch der Gegenvorschlag des Fahrplananteils zu identischem Wert aus RFF+ACW nicht o.k. ist
// REVIEW: Symmetric to condition 70: additionally to Ausfallarbeit counter-proposal (STS Z29 Z32) being rejected, the Fahrplananteil counter-proposal (STS Z30 Z32) is also rejected. Same simplification of the RFF+ACW cross-group correlation applies. (medium confidence)
fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
match ctx.has_qualified_value("STS", 0, "Z29", 1, 0, &["Z32"]) {
ConditionResult::True => ctx.has_qualified_value("STS", 0, "Z30", 1, 0, &["Z32"]),
ConditionResult::False => ConditionResult::False,
ConditionResult::Unknown => ConditionResult::Unknown,
}
}
/// [72] Wenn Gegenvorschlag erstellt werden kann
/// EXTERNAL: Requires context from outside the message.
fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("counter_proposal_possible")
}
/// [76] Wenn MP-ID in SG1 NAD+MR in der Rolle ESA
/// EXTERNAL: Requires context from outside the message.
fn evaluate_76(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_esa")
}
/// [77] Wenn STS+Z37+Z14 in dieser SG14 vorhanden
fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualified_value("STS", 0, "Z37", 1, 0, &["Z14"], &["SG14"])
}
/// [78] Wenn STS+Z38 in dieser SG14 nicht vorhanden
// REVIEW: STS+Z38 (Entsperren) is absent in the SG14 that contains STS+Z37 (Sperren). Uses any_group_has_qualifier_without to find an SG14 instance where STS+Z37 is present but STS+Z38 is not, covering the intra-SG14 scoping requirement. (medium confidence)
fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualifier_without("STS", 0, "Z37", "STS", 0, "Z38", &["SG14"])
}
/// [79] Wenn STS+Z37 in dieser SG14 nicht vorhanden
// REVIEW: STS+Z37 (Sperren) is absent in the SG14 that contains STS+Z38 (Entsperren). Symmetric to condition 78 — finds SG14 where STS+Z38 is present but STS+Z37 is not. (medium confidence)
fn evaluate_79(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualifier_without("STS", 0, "Z38", "STS", 0, "Z37", &["SG14"])
}
/// [80] Wenn STS+Z38+Z14 in dieser SG14 vorhanden
fn evaluate_80(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualified_value("STS", 0, "Z38", 1, 0, &["Z14"], &["SG14"])
}
/// [83] Wenn STS+Z37+Z13+A04/A05/A06:E_0472 in dieser SG14 vorhanden
// REVIEW: STS+Z37 (Sperren) with Status Z13 and Statusanlass (elements[2][0], DE9013) in {A04, A05, A06}. The Codeliste code E_0472 in elements[2][1] is not checked as the primary discriminator is the DE9013 Prüfschritt code. Message-wide search is used as group-scoped SG15 traversal is not available via documented helpers. (medium confidence)
fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
let sts_segs = ctx.find_segments("STS");
let found = sts_segs.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "Z37")
&& s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v == "Z13")
&& s.elements
.get(2)
.and_then(|e| e.first())
.is_some_and(|v| matches!(v.as_str(), "A04" | "A05" | "A06"))
});
ConditionResult::from(found)
}
/// [84] Wenn STS+Z38+Z13+A02:E_0499 in dieser SG14 vorhanden
// REVIEW: STS+Z38 (Entsperren) with Status Z13 and Statusanlass DE9013=A02 (Codeliste E_0499). Checks elements[0][0]=Z38, elements[1][0]=Z13, elements[2][0]=A02. Message-wide search used as SG14/SG15 child group traversal is not in documented helpers. (medium confidence)
fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
let sts_segs = ctx.find_segments("STS");
let found = sts_segs.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "Z38")
&& s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v == "Z13")
&& s.elements
.get(2)
.and_then(|e| e.first())
.is_some_and(|v| v == "A02")
});
ConditionResult::from(found)
}
/// [85] Wenn STS+Z37+Z32+A01:E_0501 in dieser SG14 vorhanden
// REVIEW: STS+Z37 (Sperren) with Status Z32 (Ablehnung) and Statusanlass DE9013=A01 (Codeliste E_0501). Checks elements[0][0]=Z37, elements[1][0]=Z32, elements[2][0]=A01. Message-wide search used as SG14/SG15 child group traversal is not in documented helpers. (medium confidence)
fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
let sts_segs = ctx.find_segments("STS");
let found = sts_segs.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "Z37")
&& s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v == "Z32")
&& s.elements
.get(2)
.and_then(|e| e.first())
.is_some_and(|v| v == "A01")
});
ConditionResult::from(found)
}
/// [86] Wenn MP-ID in SG1 NAD+MS in der Rolle LF
/// EXTERNAL: Requires context from outside the message.
fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("sender_is_lf")
}
/// [91] Wenn in diesem STS DE1131 = E_0472
fn evaluate_91(&self, ctx: &EvaluationContext) -> ConditionResult {
let segments = ctx.find_segments("STS");
ConditionResult::from(segments.iter().any(|s| {
s.elements
.get(2)
.and_then(|e| e.get(1))
.map(|v| v == "E_0472")
.unwrap_or(false)
}))
}
/// [92] Wenn in diesem STS DE1131 = E_0501
fn evaluate_92(&self, ctx: &EvaluationContext) -> ConditionResult {
let segments = ctx.find_segments("STS");
ConditionResult::from(segments.iter().any(|s| {
s.elements
.get(2)
.and_then(|e| e.get(1))
.map(|v| v == "E_0501")
.unwrap_or(false)
}))
}
/// [93] Wenn STS+Z37+Z13 vorhanden, dann sind nur Codes aus dem EBD-Cluster "gescheitert" möglich.
fn evaluate_93(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z37", 1, 0, &["Z13"])
}
/// [94] Wenn STS+Z37+Z14 vorhanden, dann sind nur Codes aus dem EBD-Cluster "erfolgreich" möglich.
fn evaluate_94(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z37", 1, 0, &["Z14"])
}
/// [95] Wenn in diesem STS DE1131 = E_0499
fn evaluate_95(&self, ctx: &EvaluationContext) -> ConditionResult {
let segments = ctx.find_segments("STS");
ConditionResult::from(segments.iter().any(|s| {
s.elements
.get(2)
.and_then(|e| e.get(1))
.map(|v| v == "E_0499")
.unwrap_or(false)
}))
}
/// [96] Wenn in diesem STS DE1131 = E_0487
fn evaluate_96(&self, ctx: &EvaluationContext) -> ConditionResult {
let segments = ctx.find_segments("STS");
ConditionResult::from(segments.iter().any(|s| {
s.elements
.get(2)
.and_then(|e| e.get(1))
.map(|v| v == "E_0487")
.unwrap_or(false)
}))
}
/// [97] Wenn STS+Z38+Z13 vorhanden, dann sind nur Codes aus dem EBD-Cluster "gescheitert" möglich.
fn evaluate_97(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z38", 1, 0, &["Z13"])
}
/// [98] Wenn STS+Z38+Z14 vorhanden, dann sind nur Codes aus dem EBD-Cluster "erfolgreich" möglich.
fn evaluate_98(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z38", 1, 0, &["Z14"])
}
/// [99] Wenn in diesem STS DE1131 = E_0487, dann ist nur der Code A01 möglich.
fn evaluate_99(&self, ctx: &EvaluationContext) -> ConditionResult {
let segments = ctx.find_segments("STS");
ConditionResult::from(segments.iter().any(|s| {
s.elements
.get(2)
.and_then(|e| e.get(1))
.map(|v| v == "E_0487")
.unwrap_or(false)
}))
}
/// [100] Wenn STS+Z43+Z47 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Änderung der Daten" möglich.
fn evaluate_100(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z43", 1, 0, &["Z47"])
}
/// [101] Wenn STS+Z43+Z48 vorhanden, dann sind nur Codes aus dem EBD-Cluster "keine Änderung der Daten" möglich.
fn evaluate_101(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z43", 1, 0, &["Z48"])
}
/// [107] Wenn STS+Z37+Z32 vorhanden, dann sind nur Codes aus dem EBD-Cluster "Ablehnung" möglich.
fn evaluate_107(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z37", 1, 0, &["Z32"])
}
/// [114] Wenn MP-ID in SG1 NAD+MS in der Rolle MSB
/// EXTERNAL: Requires context from outside the message.
fn evaluate_114(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("sender_is_msb")
}
/// [115] Wenn MP-ID in SG1 NAD+MS in der Rolle NB
/// EXTERNAL: Requires context from outside the message.
fn evaluate_115(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("sender_is_nb")
}
/// [117] Wenn MP-ID in SG1 NAD+MR in der Rolle MSB
/// EXTERNAL: Requires context from outside the message.
fn evaluate_117(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.external.evaluate("recipient_is_msb")
}
/// [118] Wenn in diesem STS DE1131 = E_0526
fn evaluate_118(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z21", 2, 1, &["E_0526"])
}
/// [119] Wenn in diesem STS DE1131 = E_0528
fn evaluate_119(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z21", 2, 1, &["E_0528"])
}
/// [120] Wenn in diesem STS DE1131 = E_0529
fn evaluate_120(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z21", 2, 1, &["E_0529"])
}
/// [121] Wenn in diesem STS DE1131 = E_0536
fn evaluate_121(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z21", 2, 1, &["E_0536"])
}
/// [129] Wenn STS+Z20+Z32+A99:E_0524 in dieser SG14 vorhanden
// REVIEW: Check STS with Statuskategorie Z20, Status Z32, Prüfschritt A99 (elements[2][0]), and DE1131 E_0524 (elements[2][1]). Group-scoped to SG14 but falls back to message-wide search. (medium confidence)
fn evaluate_129(&self, ctx: &EvaluationContext) -> ConditionResult {
let segments = ctx.find_segments("STS");
ConditionResult::from(segments.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.map(|v| v == "Z20")
.unwrap_or(false)
&& s.elements
.get(1)
.and_then(|e| e.first())
.map(|v| v == "Z32")
.unwrap_or(false)
&& s.elements
.get(2)
.and_then(|e| e.first())
.map(|v| v == "A99")
.unwrap_or(false)
&& s.elements
.get(2)
.and_then(|e| e.get(1))
.map(|v| v == "E_0524")
.unwrap_or(false)
}))
}
/// [130] Wenn STS+Z20+Z32+A99:E_0531 in dieser SG14 vorhanden
// REVIEW: Check STS with Statuskategorie Z20, Status Z32, Prüfschritt A99 (elements[2][0]), and DE1131 E_0531 (elements[2][1]). Group-scoped to SG14 but falls back to message-wide search. (medium confidence)
fn evaluate_130(&self, ctx: &EvaluationContext) -> ConditionResult {
let segments = ctx.find_segments("STS");
ConditionResult::from(segments.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.map(|v| v == "Z20")
.unwrap_or(false)
&& s.elements
.get(1)
.and_then(|e| e.first())
.map(|v| v == "Z32")
.unwrap_or(false)
&& s.elements
.get(2)
.and_then(|e| e.first())
.map(|v| v == "A99")
.unwrap_or(false)
&& s.elements
.get(2)
.and_then(|e| e.get(1))
.map(|v| v == "E_0531")
.unwrap_or(false)
}))
}
/// [131] Wenn STS+Z37+Z13+A04/A05/A06:E_1003 in dieser SG14 vorhanden
// REVIEW: Check STS with Statuskategorie Z37 (Auftragstatus Sperren), Status Z13, DE9013 in {A04,A05,A06} (elements[2][0]), and DE1131 E_1003 (elements[2][1]). Group-scoped to SG14 but falls back to message-wide. (medium confidence)
fn evaluate_131(&self, ctx: &EvaluationContext) -> ConditionResult {
let segments = ctx.find_segments("STS");
ConditionResult::from(segments.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.map(|v| v == "Z37")
.unwrap_or(false)
&& s.elements
.get(1)
.and_then(|e| e.first())
.map(|v| v == "Z13")
.unwrap_or(false)
&& s.elements
.get(2)
.and_then(|e| e.first())
.map(|v| matches!(v.as_str(), "A04" | "A05" | "A06"))
.unwrap_or(false)
&& s.elements
.get(2)
.and_then(|e| e.get(1))
.map(|v| v == "E_1003")
.unwrap_or(false)
}))
}
/// [132] Wenn STS+Z37+Z32+A01:E_1002 in dieser SG14 vorhanden
// REVIEW: Check STS with Statuskategorie Z37 (Auftragstatus Sperren), Status Z32, Prüfschritt A01 (elements[2][0]), and DE1131 E_1002 (elements[2][1]). Group-scoped to SG14 but falls back to message-wide. (medium confidence)
fn evaluate_132(&self, ctx: &EvaluationContext) -> ConditionResult {
let segments = ctx.find_segments("STS");
ConditionResult::from(segments.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.map(|v| v == "Z37")
.unwrap_or(false)
&& s.elements
.get(1)
.and_then(|e| e.first())
.map(|v| v == "Z32")
.unwrap_or(false)
&& s.elements
.get(2)
.and_then(|e| e.first())
.map(|v| v == "A01")
.unwrap_or(false)
&& s.elements
.get(2)
.and_then(|e| e.get(1))
.map(|v| v == "E_1002")
.unwrap_or(false)
}))
}
/// [133] Wenn in diesem STS DE1131 = E_1003
fn evaluate_133(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z37", 2, 1, &["E_1003"])
}
/// [134] Wenn in diesem STS DE1131 = E_1002
fn evaluate_134(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z37", 2, 1, &["E_1002"])
}
/// [135] Wenn in dieser SG15 in STS+Z21 DE9013 = A99
fn evaluate_135(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z21", 2, 0, &["A99"])
}
/// [136] Wenn STS+Z38+Z13+A02:E_1005 in dieser SG14 vorhanden
// REVIEW: Check STS with Statuskategorie Z38 (Auftragstatus Entsperren), Status Z13, Prüfschritt A02 (elements[2][0]), and DE1131 E_1005 (elements[2][1]). Group-scoped to SG14 but falls back to message-wide. (medium confidence)
fn evaluate_136(&self, ctx: &EvaluationContext) -> ConditionResult {
let segments = ctx.find_segments("STS");
ConditionResult::from(segments.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.map(|v| v == "Z38")
.unwrap_or(false)
&& s.elements
.get(1)
.and_then(|e| e.first())
.map(|v| v == "Z13")
.unwrap_or(false)
&& s.elements
.get(2)
.and_then(|e| e.first())
.map(|v| v == "A02")
.unwrap_or(false)
&& s.elements
.get(2)
.and_then(|e| e.get(1))
.map(|v| v == "E_1005")
.unwrap_or(false)
}))
}
/// [137] Wenn in diesem STS DE1131 = E_1005
fn evaluate_137(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z38", 2, 1, &["E_1005"])
}
/// [138] Wenn in diesem STS DE1131 = E_1020
fn evaluate_138(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z38", 2, 1, &["E_1020"])
}
/// [139] Wenn in diesem STS DE1131 = E_1020, dann ist nur der Code A01 möglich.
// REVIEW: Condition checks if DE1131=E_1020 in STS Z38 (Auftragstatus Entsperren). The clause 'dann ist nur der Code A01 möglich' documents that when this condition is true, only code A01 is valid for the dependent field — this is a downstream validation note, not an additional condition predicate. Implementation is identical to condition 138. (medium confidence)
fn evaluate_139(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.has_qualified_value("STS", 0, "Z38", 2, 1, &["E_1020"])
}
/// [140] Wenn in diesem STS DE9013 <> A01
// REVIEW: Checks if any STS segment has DE9013 (elements[2][0]) with a non-empty value that is not 'A01'. Medium confidence because 'in diesem STS' implies a segment-level context that may not be fully captured by message-wide search. (medium confidence)
fn evaluate_140(&self, ctx: &EvaluationContext) -> ConditionResult {
let sts_segments = ctx.find_segments("STS");
ConditionResult::from(sts_segments.iter().any(|s| {
s.elements
.get(2)
.and_then(|e| e.first())
.is_some_and(|v| !v.is_empty() && v != "A01")
}))
}
/// [141] Wenn in diesem STS DE9013 = A01
// REVIEW: Checks if any STS segment has DE9013 (elements[2][0]) equal to 'A01'. Medium confidence due to segment-level context ambiguity. (medium confidence)
fn evaluate_141(&self, ctx: &EvaluationContext) -> ConditionResult {
let sts_segments = ctx.find_segments("STS");
ConditionResult::from(sts_segments.iter().any(|s| {
s.elements
.get(2)
.and_then(|e| e.first())
.is_some_and(|v| v == "A01")
}))
}
/// [142] Wenn in diesem STS DE1131 = E_0535
fn evaluate_142(&self, ctx: &EvaluationContext) -> ConditionResult {
let sts_segments = ctx.find_segments("STS");
ConditionResult::from(sts_segments.iter().any(|s| {
s.elements
.get(2)
.and_then(|e| e.get(1))
.is_some_and(|v| v == "E_0535")
}))
}
/// [143] Wenn mehr als ein Grund vorliegt und die voranstehenden DE9013 dieses STS nicht ausreichen
// REVIEW: STS Z41 (Grund der Privilegierung nach EnFG) supports up to 5 C556 groups (elements[2]-[6]). 'mehr als ein Grund' is indicated when elements[3][0] (second C556 DE9013) is non-empty, meaning the first slot alone was insufficient. Medium confidence because the exact threshold depends on which positional slot this condition guards. (medium confidence)
fn evaluate_143(&self, ctx: &EvaluationContext) -> ConditionResult {
let sts_segments = ctx.find_segments("STS");
ConditionResult::from(sts_segments.iter().any(|s| {
s.elements
.get(3)
.and_then(|e| e.first())
.is_some_and(|v| !v.is_empty())
}))
}
/// [144] Wenn RFF+Z45 in dieser SG15 nicht vorhanden
// REVIEW: Checks that in some SG15 instance (inside SG14 for IFTSTA Family B), RFF+Z13 (Prüfidentifikator, always present) is present but RFF+Z45 (Artikel-ID reference) is absent. Uses RFF+Z13 as the anchor since it is mandatory in every SG15. (medium confidence)
fn evaluate_144(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualifier_without("RFF", 0, "Z13", "RFF", 0, "Z45", &["SG14", "SG15"])
}
/// [145] Wenn RFF+Z17 in dieser SG15 nicht vorhanden
// REVIEW: Checks that in some SG15 instance, RFF+Z13 (mandatory Prüfidentifikator) is present but RFF+Z17 (Preisschlüsselstamm reference) is absent. (medium confidence)
fn evaluate_145(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.any_group_has_qualifier_without("RFF", 0, "Z13", "RFF", 0, "Z17", &["SG14", "SG15"])
}
/// [146] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
fn evaluate_146(&self, ctx: &EvaluationContext) -> ConditionResult {
let com_segs = ctx.find_segments("COM");
ConditionResult::from(com_segs.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.get(1))
.is_some_and(|v| v == "EM")
}))
}
/// [147] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
fn evaluate_147(&self, ctx: &EvaluationContext) -> ConditionResult {
let com_segs = ctx.find_segments("COM");
ConditionResult::from(com_segs.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.get(1))
.is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
}))
}
/// [148] Wenn in dieser SG15 STS+Z43+Z48+A99:E_0595 vorhanden
fn evaluate_148(&self, ctx: &EvaluationContext) -> ConditionResult {
let sts_segs = ctx.find_segments("STS");
ConditionResult::from(sts_segs.iter().any(|s| {
s.elements
.first()
.and_then(|e| e.first())
.is_some_and(|v| v == "Z43")
&& s.elements
.get(1)
.and_then(|e| e.first())
.is_some_and(|v| v == "Z48")
&& s.elements
.get(2)
.and_then(|e| e.first())
.is_some_and(|v| v == "A99")
&& s.elements
.get(2)
.and_then(|e| e.get(1))
.is_some_and(|v| v == "E_0595")
}))
}
/// [490] wenn Wert in diesem DE, an der Stelle CCYYMMDD ein Datum aus dem angegeben Zeitraum der Tabelle Kapitel 3.5 „Prozesszeitpunkt bei MESZ mit UTC“ ist
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 CCYYMMDD ein Datum aus dem angegeben Zeitraum der Tabelle Kapitel 3.6 „Prozesszeitpunkt bei MEZ mit UTC“ ist
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_strom")
}
/// [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")
}
/// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
// REVIEW: The date must be the document creation time (DTM+137) or earlier. Applied to DTM+334 (Zeitpunkt der Statusvergabe) which is the most natural target in IFTSTA. Format 303 timestamps are lexicographically ordered so string comparison is valid. (medium confidence)
fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
// The referenced date must be the document creation time or earlier (≤ DTM+137)
let doc_segs = ctx.find_segments_with_qualifier("DTM", 0, "137");
let doc_val = match doc_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(v) => v.as_str(),
None => return ConditionResult::Unknown,
};
let status_segs = ctx.find_segments_with_qualifier("DTM", 0, "334");
match status_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => ConditionResult::from(val.as_str() <= doc_val),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [495] Der Zeitpunkt muss ≤ dem Wert im DE2380 des DTM+137 sein
// REVIEW: The timestamp must be ≤ DE2380 of DTM+137 (document creation date). Compares DTM+334 against DTM+137 using lexicographic ordering, valid for format 303 (YYYYMMDDhhmm) timestamps. (medium confidence)
fn evaluate_495(&self, ctx: &EvaluationContext) -> ConditionResult {
// The timestamp must be ≤ the value in DE2380 of DTM+137
let doc_segs = ctx.find_segments_with_qualifier("DTM", 0, "137");
let doc_val = match doc_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(v) => v.as_str(),
None => return ConditionResult::Unknown,
};
let status_segs = ctx.find_segments_with_qualifier("DTM", 0, "334");
match status_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => ConditionResult::from(val.as_str() <= doc_val),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [496] Der Zeitpunkt muss > dem Wert im DE2380 des DTM+137 sein
// REVIEW: The timestamp must be strictly greater than DE2380 of DTM+137. Inverse of condition 495. Applied to DTM+334 which must be after the document creation date in this usage context. (medium confidence)
fn evaluate_496(&self, ctx: &EvaluationContext) -> ConditionResult {
// The timestamp must be > the value in DE2380 of DTM+137
let doc_segs = ctx.find_segments_with_qualifier("DTM", 0, "137");
let doc_val = match doc_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(v) => v.as_str(),
None => return ConditionResult::Unknown,
};
let status_segs = ctx.find_segments_with_qualifier("DTM", 0, "334");
match status_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => ConditionResult::from(val.as_str() > doc_val),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [501] Hinweis: Aus QUOTES BGM DE1004
fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Aus QUOTES BGM DE1004 — informational note about field origin, always applies
ConditionResult::True
}
/// [502] Hinweis: Aus REQOTE BGM DE1004
fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Aus REQOTE BGM DE1004 — informational note about field origin, always applies
ConditionResult::True
}
/// [503] Hinweis: Auf Selbsteinbau eines iMS oder einer mME wird verzichtet
fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Auf Selbsteinbau eines iMS oder einer mME wird verzichtet — informational note, always applies
ConditionResult::True
}
/// [504] Hinweis: Verwendung der ID des MaBiS-ZP
fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der ID des MaBiS-ZP — informational note, always applies
ConditionResult::True
}
/// [505] Hinweis: Verwendung der ID der Messlokation
fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der ID der Messlokation — informational note, always applies
ConditionResult::True
}
/// [506] Hinweis: Verwendung der ID der Marktlokation
fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der ID der Marktlokation — informational note, always applies
ConditionResult::True
}
/// [510] Hinweis: Es ist neben der Information über die Ablehnung auch der unverändert gebliebene Datenstatus informell mitzugeben.
fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Es ist neben der Information über die Ablehnung auch der unverändert gebliebene Datenstatus informell mitzugeben — informational note, always applies
ConditionResult::True
}
/// [512] Hinweis: Aus MSCONS BGM DE1004
fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Aus MSCONS BGM DE1004 — informational note, always applies
ConditionResult::True
}
/// [519] Hinweis: Aus ORDERS BGM DE1004
fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Aus ORDERS BGM DE1004 — informational note, always applies
ConditionResult::True
}
/// [520] Hinweis: Zeitpunkt, zu dem der Wechsel erfolgt, falls er zustande kommt
fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Zeitpunkt, zu dem der Wechsel erfolgt, falls er zustande kommt — informational note, always applies
ConditionResult::True
}
/// [521] Hinweis: Zeitpunkt, ab dem der MSBN tatsächlich den Messstellenbetrieb übernimmt
fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Zeitpunkt, ab dem der MSBN tatsächlich den Messstellenbetrieb übernimmt — informational note, always applies
ConditionResult::True
}
/// [522] Hinweis: Zeitpunkt, ab dem der gMSB den Messstellenbetrieb übernimmt
fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Zeitpunkt, ab dem der gMSB den Messstellenbetrieb übernimmt — informational note, always applies
ConditionResult::True
}
/// [523] Hinweis: Wert aus BGM DE1004 der MSCONS, auf die sich die Statusangabe bezieht
fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wert aus BGM DE1004 der MSCONS, auf die sich die Statusangabe bezieht — informational note, always applies
ConditionResult::True
}
/// [524] Hinweis: Wert aus BGM DE1004 der MSCONS, die den Gegenvorschlag enthält
fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Wert aus BGM DE1004 der MSCONS, die den Gegenvorschlag enthält — informational note, always applies
ConditionResult::True
}
/// [525] Hinweis: Je SG14 sind nur Statusinformationen zu einer MSCONS enthalten
fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Je SG14 sind nur Statusinformationen zu einer MSCONS enthalten — informational note, always applies
ConditionResult::True
}
/// [530] Hinweis: Hier ist die Arbeit bzw. Leistung anzugeben, die der Sender der IFTSTA im Lieferschein für den von ihm genannten Zeitraum / Leistungsperiode erwartet hätte.
fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Hier ist die Arbeit bzw. Leistung anzugeben, die der Sender der IFTSTA im Lieferschein für den von ihm genannten Zeitraum / Leistungsperiode erwartet hätte — informational note, always applies
ConditionResult::True
}
/// [531] Vom MSBN in Schritt 1 des SD verwendete Vorgangsnummer, damit der LF diese bei der Bestellung einer Konfiguration beim MSBN, unter dem Vorbehalt, dass der MSB-Wechsel an der Messlokation erfolgreic...
fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Vom MSBN in Schritt 1 des SD verwendete Vorgangsnummer, damit der LF diese
// bei der Bestellung einer Konfiguration beim MSBN verwenden kann — informational note
// describing field semantics and usage context, always applies unconditionally
ConditionResult::True
}
/// [532] Hinweis: Verwendung der ID der Netzlokation
fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der ID der Netzlokation — informational note, always applies
ConditionResult::True
}
/// [533] Hinweis: Verwendung der ID der Steuerbaren Ressource
fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Verwendung der ID der Steuerbaren Ressource — informational note, always applies
ConditionResult::True
}
/// [534] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
fn evaluate_534(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Es darf nur eine Information im DE3148 übermittelt werden — informational note, always applies
ConditionResult::True
}
/// [535] Hinweis: Aus UTILMD IDE DE7402
fn evaluate_535(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Aus UTILMD IDE DE7402 — informational note about value origin, always applies
ConditionResult::True
}
/// [902] Format: Wert darf nur positiv oder 0 sein
// REVIEW: Format condition requiring value >= 0. Applied to QTY segment value (elements[0][1]) which is the standard location for quantity values in EDIFACT. Uses validate_numeric helper with >= 0.0. (medium confidence)
fn evaluate_902(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">=", 0.0))
}
/// [903] Format: Möglicher Wert: 1
// REVIEW: Format condition requiring value == 1. Uses validate_numeric with == operator. Targets QTY segment value (elements[0][1]) as most common numeric value field in IFTSTA context. (medium confidence)
fn evaluate_903(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, "==", 1.0))
}
/// [906] Format: max. 3 Nachkommastellen
// REVIEW: Format condition: max 3 decimal places. Applies to QTY segment value field (elements[0][1]). (medium confidence)
fn evaluate_906(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 3))
}
/// [911] Format: Mögliche Werte: 1 bis n, je Nachricht bei 1 beginnend und fortlaufend aufsteigend
fn evaluate_911(&self, _ctx: &EvaluationContext) -> ConditionResult {
// Hinweis: Mögliche Werte 1 bis n, je Nachricht bei 1 beginnend und fortlaufend aufsteigend
// Informational note about sequence numbering — always applies
ConditionResult::True
}
/// [931] Format: ZZZ = +00
// REVIEW: Format condition: timezone must be UTC (+00). Uses validate_timezone_utc on the DTM value field. Targets first DTM segment found. (medium confidence)
fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check("DTM", 0, 1, validate_timezone_utc)
}
/// [932] Format: HHMM = 2200
// REVIEW: Format condition: HHMM must equal 2200. Uses validate_hhmm_equals. DTM+163 is a common time qualifier in IFTSTA for delivery end times. (medium confidence)
fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check_qualified("DTM", 0, "163", 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"))
}
/// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
// REVIEW: Checks if any COM segment value contains both '@' and '.' characters, validating email address format. COM segments carry communication details; email format requires both characters. This is the standard way to detect email COM values in EDIFACT. (medium confidence)
fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
let com_segments = ctx.find_segments("COM");
for seg in &com_segments {
if let Some(value) = seg.elements.first().and_then(|e| e.first()) {
if value.contains('@') && value.contains('.') {
return ConditionResult::True;
}
}
}
ConditionResult::False
}
/// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
// REVIEW: Checks if any COM segment value starts with '+' and is followed only by digits — the international phone number format (E.164). This is the standard pattern for phone number validation in German energy market EDIFACT messages. (medium confidence)
fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
let com_segments = ctx.find_segments("COM");
for seg in &com_segments {
if let Some(value) = seg.elements.first().and_then(|e| e.first()) {
if value.starts_with('+')
&& value.len() > 1
&& value[1..].chars().all(|c| c.is_ascii_digit())
{
return ConditionResult::True;
}
}
}
ConditionResult::False
}
/// [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 condition validating Zählpunktbezeichnung (33 alphanumeric chars). In IFTSTA, Zählpunkt is typically associated with LOC+Z19 (Messlokation/Zählpunkt). Uses validate_zahlpunkt helper. Medium confidence because the exact LOC qualifier for Zählpunkt in IFTSTA may vary — could also be a different qualifier depending on context. (medium confidence)
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, "Z18", 1, 0, validate_nelo_id)
}
/// [961] Format: SR-ID
// REVIEW: SR-ID (Steuerbare Ressource ID) uses the same 11-digit Luhn check digit format as Marktlokations-ID. LOC+Z19 is the standard qualifier for SteuerbareRessource. Element 1, component 0 holds the location ID. (medium confidence)
fn evaluate_961(&self, ctx: &EvaluationContext) -> ConditionResult {
ctx.format_check_qualified("LOC", 0, "Z19", 1, 0, validate_malo_id)
}
}