// <auto-generated>
// Generated by automapper-generator generate-conditions
// AHB: xml-migs-and-ahbs/FV2504/IFTSTA_AHB_2_0g_Fehlerkorrektur_20250225.xml
// Generated: 2026-03-12T09:53:39Z
// </auto-generated>
#[allow(unused_imports)]
use crate::eval::format_validators::*;
use crate::eval::{ConditionEvaluator, ConditionResult, EvaluationContext};
/// Generated condition evaluator for IFTSTA FV2504.
pub struct IftstaConditionEvaluatorFV2504 {
// External condition IDs that require runtime context.
external_conditions: std::collections::HashSet<u32>,
}
impl Default for IftstaConditionEvaluatorFV2504 {
fn default() -> Self {
let mut external_conditions = std::collections::HashSet::new();
external_conditions.insert(1);
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 IftstaConditionEvaluatorFV2504 {
fn message_type(&self) -> &str {
"IFTSTA"
}
fn format_version(&self) -> &str {
"FV2504"
}
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),
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),
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),
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),
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
| 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
| 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
| 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
| 902
| 903
| 906
| 911
| 931
| 932
| 933
| 934
| 935
| 939
| 940
| 950
| 951
| 960
| 961
)
}
}
impl IftstaConditionEvaluatorFV2504 {
/// [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")
}
/// [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.
fn evaluate_6(&self, _ctx: &EvaluationContext) -> ConditionResult {
// TODO: Condition [6] requires manual implementation
// Reason: Complex co-occurrence rule: if Prüfstatus is available for the 3-tuple (MaBiS-ZP, Betrachtungszeitraum, Version der Summenzeitreihe) from BIKO, then it MUST always be transmitted together with the Datenstatus. Requires knowledge of the exact STS qualifier codes distinguishing Prüfstatus from Datenstatus in this IFTSTA context, and how the 3-tuple membership is expressed in segment structure. The AHB segment structure reference is needed to identify the correct element indices and qualifier values.
ConditionResult::Unknown
}
/// [7] Wenn der Datenstatus "Abgerechnete Daten" bzw. "Abgerechnete Daten Korrektur-BKA" nicht vorhanden.
fn evaluate_7(&self, _ctx: &EvaluationContext) -> ConditionResult {
// TODO: Condition [7] requires manual implementation
// Reason: Checks absence of Datenstatus values 'Abgerechnete Daten' and 'Abgerechnete Daten Korrektur-BKA' in STS segments. The structure (STS segment absence check) is clear, but the exact qualifier codes for these specific data status values in IFTSTA are not provided in the segment structure reference. Without authoritative codes from the AHB, implementing this would require guessing qualifier values, which risks incorrect behavior.
ConditionResult::Unknown
}
/// [8] Wenn der Datenstatus einer NZR vom BIKO an NB nicht vorhanden.
fn evaluate_8(&self, _ctx: &EvaluationContext) -> ConditionResult {
// TODO: Condition [8] requires manual implementation
// Reason: Checks absence of the Datenstatus for a NZR (Netzregelzone-Zeitreihe) from BIKO to NB. The 'vom BIKO an NB' routing context and the specific qualifier codes identifying NZR-type Datenstatus in STS are not determinable without the AHB segment structure reference. This may also involve external context about message routing (whether this is a BIKO-to-NB message).
ConditionResult::Unknown
}
/// [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")
}
/// [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")
}
/// [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")
}
/// [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 {
let dtm_segs = ctx.find_segments("DTM");
match dtm_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => is_mesz_utc(val),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [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 {
let dtm_segs = ctx.find_segments("DTM");
match dtm_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => is_mez_utc(val),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [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")
}
/// [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")
}
/// [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,
}
}
/// [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")
}))
}
/// [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 {
// Format: Wert darf nur positiv oder 0 sein — value must be >= 0
let segs = ctx.find_segments("QTY");
match segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => validate_numeric(val, ">=", 0.0),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [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 {
let segs = ctx.find_segments("QTY");
match segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => validate_numeric(val, "==", 1.0),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [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 {
let segs = ctx.find_segments("QTY");
match segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => validate_max_decimal_places(val, 3),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [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 {
let dtm_segs = ctx.find_segments("DTM");
match dtm_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => validate_timezone_utc(val),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [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 {
let dtm_segs = ctx.find_segments_with_qualifier("DTM", 0, "163");
match dtm_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => validate_hhmm_equals(val, "2200"),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [933] Format: HHMM = 2300
fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
let dtm_segs = ctx.find_segments("DTM");
match dtm_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => validate_hhmm_equals(val, "2300"),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [934] Format: HHMM = 0400
fn evaluate_934(&self, ctx: &EvaluationContext) -> ConditionResult {
let dtm_segs = ctx.find_segments("DTM");
match dtm_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => validate_hhmm_equals(val, "0400"),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [935] Format: HHMM = 0500
fn evaluate_935(&self, ctx: &EvaluationContext) -> ConditionResult {
let dtm_segs = ctx.find_segments("DTM");
match dtm_segs
.first()
.and_then(|s| s.elements.first())
.and_then(|e| e.get(1))
{
Some(val) => validate_hhmm_equals(val, "0500"),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [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 {
let segs = ctx.find_segments_with_qualifier("LOC", 0, "Z16");
match segs
.first()
.and_then(|s| s.elements.get(1))
.and_then(|e| e.first())
{
Some(val) => validate_malo_id(val),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [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 {
let segs = ctx.find_segments_with_qualifier("LOC", 0, "Z19");
match segs
.first()
.and_then(|s| s.elements.get(1))
.and_then(|e| e.first())
{
Some(val) => validate_zahlpunkt(val),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [960] Format: Netzlokations-ID
// REVIEW: Netzlokations-ID uses the same 11-digit Luhn check digit format as Marktlokations-ID. LOC+Z18 is the standard qualifier for Netzlokation in IFTSTA. Element 1, component 0 holds the location ID. (medium confidence)
fn evaluate_960(&self, ctx: &EvaluationContext) -> ConditionResult {
let segs = ctx.find_segments_with_qualifier("LOC", 0, "Z18");
match segs
.first()
.and_then(|s| s.elements.get(1))
.and_then(|e| e.first())
{
Some(val) => validate_malo_id(val),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
/// [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 {
let segs = ctx.find_segments_with_qualifier("LOC", 0, "Z19");
match segs
.first()
.and_then(|s| s.elements.get(1))
.and_then(|e| e.first())
{
Some(val) => validate_malo_id(val),
None => ConditionResult::False, // segment absent → condition not applicable
}
}
}