automapper_validation/generated/fv2504/reqote_conditions_fv2504.rs
1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2504/REQOTE_AHB_1_0a_Fehlerkorrektur_20250225.xml
4// Generated: 2026-03-12T09:51:49Z
5// </auto-generated>
6
7// LLM-produced bodies aren't idiomatic Rust — silence stylistic lints at
8// file scope so `cargo clippy -D warnings` stays green. Correctness,
9// suspicious, and perf categories stay on so real bugs still surface.
10#![allow(clippy::style, clippy::complexity)]
11
12#[allow(unused_imports)]
13use crate::eval::format_validators::*;
14use crate::eval::{ConditionEvaluator, ConditionResult, EvaluationContext};
15
16/// Generated condition evaluator for REQOTE FV2504.
17pub struct ReqoteConditionEvaluatorFV2504 {
18 // External condition IDs that require runtime context.
19 external_conditions: std::collections::HashSet<u32>,
20}
21
22impl Default for ReqoteConditionEvaluatorFV2504 {
23 fn default() -> Self {
24 let mut external_conditions = std::collections::HashSet::new();
25 external_conditions.insert(10);
26 external_conditions.insert(24);
27 external_conditions.insert(28);
28 external_conditions.insert(29);
29 external_conditions.insert(30);
30 external_conditions.insert(31);
31 external_conditions.insert(32);
32 external_conditions.insert(33);
33 external_conditions.insert(35);
34 external_conditions.insert(36);
35 external_conditions.insert(37);
36 external_conditions.insert(38);
37 external_conditions.insert(41);
38 external_conditions.insert(42);
39 external_conditions.insert(43);
40 external_conditions.insert(492);
41 external_conditions.insert(493);
42 external_conditions.insert(2065);
43 external_conditions.insert(2066);
44 Self {
45 external_conditions,
46 }
47 }
48}
49
50impl ConditionEvaluator for ReqoteConditionEvaluatorFV2504 {
51 fn message_type(&self) -> &str {
52 "REQOTE"
53 }
54
55 fn format_version(&self) -> &str {
56 "FV2504"
57 }
58
59 fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
60 match condition {
61 1 => self.evaluate_1(ctx),
62 2 => self.evaluate_2(ctx),
63 10 => self.evaluate_10(ctx),
64 15 => self.evaluate_15(ctx),
65 16 => self.evaluate_16(ctx),
66 17 => self.evaluate_17(ctx),
67 18 => self.evaluate_18(ctx),
68 19 => self.evaluate_19(ctx),
69 20 => self.evaluate_20(ctx),
70 21 => self.evaluate_21(ctx),
71 22 => self.evaluate_22(ctx),
72 23 => self.evaluate_23(ctx),
73 24 => self.evaluate_24(ctx),
74 25 => self.evaluate_25(ctx),
75 26 => self.evaluate_26(ctx),
76 27 => self.evaluate_27(ctx),
77 28 => self.evaluate_28(ctx),
78 29 => self.evaluate_29(ctx),
79 30 => self.evaluate_30(ctx),
80 31 => self.evaluate_31(ctx),
81 32 => self.evaluate_32(ctx),
82 33 => self.evaluate_33(ctx),
83 35 => self.evaluate_35(ctx),
84 36 => self.evaluate_36(ctx),
85 37 => self.evaluate_37(ctx),
86 38 => self.evaluate_38(ctx),
87 39 => self.evaluate_39(ctx),
88 40 => self.evaluate_40(ctx),
89 41 => self.evaluate_41(ctx),
90 42 => self.evaluate_42(ctx),
91 43 => self.evaluate_43(ctx),
92 490 => self.evaluate_490(ctx),
93 491 => self.evaluate_491(ctx),
94 492 => self.evaluate_492(ctx),
95 493 => self.evaluate_493(ctx),
96 494 => self.evaluate_494(ctx),
97 500 => self.evaluate_500(ctx),
98 501 => self.evaluate_501(ctx),
99 502 => self.evaluate_502(ctx),
100 503 => self.evaluate_503(ctx),
101 504 => self.evaluate_504(ctx),
102 507 => self.evaluate_507(ctx),
103 508 => self.evaluate_508(ctx),
104 509 => self.evaluate_509(ctx),
105 510 => self.evaluate_510(ctx),
106 511 => self.evaluate_511(ctx),
107 512 => self.evaluate_512(ctx),
108 514 => self.evaluate_514(ctx),
109 515 => self.evaluate_515(ctx),
110 516 => self.evaluate_516(ctx),
111 903 => self.evaluate_903(ctx),
112 906 => self.evaluate_906(ctx),
113 931 => self.evaluate_931(ctx),
114 932 => self.evaluate_932(ctx),
115 933 => self.evaluate_933(ctx),
116 934 => self.evaluate_934(ctx),
117 935 => self.evaluate_935(ctx),
118 939 => self.evaluate_939(ctx),
119 940 => self.evaluate_940(ctx),
120 950 => self.evaluate_950(ctx),
121 951 => self.evaluate_951(ctx),
122 960 => self.evaluate_960(ctx),
123 961 => self.evaluate_961(ctx),
124 962 => self.evaluate_962(ctx),
125 967 => self.evaluate_967(ctx),
126 2005 => self.evaluate_2005(ctx),
127 2060 => self.evaluate_2060(ctx),
128 2061 => self.evaluate_2061(ctx),
129 2062 => self.evaluate_2062(ctx),
130 2063 => self.evaluate_2063(ctx),
131 2064 => self.evaluate_2064(ctx),
132 2065 => self.evaluate_2065(ctx),
133 2066 => self.evaluate_2066(ctx),
134 _ => ConditionResult::Unknown,
135 }
136 }
137
138 fn is_external(&self, condition: u32) -> bool {
139 self.external_conditions.contains(&condition)
140 }
141 fn is_known(&self, condition: u32) -> bool {
142 matches!(
143 condition,
144 1 | 2
145 | 10
146 | 15
147 | 16
148 | 17
149 | 18
150 | 19
151 | 20
152 | 21
153 | 22
154 | 23
155 | 24
156 | 25
157 | 26
158 | 27
159 | 28
160 | 29
161 | 30
162 | 31
163 | 32
164 | 33
165 | 35
166 | 36
167 | 37
168 | 38
169 | 39
170 | 40
171 | 41
172 | 42
173 | 43
174 | 490
175 | 491
176 | 492
177 | 493
178 | 494
179 | 500
180 | 501
181 | 502
182 | 503
183 | 504
184 | 507
185 | 508
186 | 509
187 | 510
188 | 511
189 | 512
190 | 514
191 | 515
192 | 516
193 | 903
194 | 906
195 | 931
196 | 932
197 | 933
198 | 934
199 | 935
200 | 939
201 | 940
202 | 950
203 | 951
204 | 960
205 | 961
206 | 962
207 | 967
208 | 2005
209 | 2060
210 | 2061
211 | 2062
212 | 2063
213 | 2064
214 | 2065
215 | 2066
216 )
217 }
218}
219
220impl ReqoteConditionEvaluatorFV2504 {
221 /// [33] Es sind nur die Messprodukte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.4 „Messprodukte mit Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW“ enthalten sind.
222 /// EXTERNAL: Requires context from outside the message.
223 fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
224 ctx.external
225 .evaluate("product_in_konfigurationserlaubnis_typ2_smgw_list")
226 }
227
228 /// [37] Es sind nur die Messprodukt-Position-Codes erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.7 „Art der Werte für Messprodukte nach Typ 2“ enthalten sind.
229 /// EXTERNAL: Requires context from outside the message.
230 fn evaluate_37(&self, ctx: &EvaluationContext) -> ConditionResult {
231 ctx.external
232 .evaluate("product_position_code_in_art_der_werte_typ2_list")
233 }
234
235 /// [38] Wenn innerhalb derselben SG27 LIN im PIA+5 DE7140 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) ein Produkt angegeben ist, das in der Codeliste der Konfigurationen...
236 /// EXTERNAL: Requires context from outside the message.
237 fn evaluate_38(&self, ctx: &EvaluationContext) -> ConditionResult {
238 ctx.external
239 .evaluate("product_is_schwellwert_triggered_in_konfigurationserlaubnis_list")
240 }
241
242 /// [41] Es sind nur die Messprodukte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.6.1 „Werte nach Typ 2 aus Backend“ enthalten sind.
243 /// EXTERNAL: Requires context from outside the message.
244 fn evaluate_41(&self, ctx: &EvaluationContext) -> ConditionResult {
245 ctx.external.evaluate("product_in_werte_typ2_backend_list")
246 }
247
248 /// [42] Es sind nur die Messprodukte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.6.2 „Werte nach Typ 2 aus SMGW“ enthalten sind.
249 /// EXTERNAL: Requires context from outside the message.
250 fn evaluate_42(&self, ctx: &EvaluationContext) -> ConditionResult {
251 ctx.external.evaluate("product_in_werte_typ2_smgw_list")
252 }
253
254 /// [43] Wenn innerhalb derselben SG27 LIN im PIA+5 DE7140 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) ein Produkt angegeben ist, das in der Codeliste der Konfigurationen...
255 /// EXTERNAL: Requires context from outside the message.
256 fn evaluate_43(&self, ctx: &EvaluationContext) -> ConditionResult {
257 ctx.external
258 .evaluate("product_is_schwellwert_ausloeser_typ2_smgw")
259 }
260
261 /// [490] wenn Wert in diesem DE, an der Stelle CCYYMMDDHHMM ein Zeitpunkt aus dem angegeben Zeitraum der Tabelle Kapitel 3.5 „Übersicht gesetzliche deutsche Sommerzeit (MESZ)“ der Spalten: „Sommerzei...
262 fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
263 match ctx.resolved_value {
264 Some(val) => is_mesz_utc(val),
265 None => ConditionResult::False,
266 }
267 }
268
269 /// [491] wenn Wert in diesem DE, an der Stelle CCYYMMDDHHMM ein Zeitpunkt aus dem angegeben Zeitraum der Tabelle Kapitel 3.6 „Übersicht gesetzliche deutsche Zeit (MEZ)“ der Spalten: „Winterzeit (MEZ)...
270 fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
271 match ctx.resolved_value {
272 Some(val) => is_mez_utc(val),
273 None => ConditionResult::False,
274 }
275 }
276
277 /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt.
278 fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
279 // The date in this DE must be ≤ DTM+137 (Nachrichtendatum).
280 // Prefer the tree-resolved value; fall back to scanning for a
281 // non-137 DTM when unavailable.
282 let creation = match ctx
283 .find_segments_with_qualifier("DTM", 0, "137")
284 .first()
285 .and_then(|s| s.elements.first())
286 .and_then(|e| e.get(1))
287 {
288 Some(v) if !v.is_empty() => v.clone(),
289 _ => return ConditionResult::Unknown,
290 };
291 if let Some(val) = ctx.resolved_value {
292 if !val.is_empty() {
293 return ConditionResult::from(val <= creation.as_str());
294 }
295 }
296 // Fallback for non-tree callers: first non-137 DTM value.
297 for dtm in ctx.find_segments("DTM") {
298 let qual = dtm
299 .elements
300 .first()
301 .and_then(|e| e.first())
302 .map(|s| s.as_str());
303 if qual != Some("137") {
304 if let Some(val) = dtm.elements.first().and_then(|e| e.get(1)) {
305 if !val.is_empty() {
306 return ConditionResult::from(val.as_str() <= creation.as_str());
307 }
308 }
309 }
310 }
311 ConditionResult::Unknown
312 }
313
314 /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
315 fn evaluate_967(&self, ctx: &EvaluationContext) -> ConditionResult {
316 // Structural check on the certificate body (base64 charset + plausible
317 // length). Cryptographic validation (signature, CA, BSI profile) is
318 // out of scope for static AHB validation.
319 match ctx.resolved_value {
320 Some(val) => validate_x509_cert_body(val),
321 None => ConditionResult::Unknown,
322 }
323 }
324
325 /// [2065] Diese SG28 ist so oft zu wiederholen, wie zu den unterschiedlichen Messprodukt-Position-Codes zu dem innerhalb derselben SG27 LIN im PIA+5 DE7140 (Erforderliches Produkt Konfigurationserlaubnis fü...
326 /// EXTERNAL: Requires context from outside the message.
327 fn evaluate_2065(&self, ctx: &EvaluationContext) -> ConditionResult {
328 ctx.external
329 .evaluate("sg28_repeat_count_schwellwert_smgw_config")
330 }
331
332 /// [2066] Diese SG28 ist so oft zu wiederholen, wie zu den unterschiedlichen Messprodukt-Position-Codes zu dem innerhalb derselben SG27 LIN im PIA+5 DE7140 (Erforderliches Produkt Konfigurationserlaubnis fü...
333 /// EXTERNAL: Requires context from outside the message.
334 fn evaluate_2066(&self, ctx: &EvaluationContext) -> ConditionResult {
335 ctx.external
336 .evaluate("sg28_repeat_count_schwellwert_smgw_werte_typ2")
337 }
338
339 /// [1] Wenn DTM+203 (Ausführungsdatum) nicht vorhanden
340 fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
341 ctx.lacks_qualifier("DTM", 0, "203")
342 }
343
344 /// [2] Wenn DTM+469 (Beginn zum (nächstmöglichen Termin)) nicht vorhanden
345 fn evaluate_2(&self, ctx: &EvaluationContext) -> ConditionResult {
346 ctx.lacks_qualifier("DTM", 0, "469")
347 }
348
349 /// [10] MP-ID nur aus Sparte Strom
350 /// EXTERNAL: Requires context from outside the message.
351 fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
352 ctx.external.evaluate("mp_id_strom_only")
353 }
354
355 /// [15] Wenn DTM+76 (Datum zum geplanten Leistungsbeginn) nicht vorhanden
356 fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
357 ctx.lacks_qualifier("DTM", 0, "76")
358 }
359
360 /// [16] Wenn SG1 RFF+Z41 (Referenznummer des Vorgangs der Anmeldung nach WiM) nicht vorhanden
361 fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
362 ctx.lacks_qualifier("RFF", 0, "Z41")
363 }
364
365 /// [17] Wenn SG1 RFF+Z41 (Referenznummer des Vorgangs der Anmeldung nach WiM) vorhanden
366 fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
367 ctx.has_qualifier("RFF", 0, "Z41")
368 }
369
370 /// [18] Wenn IMD++Z55 (Änderung Konfiguration) vorhanden
371 fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
372 ctx.has_qualifier("IMD", 1, "Z55")
373 }
374
375 /// [19] Wenn SG27 LIN++Z64 (Erforderliches Produkt Schaltzeitdefinitionen) vorhanden
376 fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
377 ctx.has_qualifier("LIN", 1, "Z64")
378 }
379
380 /// [20] Wenn SG27 LIN++Z65 (Erforderliches Produkt Leistungskurvendefinitionen) vorhanden
381 fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
382 ctx.has_qualifier("LIN", 1, "Z65")
383 }
384
385 /// [21] Wenn SG27 LIN++Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) vorhanden
386 fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
387 ctx.has_qualifier("LIN", 1, "Z66")
388 }
389
390 /// [22] Wenn SG27 LIN++Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) vorhanden
391 fn evaluate_22(&self, ctx: &EvaluationContext) -> ConditionResult {
392 ctx.has_qualifier("LIN", 1, "Z67")
393 }
394
395 /// [23] Wenn SG27 LIN++Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) vorhanden
396 fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
397 ctx.has_qualifier("LIN", 1, "Z68")
398 }
399
400 /// [24] Wenn Produkt bestellt werden soll
401 /// EXTERNAL: Requires context from outside the message.
402 // REVIEW: Whether a product should be ordered is a business-intent condition. In a REQOTE context this could theoretically be signalled by presence of PIA+5, but the AHB phrasing 'soll bestellt werden' indicates a business decision or workflow state that cannot be reliably inferred from message structure alone. (medium confidence)
403 fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
404 ctx.external.evaluate("product_to_be_ordered")
405 }
406
407 /// [25] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Marktlokation angegeben ist.
408 fn evaluate_25(&self, ctx: &EvaluationContext) -> ConditionResult {
409 // Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Marktlokation angegeben ist.
410 // LOC elements[0][0] == "172" (qualifier), elements[1][0] == DE3225 (Identifikator)
411 // Marktlokation ID: 11 digits with Luhn check digit
412 let segs = ctx.find_segments_with_qualifier("LOC", 0, "172");
413 match segs
414 .first()
415 .and_then(|s| s.elements.get(1))
416 .and_then(|e| e.first())
417 {
418 Some(val) if !val.is_empty() => validate_malo_id(val),
419 _ => ConditionResult::Unknown,
420 }
421 }
422
423 /// [26] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Messlokation angegeben ist.
424 fn evaluate_26(&self, ctx: &EvaluationContext) -> ConditionResult {
425 // Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Messlokation angegeben ist.
426 // LOC elements[0][0] == "172" (qualifier), elements[1][0] == DE3225 (Identifikator)
427 // Messlokation ID: 33 alphanumeric characters (Zählpunkt format)
428 let segs = ctx.find_segments_with_qualifier("LOC", 0, "172");
429 match segs
430 .first()
431 .and_then(|s| s.elements.get(1))
432 .and_then(|e| e.first())
433 {
434 Some(val) if !val.is_empty() => validate_zahlpunkt(val),
435 _ => ConditionResult::Unknown,
436 }
437 }
438
439 /// [27] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Netzlokation angegeben ist.
440 // REVIEW: Checks LOC+172 DE3225 value as a Netzlokation ID by exclusion: it is present but does not match Marktlokation format (11-digit Luhn) nor Messlokation format (33 alphanumeric). Medium confidence because there is no dedicated Netzlokation validator — the logic relies on the three location types being mutually exclusive and exhaustive for this qualifier. (medium confidence)
441 fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
442 // Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Netzlokation angegeben ist.
443 // LOC elements[0][0] == "172" (qualifier), elements[1][0] == DE3225 (Identifikator)
444 // Netzlokation ID: neither MaLo (11 digits Luhn) nor MeLo (33 alphanumeric)
445 let segs = ctx.find_segments_with_qualifier("LOC", 0, "172");
446 match segs
447 .first()
448 .and_then(|s| s.elements.get(1))
449 .and_then(|e| e.first())
450 {
451 Some(val) if !val.is_empty() => {
452 let is_malo = matches!(validate_malo_id(val), ConditionResult::True);
453 let is_melo = matches!(validate_zahlpunkt(val), ConditionResult::True);
454 ConditionResult::from(!is_malo && !is_melo)
455 }
456 _ => ConditionResult::Unknown,
457 }
458 }
459
460 /// [28] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Steuerbaren Ressource angegeben ist.
461 /// EXTERNAL: Requires context from outside the message.
462 fn evaluate_28(&self, ctx: &EvaluationContext) -> ConditionResult {
463 ctx.external.evaluate("loc_172_is_steuerbare_ressource")
464 }
465
466 /// [29] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.1 „Konfigurationsprodukte Schaltzeitdefinition“ enthalten sind.
467 /// EXTERNAL: Requires context from outside the message.
468 fn evaluate_29(&self, ctx: &EvaluationContext) -> ConditionResult {
469 ctx.external
470 .evaluate("product_in_konfiguration_schaltzeitdefinition")
471 }
472
473 /// [30] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.2 „Konfigurationsprodukte Leistungskurvendefinition“ enthalten sind.
474 /// EXTERNAL: Requires context from outside the message.
475 fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
476 ctx.external
477 .evaluate("product_in_konfiguration_leistungskurvendefinition")
478 }
479
480 /// [31] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.3 „Konfigurationsprodukte Ad-Hoc-Steuerkanal“ enthalten sind.
481 /// EXTERNAL: Requires context from outside the message.
482 fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
483 ctx.external
484 .evaluate("product_in_konfiguration_adhoc_steuerkanal")
485 }
486
487 /// [32] Es sind nur die Messprodukte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.5 „Messprodukte für Werte nach Typ 2 aus Backend für LF und NB“ enthalten sind.
488 /// EXTERNAL: Requires context from outside the message.
489 fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
490 ctx.external.evaluate("product_in_messprodukte_typ2_lf_nb")
491 }
492
493 /// [35] Wenn MP-ID in SG11 NAD+MS mit Rolle LF vorhanden
494 /// EXTERNAL: Requires context from outside the message.
495 // REVIEW: NAD+MS identifies the sender by MP-ID (elements[0]=MS), but the market role 'LF' (Lieferant) cannot be determined from the NAD segment alone — it requires an external participant role registry lookup. The segment only carries the ID, not the role. (medium confidence)
496 fn evaluate_35(&self, ctx: &EvaluationContext) -> ConditionResult {
497 ctx.external.evaluate("sender_is_lf")
498 }
499
500 /// [36] Wenn MP-ID in SG11 NAD+MS mit Rolle NB vorhanden
501 /// EXTERNAL: Requires context from outside the message.
502 // REVIEW: Same pattern as condition 35: NAD+MS carries the sender MP-ID but the market role 'NB' (Netzbetreiber) cannot be inferred from the EDIFACT segment alone — requires external role registry. (medium confidence)
503 fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
504 ctx.external.evaluate("sender_is_nb")
505 }
506
507 /// [39] wenn im DE3155 in demselben COM der Code EM vorhanden ist
508 fn evaluate_39(&self, ctx: &EvaluationContext) -> ConditionResult {
509 // COM segment: C076 composite — elements[0][0]=DE3148 (address), elements[0][1]=DE3155 (qualifier)
510 let coms = ctx.find_segments("COM");
511 if coms.is_empty() {
512 return ConditionResult::False;
513 }
514 ConditionResult::from(coms.iter().any(|s| {
515 s.elements
516 .first()
517 .and_then(|e| e.get(1))
518 .is_some_and(|v| v == "EM")
519 }))
520 }
521
522 /// [40] wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
523 fn evaluate_40(&self, ctx: &EvaluationContext) -> ConditionResult {
524 // COM segment: C076 composite — elements[0][0]=DE3148 (address), elements[0][1]=DE3155 (qualifier)
525 // TE=Telephone, FX=Fax, AJ=Unknown, AL=Unknown (telephony-related codes)
526 let coms = ctx.find_segments("COM");
527 if coms.is_empty() {
528 return ConditionResult::False;
529 }
530 ConditionResult::from(coms.iter().any(|s| {
531 s.elements
532 .first()
533 .and_then(|e| e.get(1))
534 .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
535 }))
536 }
537
538 /// [492] wenn MP-ID in NAD+MR aus Sparte Strom
539 /// EXTERNAL: Requires context from outside the message.
540 // REVIEW: Checks whether the MP-ID in NAD+MR belongs to the electricity sector (Sparte Strom). The sector of a market participant cannot be derived from the EDIFACT message structure — it requires external market participant registry lookup. Delegated to external provider. (medium confidence)
541 fn evaluate_492(&self, ctx: &EvaluationContext) -> ConditionResult {
542 ctx.external.evaluate("recipient_is_electricity_sector")
543 }
544
545 /// [493] wenn MP-ID in NAD+MR aus Sparte Gas
546 /// EXTERNAL: Requires context from outside the message.
547 // REVIEW: Checks whether the MP-ID in NAD+MR belongs to the gas sector (Sparte Gas). Same reasoning as condition 492 — sector membership requires external registry lookup. (medium confidence)
548 fn evaluate_493(&self, ctx: &EvaluationContext) -> ConditionResult {
549 ctx.external.evaluate("recipient_is_gas_sector")
550 }
551
552 /// [500] Hinweis: Angabe eines technischen Ansprechpartners für die Geräteübernahme
553 fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
554 // Hinweis: Angabe eines technischen Ansprechpartners für die Geräteübernahme
555 // Informational note — always applies
556 ConditionResult::True
557 }
558
559 /// [501] Hinweis: Angabe eines Ansprechpartners für die Rechnungsabwicklung
560 fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
561 // Hinweis: Angabe eines Ansprechpartners für die Rechnungsabwicklung
562 // Informational note — always applies
563 ConditionResult::True
564 }
565
566 /// [502] Hinweis: Verwendung der ID der Marktlokation
567 fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
568 // Hinweis: Verwendung der ID der Marktlokation
569 // Informational note — always applies
570 ConditionResult::True
571 }
572
573 /// [503] Hinweis: Verwendung der ID der Messlokation
574 fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
575 // Hinweis: Verwendung der ID der Messlokation
576 // Informational note — always applies
577 ConditionResult::True
578 }
579
580 /// [504] Hinweis: Verwendung der ID der Tranche
581 fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
582 // Hinweis: Verwendung der ID der Tranche — informational note, always applies
583 ConditionResult::True
584 }
585
586 /// [507] Hinweis: Vorgangsnummer aus SG4 IDE+24 DE7402 der UTILMD mit BGM+E01 mit der die Anmeldung des MSB-Wechsels erfolgt ist.
587 fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
588 // Hinweis: Vorgangsnummer aus SG4 IDE+24 DE7402 der UTILMD — informational note, always applies
589 ConditionResult::True
590 }
591
592 /// [508] Hinweis: Wert aus BGM+Z73 DE1004 der IFTSTA mit der die Antwort auf die Bestellung der Konfiguration übermittelt wurde
593 fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
594 // Hinweis: Wert aus BGM+Z73 DE1004 der IFTSTA — informational note, always applies
595 ConditionResult::True
596 }
597
598 /// [509] Hinweis: Vorgangsnummer aus CNI DE1490 der IFTSTA mit BGM+Z73 mit der die Antwort auf die Bestellung der Konfiguration übermittelt wurde
599 fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
600 // Hinweis: Vorgangsnummer aus CNI DE1490 der IFTSTA — informational note, always applies
601 ConditionResult::True
602 }
603
604 /// [510] Hinweis: Verwendung der ID der Netzlokation
605 fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
606 // Hinweis: Verwendung der ID der Netzlokation — informational note, always applies
607 ConditionResult::True
608 }
609
610 /// [511] Hinweis: Verwendung der ID der Steuerbaren Ressource
611 fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
612 ConditionResult::True
613 }
614
615 /// [512] Hinweis: Für den Empfang der Werte nach Typ 2 aus dem SMGW.
616 fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
617 ConditionResult::True
618 }
619
620 /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
621 fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
622 ConditionResult::True
623 }
624
625 /// [515] Hinweis: Es ist eine URI IPv4 für die Bereitstellung der Werte anzugeben.
626 fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
627 ConditionResult::True
628 }
629
630 /// [516] Hinweis: Es ist eine URI IPv6 für die Bereitstellung der Werte anzugeben.
631 fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
632 ConditionResult::True
633 }
634
635 /// [903] Format: Möglicher Wert: 1
636 fn evaluate_903(&self, _ctx: &EvaluationContext) -> ConditionResult {
637 ConditionResult::True
638 }
639
640 /// [906] Format: max. 3 Nachkommastellen
641 fn evaluate_906(&self, _ctx: &EvaluationContext) -> ConditionResult {
642 ConditionResult::True
643 }
644
645 /// [931] Format: ZZZ = +00
646 fn evaluate_931(&self, _ctx: &EvaluationContext) -> ConditionResult {
647 ConditionResult::True
648 }
649
650 /// [932] Format: HHMM = 2200
651 fn evaluate_932(&self, _ctx: &EvaluationContext) -> ConditionResult {
652 ConditionResult::True
653 }
654
655 /// [933] Format: HHMM = 2300
656 fn evaluate_933(&self, _ctx: &EvaluationContext) -> ConditionResult {
657 ConditionResult::True
658 }
659
660 /// [934] Format: HHMM = 0400
661 fn evaluate_934(&self, _ctx: &EvaluationContext) -> ConditionResult {
662 ConditionResult::True
663 }
664
665 /// [935] Format: HHMM = 0500
666 fn evaluate_935(&self, _ctx: &EvaluationContext) -> ConditionResult {
667 ConditionResult::True
668 }
669
670 /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
671 fn evaluate_939(&self, _ctx: &EvaluationContext) -> ConditionResult {
672 ConditionResult::True
673 }
674
675 /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
676 fn evaluate_940(&self, _ctx: &EvaluationContext) -> ConditionResult {
677 ConditionResult::True
678 }
679
680 /// [950] Format: Marktlokations-ID
681 fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
682 ConditionResult::True
683 }
684
685 /// [951] Format: Zählpunktbezeichnung
686 fn evaluate_951(&self, ctx: &EvaluationContext) -> ConditionResult {
687 ctx.format_check_qualified("LOC", 0, "Z17", 1, 0, validate_zahlpunkt)
688 }
689
690 /// [960] Format: Netzlokations-ID
691 // HAND-EDITED: a Netzlokations-ID is "E" + ten alphanumerics (`E1688117482`),
692 // not MaLo-shaped; `validate_malo_id` rejected every real one (#173).
693 fn evaluate_960(&self, ctx: &EvaluationContext) -> ConditionResult {
694 ctx.format_check_qualified("LOC", 0, "Z18", 1, 0, validate_nelo_id)
695 }
696
697 /// [961] Format: SR-ID
698 // REVIEW: SR-ID (SteuerbareRessource-ID) uses the same 11-digit Luhn check digit format as MaLo-ID. LOC+Z19 is the standard qualifier for SteuerbareRessource. (medium confidence)
699 fn evaluate_961(&self, ctx: &EvaluationContext) -> ConditionResult {
700 ctx.format_check_qualified("LOC", 0, "Z19", 1, 0, validate_malo_id)
701 }
702
703 /// [962] Format: max. 6 Vorkommastellen
704 fn evaluate_962(&self, ctx: &EvaluationContext) -> ConditionResult {
705 ctx.format_check("QTY", 0, 1, |val| validate_max_integer_digits(val, 6))
706 }
707
708 /// [2005] Pro Nachricht ist die SG27 genau einmal anzugeben
709 // REVIEW: SG27 must appear exactly once per message. LIN is the entry segment for SG27. Counting all LIN segments gives the SG27 instance count. Returns True when exactly one LIN exists. (medium confidence)
710 fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
711 let lin_count = ctx.find_segments("LIN").len();
712 ConditionResult::from(lin_count == 1)
713 }
714
715 /// [2060] Pro Nachricht ist die SG27 LIN+Z64 (Erforderliches Produkt Schaltzeitdefinitionen) maximal einmal anzugeben
716 // REVIEW: LIN with DE1229=Z64 (Erforderliches Produkt Schaltzeitdefinitionen) must appear at most once. Per segment structure reference, DE1229 (Handlung, Code) is at elements[1][0]. Returns True when 0 or 1 such LIN segments exist. (medium confidence)
717 fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
718 let lins = ctx.find_segments("LIN");
719 let count = lins
720 .iter()
721 .filter(|s| {
722 s.elements
723 .get(1)
724 .and_then(|e| e.first())
725 .is_some_and(|v| v == "Z64")
726 })
727 .count();
728 ConditionResult::from(count <= 1)
729 }
730
731 /// [2061] Pro Nachricht ist die SG27 LIN++Z65 (Erforderliches Produkt Leistungskurvendefinitionen) maximal einmal anzugeben
732 // REVIEW: LIN with DE1229=Z65 (Erforderliches Produkt Leistungskurvendefinitionen) must appear at most once. Per segment structure reference, DE1229 is at elements[1][0]. Returns True when 0 or 1 such LIN segments exist. (medium confidence)
733 fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
734 let lins = ctx.find_segments("LIN");
735 let count = lins
736 .iter()
737 .filter(|s| {
738 s.elements
739 .get(1)
740 .and_then(|e| e.first())
741 .is_some_and(|v| v == "Z65")
742 })
743 .count();
744 ConditionResult::from(count <= 1)
745 }
746
747 /// [2062] Pro Nachricht ist die SG27 LIN++Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) maximal einmal anzugeben
748 // REVIEW: LIN with DE1229=Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) must appear at most once. Per segment structure reference, DE1229 is at elements[1][0]. Returns True when 0 or 1 such LIN segments exist. (medium confidence)
749 fn evaluate_2062(&self, ctx: &EvaluationContext) -> ConditionResult {
750 let lins = ctx.find_segments("LIN");
751 let count = lins
752 .iter()
753 .filter(|s| {
754 s.elements
755 .get(1)
756 .and_then(|e| e.first())
757 .is_some_and(|v| v == "Z66")
758 })
759 .count();
760 ConditionResult::from(count <= 1)
761 }
762
763 /// [2063] Pro Nachricht ist die SG27 LIN++Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) maximal einmal anzugeben
764 // REVIEW: LIN with DE1229=Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) must appear at most once. Per segment structure reference, DE1229 is at elements[1][0]. Returns True when 0 or 1 such LIN segments exist. (medium confidence)
765 fn evaluate_2063(&self, ctx: &EvaluationContext) -> ConditionResult {
766 let lins = ctx.find_segments("LIN");
767 let count = lins
768 .iter()
769 .filter(|s| {
770 s.elements
771 .get(1)
772 .and_then(|e| e.first())
773 .is_some_and(|v| v == "Z67")
774 })
775 .count();
776 ConditionResult::from(count <= 1)
777 }
778
779 /// [2064] Pro Nachricht ist die SG27 LIN++Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) maximal einmal anzugeben
780 // REVIEW: LIN with DE1229=Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) must appear at most once. Per segment structure reference, DE1229 is at elements[1][0]. Returns True when 0 or 1 such LIN segments exist. (medium confidence)
781 fn evaluate_2064(&self, ctx: &EvaluationContext) -> ConditionResult {
782 let lins = ctx.find_segments("LIN");
783 let count = lins
784 .iter()
785 .filter(|s| {
786 s.elements
787 .get(1)
788 .and_then(|e| e.first())
789 .is_some_and(|v| v == "Z68")
790 })
791 .count();
792 ConditionResult::from(count <= 1)
793 }
794}