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 // HAND-EDITED: judged on the field's own value; the stub returned
637 // True, so REQOTE 35005's DTM+469 `[UB1]` passed any time of day.
638 fn evaluate_903(&self, ctx: &EvaluationContext) -> ConditionResult {
639 ctx.format_check_resolved(|val| validate_numeric(val, "==", 1.0))
640 }
641
642 /// [906] Format: max. 3 Nachkommastellen
643 // HAND-EDITED: judged on the field's own value; the stub returned
644 // True, so REQOTE 35005's DTM+469 `[UB1]` passed any time of day.
645 fn evaluate_906(&self, ctx: &EvaluationContext) -> ConditionResult {
646 ctx.format_check_resolved(|val| validate_max_decimal_places(val, 3))
647 }
648
649 /// [931] Format: ZZZ = +00
650 // HAND-EDITED: judged on the field's own value; the stub returned
651 // True, so REQOTE 35005's DTM+469 `[UB1]` passed any time of day.
652 fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
653 ctx.format_check("DTM", 0, 1, validate_timezone_utc)
654 }
655
656 /// [932] Format: HHMM = 2200
657 // HAND-EDITED: judged on the field's own value; the stub returned
658 // True, so REQOTE 35005's DTM+469 `[UB1]` passed any time of day.
659 fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
660 ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
661 }
662
663 /// [933] Format: HHMM = 2300
664 // HAND-EDITED: judged on the field's own value; the stub returned
665 // True, so REQOTE 35005's DTM+469 `[UB1]` passed any time of day.
666 fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
667 ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
668 }
669
670 /// [934] Format: HHMM = 0400
671 // HAND-EDITED: judged on the field's own value; the stub returned
672 // True, so REQOTE 35005's DTM+469 `[UB1]` passed any time of day.
673 fn evaluate_934(&self, ctx: &EvaluationContext) -> ConditionResult {
674 ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0400"))
675 }
676
677 /// [935] Format: HHMM = 0500
678 // HAND-EDITED: judged on the field's own value; the stub returned
679 // True, so REQOTE 35005's DTM+469 `[UB1]` passed any time of day.
680 fn evaluate_935(&self, ctx: &EvaluationContext) -> ConditionResult {
681 ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0500"))
682 }
683
684 /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
685 // HAND-EDITED: judged on the field's own value; the stub returned
686 // True, so REQOTE 35005's DTM+469 `[UB1]` passed any time of day.
687 fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
688 ctx.format_check("COM", 0, 0, validate_email)
689 }
690
691 /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
692 // HAND-EDITED: judged on the field's own value; the stub returned
693 // True, so REQOTE 35005's DTM+469 `[UB1]` passed any time of day.
694 fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
695 ctx.format_check("COM", 0, 0, validate_phone)
696 }
697
698 /// [950] Format: Marktlokations-ID
699 // HAND-EDITED: was a stub returning True, so any Meldepunkt passed the
700 // MaLo branch of its format alternatives. As QUOTES [950].
701 // HAND-EDITED (#373): the condition sits on LOC+172 and RFF C506 1154, not on
702 // LOC+Z16; judge the value it is attached to.
703 fn evaluate_950(&self, ctx: &EvaluationContext) -> ConditionResult {
704 ctx.format_check_resolved(validate_malo_id)
705 }
706
707 /// [951] Format: Zählpunktbezeichnung
708 // HAND-EDITED (#373): the condition sits on LOC+172 and RFF C506 1154, not on
709 // LOC+Z17; judge the value it is attached to.
710 fn evaluate_951(&self, ctx: &EvaluationContext) -> ConditionResult {
711 ctx.format_check_resolved(validate_zahlpunkt)
712 }
713
714 /// [960] Format: Netzlokations-ID
715 // HAND-EDITED: a Netzlokations-ID is "E" + ten alphanumerics (`E1688117482`),
716 // not MaLo-shaped; `validate_malo_id` rejected every real one (#173).
717 // HAND-EDITED (#373): the condition sits on LOC+172 and RFF C506 1154, not on
718 // LOC+Z18; judge the value it is attached to.
719 fn evaluate_960(&self, ctx: &EvaluationContext) -> ConditionResult {
720 ctx.format_check_resolved(validate_nelo_id)
721 }
722
723 /// [961] Format: SR-ID
724 // 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)
725 // HAND-EDITED: an SR-ID ("C" + 10 characters), not a MaLo-ID.
726 // HAND-EDITED (#373): the condition sits on LOC+172 and RFF C506 1154, not on
727 // LOC+Z19; judge the value it is attached to.
728 fn evaluate_961(&self, ctx: &EvaluationContext) -> ConditionResult {
729 ctx.format_check_resolved(validate_sr_id)
730 }
731
732 /// [962] Format: max. 6 Vorkommastellen
733 fn evaluate_962(&self, ctx: &EvaluationContext) -> ConditionResult {
734 ctx.format_check("QTY", 0, 1, |val| validate_max_integer_digits(val, 6))
735 }
736
737 /// [2005] Pro Nachricht ist die SG27 genau einmal anzugeben
738 // 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)
739 fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
740 let lin_count = ctx.find_segments("LIN").len();
741 ConditionResult::from(lin_count == 1)
742 }
743
744 /// [2060] Pro Nachricht ist die SG27 LIN+Z64 (Erforderliches Produkt Schaltzeitdefinitionen) maximal einmal anzugeben
745 // 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)
746 fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
747 let lins = ctx.find_segments("LIN");
748 let count = lins
749 .iter()
750 .filter(|s| {
751 s.elements
752 .get(1)
753 .and_then(|e| e.first())
754 .is_some_and(|v| v == "Z64")
755 })
756 .count();
757 ConditionResult::from(count <= 1)
758 }
759
760 /// [2061] Pro Nachricht ist die SG27 LIN++Z65 (Erforderliches Produkt Leistungskurvendefinitionen) maximal einmal anzugeben
761 // 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)
762 fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
763 let lins = ctx.find_segments("LIN");
764 let count = lins
765 .iter()
766 .filter(|s| {
767 s.elements
768 .get(1)
769 .and_then(|e| e.first())
770 .is_some_and(|v| v == "Z65")
771 })
772 .count();
773 ConditionResult::from(count <= 1)
774 }
775
776 /// [2062] Pro Nachricht ist die SG27 LIN++Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) maximal einmal anzugeben
777 // 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)
778 fn evaluate_2062(&self, ctx: &EvaluationContext) -> ConditionResult {
779 let lins = ctx.find_segments("LIN");
780 let count = lins
781 .iter()
782 .filter(|s| {
783 s.elements
784 .get(1)
785 .and_then(|e| e.first())
786 .is_some_and(|v| v == "Z66")
787 })
788 .count();
789 ConditionResult::from(count <= 1)
790 }
791
792 /// [2063] Pro Nachricht ist die SG27 LIN++Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) maximal einmal anzugeben
793 // 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)
794 fn evaluate_2063(&self, ctx: &EvaluationContext) -> ConditionResult {
795 let lins = ctx.find_segments("LIN");
796 let count = lins
797 .iter()
798 .filter(|s| {
799 s.elements
800 .get(1)
801 .and_then(|e| e.first())
802 .is_some_and(|v| v == "Z67")
803 })
804 .count();
805 ConditionResult::from(count <= 1)
806 }
807
808 /// [2064] Pro Nachricht ist die SG27 LIN++Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) maximal einmal anzugeben
809 // 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)
810 fn evaluate_2064(&self, ctx: &EvaluationContext) -> ConditionResult {
811 let lins = ctx.find_segments("LIN");
812 let count = lins
813 .iter()
814 .filter(|s| {
815 s.elements
816 .get(1)
817 .and_then(|e| e.first())
818 .is_some_and(|v| v == "Z68")
819 })
820 .count();
821 ConditionResult::from(count <= 1)
822 }
823}