automapper_validation/generated/fv2504/quotes_conditions_fv2504.rs
1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2504/QUOTES_AHB_1_0_Fehlerkorrektur_20241213.xml
4// Generated: 2026-03-12T10:21:41Z
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 QUOTES FV2504.
17pub struct QuotesConditionEvaluatorFV2504 {
18 // External condition IDs that require runtime context.
19 external_conditions: std::collections::HashSet<u32>,
20}
21
22impl Default for QuotesConditionEvaluatorFV2504 {
23 fn default() -> Self {
24 let mut external_conditions = std::collections::HashSet::new();
25 external_conditions.insert(1);
26 external_conditions.insert(4);
27 external_conditions.insert(13);
28 external_conditions.insert(29);
29 external_conditions.insert(31);
30 external_conditions.insert(39);
31 external_conditions.insert(44);
32 external_conditions.insert(54);
33 external_conditions.insert(56);
34 external_conditions.insert(57);
35 external_conditions.insert(58);
36 external_conditions.insert(59);
37 external_conditions.insert(60);
38 external_conditions.insert(61);
39 external_conditions.insert(62);
40 external_conditions.insert(63);
41 external_conditions.insert(69);
42 external_conditions.insert(70);
43 external_conditions.insert(71);
44 external_conditions.insert(74);
45 external_conditions.insert(75);
46 external_conditions.insert(77);
47 external_conditions.insert(78);
48 external_conditions.insert(492);
49 external_conditions.insert(493);
50 external_conditions.insert(494);
51 external_conditions.insert(2066);
52 Self {
53 external_conditions,
54 }
55 }
56}
57
58impl ConditionEvaluator for QuotesConditionEvaluatorFV2504 {
59 fn message_type(&self) -> &str {
60 "QUOTES"
61 }
62
63 fn format_version(&self) -> &str {
64 "FV2504"
65 }
66
67 fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
68 match condition {
69 1 => self.evaluate_1(ctx),
70 2 => self.evaluate_2(ctx),
71 3 => self.evaluate_3(ctx),
72 4 => self.evaluate_4(ctx),
73 5 => self.evaluate_5(ctx),
74 8 => self.evaluate_8(ctx),
75 9 => self.evaluate_9(ctx),
76 10 => self.evaluate_10(ctx),
77 11 => self.evaluate_11(ctx),
78 12 => self.evaluate_12(ctx),
79 13 => self.evaluate_13(ctx),
80 15 => self.evaluate_15(ctx),
81 16 => self.evaluate_16(ctx),
82 17 => self.evaluate_17(ctx),
83 18 => self.evaluate_18(ctx),
84 20 => self.evaluate_20(ctx),
85 21 => self.evaluate_21(ctx),
86 22 => self.evaluate_22(ctx),
87 25 => self.evaluate_25(ctx),
88 26 => self.evaluate_26(ctx),
89 27 => self.evaluate_27(ctx),
90 28 => self.evaluate_28(ctx),
91 29 => self.evaluate_29(ctx),
92 30 => self.evaluate_30(ctx),
93 31 => self.evaluate_31(ctx),
94 39 => self.evaluate_39(ctx),
95 44 => self.evaluate_44(ctx),
96 49 => self.evaluate_49(ctx),
97 50 => self.evaluate_50(ctx),
98 51 => self.evaluate_51(ctx),
99 52 => self.evaluate_52(ctx),
100 53 => self.evaluate_53(ctx),
101 54 => self.evaluate_54(ctx),
102 55 => self.evaluate_55(ctx),
103 56 => self.evaluate_56(ctx),
104 57 => self.evaluate_57(ctx),
105 58 => self.evaluate_58(ctx),
106 59 => self.evaluate_59(ctx),
107 60 => self.evaluate_60(ctx),
108 61 => self.evaluate_61(ctx),
109 62 => self.evaluate_62(ctx),
110 63 => self.evaluate_63(ctx),
111 65 => self.evaluate_65(ctx),
112 66 => self.evaluate_66(ctx),
113 67 => self.evaluate_67(ctx),
114 68 => self.evaluate_68(ctx),
115 69 => self.evaluate_69(ctx),
116 70 => self.evaluate_70(ctx),
117 71 => self.evaluate_71(ctx),
118 72 => self.evaluate_72(ctx),
119 73 => self.evaluate_73(ctx),
120 74 => self.evaluate_74(ctx),
121 75 => self.evaluate_75(ctx),
122 77 => self.evaluate_77(ctx),
123 78 => self.evaluate_78(ctx),
124 79 => self.evaluate_79(ctx),
125 80 => self.evaluate_80(ctx),
126 81 => self.evaluate_81(ctx),
127 82 => self.evaluate_82(ctx),
128 83 => self.evaluate_83(ctx),
129 84 => self.evaluate_84(ctx),
130 85 => self.evaluate_85(ctx),
131 86 => self.evaluate_86(ctx),
132 87 => self.evaluate_87(ctx),
133 88 => self.evaluate_88(ctx),
134 490 => self.evaluate_490(ctx),
135 491 => self.evaluate_491(ctx),
136 492 => self.evaluate_492(ctx),
137 493 => self.evaluate_493(ctx),
138 494 => self.evaluate_494(ctx),
139 500 => self.evaluate_500(ctx),
140 501 => self.evaluate_501(ctx),
141 502 => self.evaluate_502(ctx),
142 503 => self.evaluate_503(ctx),
143 504 => self.evaluate_504(ctx),
144 505 => self.evaluate_505(ctx),
145 506 => self.evaluate_506(ctx),
146 507 => self.evaluate_507(ctx),
147 511 => self.evaluate_511(ctx),
148 512 => self.evaluate_512(ctx),
149 513 => self.evaluate_513(ctx),
150 514 => self.evaluate_514(ctx),
151 516 => self.evaluate_516(ctx),
152 903 => self.evaluate_903(ctx),
153 906 => self.evaluate_906(ctx),
154 908 => self.evaluate_908(ctx),
155 911 => self.evaluate_911(ctx),
156 912 => self.evaluate_912(ctx),
157 914 => self.evaluate_914(ctx),
158 931 => self.evaluate_931(ctx),
159 932 => self.evaluate_932(ctx),
160 933 => self.evaluate_933(ctx),
161 934 => self.evaluate_934(ctx),
162 935 => self.evaluate_935(ctx),
163 939 => self.evaluate_939(ctx),
164 940 => self.evaluate_940(ctx),
165 942 => self.evaluate_942(ctx),
166 950 => self.evaluate_950(ctx),
167 951 => self.evaluate_951(ctx),
168 960 => self.evaluate_960(ctx),
169 961 => self.evaluate_961(ctx),
170 962 => self.evaluate_962(ctx),
171 2042 => self.evaluate_2042(ctx),
172 2060 => self.evaluate_2060(ctx),
173 2061 => self.evaluate_2061(ctx),
174 2062 => self.evaluate_2062(ctx),
175 2063 => self.evaluate_2063(ctx),
176 2064 => self.evaluate_2064(ctx),
177 2066 => self.evaluate_2066(ctx),
178 2068 => self.evaluate_2068(ctx),
179 2069 => self.evaluate_2069(ctx),
180 2070 => self.evaluate_2070(ctx),
181 2071 => self.evaluate_2071(ctx),
182 2072 => self.evaluate_2072(ctx),
183 2073 => self.evaluate_2073(ctx),
184 _ => ConditionResult::Unknown,
185 }
186 }
187
188 fn is_external(&self, condition: u32) -> bool {
189 self.external_conditions.contains(&condition)
190 }
191 fn is_known(&self, condition: u32) -> bool {
192 matches!(
193 condition,
194 1 | 2
195 | 3
196 | 4
197 | 5
198 | 8
199 | 9
200 | 10
201 | 11
202 | 12
203 | 13
204 | 15
205 | 16
206 | 17
207 | 18
208 | 20
209 | 21
210 | 22
211 | 25
212 | 26
213 | 27
214 | 28
215 | 29
216 | 30
217 | 31
218 | 39
219 | 44
220 | 49
221 | 50
222 | 51
223 | 52
224 | 53
225 | 54
226 | 55
227 | 56
228 | 57
229 | 58
230 | 59
231 | 60
232 | 61
233 | 62
234 | 63
235 | 65
236 | 66
237 | 67
238 | 68
239 | 69
240 | 70
241 | 71
242 | 72
243 | 73
244 | 74
245 | 75
246 | 77
247 | 78
248 | 79
249 | 80
250 | 81
251 | 82
252 | 83
253 | 84
254 | 85
255 | 86
256 | 87
257 | 88
258 | 490
259 | 491
260 | 492
261 | 493
262 | 494
263 | 500
264 | 501
265 | 502
266 | 503
267 | 504
268 | 505
269 | 506
270 | 507
271 | 511
272 | 512
273 | 513
274 | 514
275 | 516
276 | 903
277 | 906
278 | 908
279 | 911
280 | 912
281 | 914
282 | 931
283 | 932
284 | 933
285 | 934
286 | 935
287 | 939
288 | 940
289 | 942
290 | 950
291 | 951
292 | 960
293 | 961
294 | 962
295 | 2042
296 | 2060
297 | 2061
298 | 2062
299 | 2063
300 | 2064
301 | 2066
302 | 2068
303 | 2069
304 | 2070
305 | 2071
306 | 2072
307 | 2073
308 )
309 }
310}
311
312impl QuotesConditionEvaluatorFV2504 {
313 /// [31] Es sind nur die Artikelnummern erlaubt, die in der Codeliste der Artikelnummern des BDEW mit dem entsprechenden Prüfidentifikator versehen sind.
314 /// EXTERNAL: Requires context from outside the message.
315 fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
316 ctx.external.evaluate("artikel_number_in_bdew_codelist")
317 }
318
319 /// [56] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Messlokation angegeben ist.
320 /// EXTERNAL: Requires context from outside the message.
321 // REVIEW: Condition asks whether the ID in LOC+172 DE3225 (elements[1][0]) belongs to a Messlokation. While Messlokation IDs have a known 33-char format in Germany, the semantic type assignment (Messlokation vs other location types) ultimately requires external business context or a type registry lookup. Marked external. (medium confidence)
322 fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
323 ctx.external.evaluate("loc_172_id_is_messlokation")
324 }
325
326 /// [57] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Netzlokation angegeben ist.
327 /// EXTERNAL: Requires context from outside the message.
328 // REVIEW: Condition asks whether the ID in LOC+172 DE3225 (elements[1][0]) belongs to a Netzlokation. The ID type cannot be determined from EDIFACT message content alone — requires external type registry or format-based classification beyond the available validators. (medium confidence)
329 fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
330 ctx.external.evaluate("loc_172_id_is_netzlokation")
331 }
332
333 /// [58] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Steuerbaren Ressource angegeben ist.
334 /// EXTERNAL: Requires context from outside the message.
335 // REVIEW: Condition asks whether the ID in LOC+172 DE3225 (elements[1][0]) belongs to a Steuerbare Ressource. Same reasoning as [56] and [57] — semantic type assignment requires external business context. (medium confidence)
336 fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
337 ctx.external.evaluate("loc_172_id_is_steuerbare_ressource")
338 }
339
340 /// [59] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.1 „Konfigurationsprodukte Schaltzeitdefinition“ enthalten sind.
341 /// EXTERNAL: Requires context from outside the message.
342 fn evaluate_59(&self, ctx: &EvaluationContext) -> ConditionResult {
343 ctx.external
344 .evaluate("konfiguration_in_kapitel_4_1_codelist")
345 }
346
347 /// [60] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.2 „Konfigurationsprodukte Leistungskurvendefinition“ enthalten sind.
348 /// EXTERNAL: Requires context from outside the message.
349 fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
350 ctx.external.evaluate("config_product_leistungskurve")
351 }
352
353 /// [61] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.3 „Konfigurationsprodukte Ad-hoc-Steuerkanal“ enthalten sind.
354 /// EXTERNAL: Requires context from outside the message.
355 fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
356 ctx.external.evaluate("config_product_adhoc_steuerkanal")
357 }
358
359 /// [62] 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.
360 /// EXTERNAL: Requires context from outside the message.
361 fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
362 ctx.external.evaluate("messprodukts_typ2_backend_lf_nb")
363 }
364
365 /// [63] 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.
366 /// EXTERNAL: Requires context from outside the message.
367 fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
368 ctx.external.evaluate("messprodukts_typ2_smgw")
369 }
370
371 /// [69] Es sind nur Werte aus der EDI@Energy Codeliste der Artikelnummern und Artikel-ID erlaubt, die im Kapitel 3.5 "Abrechnung Messstellenbetrieb für die Sparte Strom" genannt sind.
372 /// EXTERNAL: Requires context from outside the message.
373 fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
374 ctx.external
375 .evaluate("artikel_id_abrechnung_messstellenbetrieb_strom")
376 }
377
378 /// [70] 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.
379 /// EXTERNAL: Requires context from outside the message.
380 fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
381 ctx.external
382 .evaluate("messprodukt_position_code_valid_typ2")
383 }
384
385 /// [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...
386 fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
387 match ctx.resolved_value {
388 Some(val) => is_mesz_utc(val),
389 None => ConditionResult::False,
390 }
391 }
392
393 /// [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)...
394 fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
395 match ctx.resolved_value {
396 Some(val) => is_mez_utc(val),
397 None => ConditionResult::False,
398 }
399 }
400
401 /// [961] Format: SR-ID
402 // REVIEW: Format: SR-ID — 11-digit Luhn check digit number. In QUOTES context, Steuerbare Ressource IDs typically appear in LOC+Z19 at elements[1][0]. validate_sr_id checks 11 digits with Luhn validation. Medium confidence because QUOTES segment structure may differ from UTILMD. (medium confidence)
403 fn evaluate_961(&self, ctx: &EvaluationContext) -> ConditionResult {
404 ctx.format_check_qualified("LOC", 0, "Z19", 1, 0, validate_sr_id)
405 }
406
407 /// [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ü...
408 /// EXTERNAL: Requires context from outside the message.
409 fn evaluate_2066(&self, ctx: &EvaluationContext) -> ConditionResult {
410 ctx.external
411 .evaluate("sg28_repetition_count_for_schwellwert_produkt")
412 }
413
414 /// [1] Wenn Position nicht angeboten werden kann, weil rechtliche Regelungen oder Rechte Dritter dem entgegenstehen
415 /// EXTERNAL: Requires context from outside the message.
416 fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
417 ctx.external.evaluate("position_legally_blocked")
418 }
419
420 /// [2] Wenn in derselben SG27 LIN IMD DE7081 (Produkt-/Leistungsbeschreibung) mit Code Z09 (Kann nicht angeboten werden) nicht vorhanden.
421 // REVIEW: Negates presence of IMD with DE7081=Z09 (Kann nicht angeboten werden) at elements[1][0] in any SG27 group instance. 'nicht vorhanden' means condition is True when Z09 is absent. any_group_has_qualifier falls back to message-wide if no group navigator is available. (medium confidence)
422 fn evaluate_2(&self, ctx: &EvaluationContext) -> ConditionResult {
423 match ctx.any_group_has_qualifier("IMD", 1, "Z09", &["SG27"]) {
424 ConditionResult::True => ConditionResult::False,
425 ConditionResult::False => ConditionResult::True,
426 ConditionResult::Unknown => ConditionResult::Unknown,
427 }
428 }
429
430 /// [3] Wenn CCI+++Z64 vorhanden ist
431 fn evaluate_3(&self, ctx: &EvaluationContext) -> ConditionResult {
432 ctx.has_qualifier("CCI", 2, "Z64")
433 }
434
435 /// [4] Wenn am Gerät vorhanden und abweichend von Gerätenummer
436 /// EXTERNAL: Requires context from outside the message.
437 fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
438 ctx.external
439 .evaluate("device_attribute_differs_from_device_number")
440 }
441
442 /// [5] Wenn in derselben SG27 LIN die Artikelnummer 9990001000649 vorhanden ist
443 // REVIEW: Checks if any SG27 group has LIN with article number 9990001000649 at elements[2][0] (C212 item number identification, first component). LIN is the entry segment of SG27 so each rep has exactly one LIN. (medium confidence)
444 fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
445 ctx.any_group_has_qualifier("LIN", 2, "9990001000649", &["SG27"])
446 }
447
448 /// [8] Wenn SG28 CCI+++E13 CAV+EHZ vorhanden
449 // REVIEW: CCI+++E13 maps to elements[2][0]=E13; CAV+EHZ maps to elements[0][0]=EHZ. Checks that in the same SG28 group instance, CCI with characteristic code E13 AND CAV with code EHZ both occur. Group path SG28 may need parent prefix depending on MIG structure. (medium confidence)
450 fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
451 ctx.any_group_has_co_occurrence("CCI", 2, &["E13"], "CAV", 0, 0, &["EHZ"], &["SG28"])
452 }
453
454 /// [9] Wenn in derselben SG27 LIN die Artikelnummer 9990001000657 vorhanden ist
455 // REVIEW: Checks if any SG27 group has LIN with article number 9990001000657 at elements[2][0] (C212 first component). Same pattern as condition 5. (medium confidence)
456 fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
457 ctx.any_group_has_qualifier("LIN", 2, "9990001000657", &["SG27"])
458 }
459
460 /// [10] Wenn SG28 CAV+MIW/MPW/MBW vorhanden
461 fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
462 ctx.any_group_has_any_qualifier("CAV", 0, &["MIW", "MPW", "MBW"], &["SG28"])
463 }
464
465 /// [11] Wenn in derselben SG27 LIN die Artikelnummer 9990001000665 vorhanden ist
466 // REVIEW: Checks if any SG27 group has LIN with article number 9990001000665 at elements[2][0]. Same pattern as condition 5. (medium confidence)
467 fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
468 ctx.any_group_has_qualifier("LIN", 2, "9990001000665", &["SG27"])
469 }
470
471 /// [12] Wenn in derselben SG27 LIN die Artikelnummer 9990001000673 vorhanden ist
472 // REVIEW: Checks if any SG27 group has LIN with article number 9990001000673 at elements[2][0]. Same pattern as condition 5. (medium confidence)
473 fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
474 ctx.any_group_has_qualifier("LIN", 2, "9990001000673", &["SG27"])
475 }
476
477 /// [13] Wenn am Gerät vorhanden
478 /// EXTERNAL: Requires context from outside the message.
479 fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
480 ctx.external.evaluate("device_attribute_present")
481 }
482
483 /// [15] Wenn in derselben SG27 LIN die Artikelnummer 9990001000772 vorhanden ist
484 // REVIEW: Checks if any SG27 group has LIN with article number 9990001000772 at elements[2][0]. Same pattern as condition 5. (medium confidence)
485 fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
486 ctx.any_group_has_qualifier("LIN", 2, "9990001000772", &["SG27"])
487 }
488
489 /// [16] Wenn in derselben SG27 LIN die Artikelnummer 9990001000780 vorhanden ist
490 // REVIEW: Checks if any SG27 group has LIN with article number 9990001000780 at elements[2][0]. Same pattern as condition 5. (medium confidence)
491 fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
492 ctx.any_group_has_qualifier("LIN", 2, "9990001000780", &["SG27"])
493 }
494
495 /// [17] Wenn das Angebot per REQOTE angefragt wurde
496 // REVIEW: A QUOTES message triggered by a REQOTE contains a reference RFF+AAV in SG1 (Nachrichtennummer der Anfrage, qualifier AAV at elements[0][0]). Presence of RFF+AAV indicates the quote was requested via REQOTE. (medium confidence)
497 fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
498 ctx.has_qualifier("RFF", 0, "AAV")
499 }
500
501 /// [18] Wenn Angebot mehrere Marktlokationen abdeckt, an welchen der identische Anschlussnutzer vorhanden ist und deren messtechnische Einordnung iMS ist (Marktlokation aus SG11 LOC+172 und alle Marktlokat...
502 // REVIEW: RFF+Z18 ('Referenz auf ID weiterer Marktlokationen') is the direct EDIFACT indicator that additional market locations are covered, which is the core structural requirement. However, the additional qualifiers — identical Anschlussnutzer and messtechnische Einordnung iMS — are business attributes about the referenced locations not determinable from message content alone. The RFF+Z18 check is the best message-derivable approximation. (medium confidence)
503 fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
504 // Check if offer references additional market locations via RFF+Z18
505 // RFF+Z18 = 'Referenz auf ID weiterer Marktlokationen' — presence indicates multiple locations covered
506 // Note: the 'identical connection user' and 'iMS classification' sub-conditions require external context
507 ctx.has_qualifier("RFF", 0, "Z18")
508 }
509
510 /// [20] Wenn IMD++Z33 vorhanden
511 fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
512 ctx.has_qualifier("IMD", 1, "Z33")
513 }
514
515 /// [21] Wenn IMD++Z34 vorhanden
516 fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
517 ctx.has_qualifier("IMD", 1, "Z34")
518 }
519
520 /// [22] Wenn CCI+++Z75 vorhanden ist
521 fn evaluate_22(&self, ctx: &EvaluationContext) -> ConditionResult {
522 ctx.has_qualifier("CCI", 2, "Z75")
523 }
524
525 /// [25] Wenn SG28 CCI+++E13 CAV+MME vorhanden
526 fn evaluate_25(&self, ctx: &EvaluationContext) -> ConditionResult {
527 ctx.any_group_has_co_occurrence("CCI", 2, &["E13"], "CAV", 0, 0, &["MME"], &["SG28"])
528 }
529
530 /// [26] Wenn DTM+203 nicht vorhanden
531 fn evaluate_26(&self, ctx: &EvaluationContext) -> ConditionResult {
532 ctx.lacks_qualifier("DTM", 0, "203")
533 }
534
535 /// [27] Wenn DTM+469 nicht vorhanden
536 fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
537 ctx.lacks_qualifier("DTM", 0, "469")
538 }
539
540 /// [28] Wenn RFF+AAV vorhanden
541 fn evaluate_28(&self, ctx: &EvaluationContext) -> ConditionResult {
542 ctx.has_qualifier("RFF", 0, "AAV")
543 }
544
545 /// [29] Wenn DTM+469 in Anfrage (REQOTE) vorhanden war
546 /// EXTERNAL: Requires context from outside the message.
547 fn evaluate_29(&self, ctx: &EvaluationContext) -> ConditionResult {
548 ctx.external.evaluate("reqote_had_dtm_469")
549 }
550
551 /// [30] Wenn SG27 IMD+Z09 vorhanden
552 fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
553 ctx.any_group_has_qualifier("IMD", 0, "Z09", &["SG27"])
554 }
555
556 /// [39] MP-ID nur aus Sparte Strom
557 /// EXTERNAL: Requires context from outside the message.
558 fn evaluate_39(&self, ctx: &EvaluationContext) -> ConditionResult {
559 ctx.external.evaluate("mp_id_is_strom")
560 }
561
562 /// [44] Wenn MSBA nicht Eigentümer des Gerätes/der Geräte ist
563 /// EXTERNAL: Requires context from outside the message.
564 fn evaluate_44(&self, ctx: &EvaluationContext) -> ConditionResult {
565 ctx.external.evaluate("msba_is_not_device_owner")
566 }
567
568 /// [49] Wenn SG27 LIN++Z64 (Erforderliches Produkt Schaltzeitdefinitionen) vorhanden
569 fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
570 ctx.any_group_has_qualifier("LIN", 1, "Z64", &["SG27"])
571 }
572
573 /// [50] Wenn SG27 LIN++Z65 (Erforderliches Produkt Leistungskurvendefinitionen) vorhanden
574 fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
575 ctx.any_group_has_qualifier("LIN", 1, "Z65", &["SG27"])
576 }
577
578 /// [51] Wenn SG27 LIN++Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) vorhanden
579 fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
580 ctx.any_group_has_qualifier("LIN", 1, "Z66", &["SG27"])
581 }
582
583 /// [52] Wenn SG27 LIN++Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) vorhanden
584 fn evaluate_52(&self, ctx: &EvaluationContext) -> ConditionResult {
585 ctx.has_qualifier("LIN", 1, "Z67")
586 }
587
588 /// [53] Wenn SG27 LIN++Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ2 aus SMGW) vorhanden
589 fn evaluate_53(&self, ctx: &EvaluationContext) -> ConditionResult {
590 ctx.has_qualifier("LIN", 1, "Z68")
591 }
592
593 /// [54] Wenn in der Anfrage zum Angebot einer Konfiguration vorhanden
594 /// EXTERNAL: Requires context from outside the message.
595 fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
596 ctx.external.evaluate("configuration_in_request")
597 }
598
599 /// [55] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Marktlokation angegeben ist.
600 fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
601 // Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Marktlokation angegeben ist.
602 // LOC elements[0][0] == "172" (qualifier), elements[1][0] == DE3225 (Identifikator)
603 let locs = ctx.find_segments_with_qualifier("LOC", 0, "172");
604 match locs
605 .first()
606 .and_then(|s| s.elements.get(1))
607 .and_then(|e| e.first())
608 {
609 Some(val) => validate_malo_id(val),
610 None => ConditionResult::False, // segment absent → condition not applicable
611 }
612 }
613
614 /// [65] Wenn DTM+203 (Ausführungsdatum) im DE2380 < 202312312300?+00
615 // REVIEW: DTM+203 DE2380 is at elements[0][1]. Lexicographic comparison works for YYYYMMDDHHMI+ZZ format since all values use the same timezone offset and format. The EDIFACT escape `?+` in the threshold represents a literal `+`, giving the threshold string "202312312300+00". (medium confidence)
616 fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
617 let segs = ctx.find_segments_with_qualifier("DTM", 0, "203");
618 match segs.first() {
619 Some(dtm) => {
620 match dtm
621 .elements
622 .first()
623 .and_then(|e| e.get(1))
624 .map(|s| s.as_str())
625 {
626 Some(value) if !value.is_empty() => {
627 ConditionResult::from(value < "202312312300+00")
628 }
629 _ => ConditionResult::Unknown,
630 }
631 }
632 None => ConditionResult::False, // segment absent → condition not applicable
633 }
634 }
635
636 /// [66] Wenn DTM+469 (Beginn zum (nächstmöglichen Termin)) im DE2380 < 202312312300?+00
637 // REVIEW: DTM+469 DE2380 is at elements[0][1]. Same lexicographic comparison as condition 65, applied to the Beginn zum nächstmöglichen Termin qualifier 469. (medium confidence)
638 fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
639 let segs = ctx.find_segments_with_qualifier("DTM", 0, "469");
640 match segs.first() {
641 Some(dtm) => {
642 match dtm
643 .elements
644 .first()
645 .and_then(|e| e.get(1))
646 .map(|s| s.as_str())
647 {
648 Some(value) if !value.is_empty() => {
649 ConditionResult::from(value < "202312312300+00")
650 }
651 _ => ConditionResult::Unknown,
652 }
653 }
654 None => ConditionResult::False, // segment absent → condition not applicable
655 }
656 }
657
658 /// [67] Wenn DTM+203 (Ausführungsdatum) im DE2380 ≥ 202312312300?+00
659 // REVIEW: DTM+203 DE2380 is at elements[0][1]. Logical complement of condition 65 — True when Ausführungsdatum >= 2023-12-31 23:00 UTC. Lexicographic comparison is valid for this date format. (medium confidence)
660 fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
661 let segs = ctx.find_segments_with_qualifier("DTM", 0, "203");
662 match segs.first() {
663 Some(dtm) => {
664 match dtm
665 .elements
666 .first()
667 .and_then(|e| e.get(1))
668 .map(|s| s.as_str())
669 {
670 Some(value) if !value.is_empty() => {
671 ConditionResult::from(value >= "202312312300+00")
672 }
673 _ => ConditionResult::Unknown,
674 }
675 }
676 None => ConditionResult::False, // segment absent → condition not applicable
677 }
678 }
679
680 /// [68] Wenn DTM+469 (Beginn zum (nächstmöglichen Termin)) im DE2380 ≥ 202312312300?+00
681 // REVIEW: DTM+469 DE2380 (elements[0][1]) compared lexicographically — works for ISO datetime strings with fixed-width format. EDIFACT ?+ escape resolves to literal + in parsed value. (medium confidence)
682 fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
683 let segs = ctx.find_segments_with_qualifier("DTM", 0, "469");
684 if segs.is_empty() {
685 return ConditionResult::Unknown;
686 }
687 for seg in segs {
688 let val = seg
689 .elements
690 .get(0)
691 .and_then(|e| e.get(1))
692 .map(|s| s.as_str())
693 .unwrap_or("");
694 if val >= "202312312300+00" {
695 return ConditionResult::True;
696 }
697 }
698 ConditionResult::False
699 }
700
701 /// [71] 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...
702 /// EXTERNAL: Requires context from outside the message.
703 fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
704 ctx.external.evaluate("pia5_product_is_threshold_trigger")
705 }
706
707 /// [72] wenn im DE3155 in demselben COM der Code EM vorhanden ist
708 fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
709 let segs = ctx.find_segments("COM");
710 if segs.is_empty() {
711 return ConditionResult::Unknown;
712 }
713 ConditionResult::from(segs.iter().any(|seg| {
714 seg.elements
715 .first()
716 .and_then(|e| e.get(1))
717 .is_some_and(|v| v == "EM")
718 }))
719 }
720
721 /// [73] wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
722 fn evaluate_73(&self, ctx: &EvaluationContext) -> ConditionResult {
723 let segs = ctx.find_segments("COM");
724 if segs.is_empty() {
725 return ConditionResult::Unknown;
726 }
727 ConditionResult::from(segs.iter().any(|seg| {
728 seg.elements
729 .first()
730 .and_then(|e| e.get(1))
731 .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
732 }))
733 }
734
735 /// [74] 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.
736 /// EXTERNAL: Requires context from outside the message.
737 fn evaluate_74(&self, ctx: &EvaluationContext) -> ConditionResult {
738 ctx.external.evaluate("product_in_typ2_backend_codelist")
739 }
740
741 /// [75] 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.
742 /// EXTERNAL: Requires context from outside the message.
743 fn evaluate_75(&self, ctx: &EvaluationContext) -> ConditionResult {
744 ctx.external.evaluate("product_in_typ2_smgw_codelist")
745 }
746
747 /// [77] Wenn in der Anfrage nach Werten ein Messprodukt enthalten war, das in der Codeliste der Konfigurationen im Kapitel 4.6.1 "Werte nach Typ 2 aus Backend" enthalten ist.
748 /// EXTERNAL: Requires context from outside the message.
749 fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
750 ctx.external
751 .evaluate("request_contained_typ2_backend_product")
752 }
753
754 /// [78] Wenn in der Anfrage nach Werten ein Messprodukt enthalten war, das in der Codeliste der Konfigurationen im Kapitel 4.6.2 "Werte nach Typ 2 aus SMWG" enthalten ist.
755 /// EXTERNAL: Requires context from outside the message.
756 fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
757 ctx.external.evaluate("request_contained_typ2_smgw_product")
758 }
759
760 /// [79] Wenn IMD DE7081 (Produkt-/Leistungsbeschreibung) mit Wert Z07 (Kauf) vorhanden.
761 fn evaluate_79(&self, ctx: &EvaluationContext) -> ConditionResult {
762 let segs = ctx.find_segments("IMD");
763 if segs.is_empty() {
764 return ConditionResult::Unknown;
765 }
766 ConditionResult::from(segs.iter().any(|seg| {
767 seg.elements
768 .get(1)
769 .and_then(|e| e.first())
770 .is_some_and(|v| v == "Z07")
771 }))
772 }
773
774 /// [80] Wenn IMD DE7081 (Produkt-/Leistungsbeschreibung) mit Wert Z08 (Nutzungsüberlassung) vorhanden.
775 fn evaluate_80(&self, ctx: &EvaluationContext) -> ConditionResult {
776 let segs = ctx.find_segments("IMD");
777 if segs.is_empty() {
778 return ConditionResult::Unknown;
779 }
780 ConditionResult::from(segs.iter().any(|seg| {
781 seg.elements
782 .get(1)
783 .and_then(|e| e.first())
784 .is_some_and(|v| v == "Z08")
785 }))
786 }
787
788 /// [81] Wenn IMD DE7081 (Produkt-/Leistungsbeschreibung) mit Wert Z33 (Angebot auf Basis Preisblatt) vorhanden.
789 fn evaluate_81(&self, ctx: &EvaluationContext) -> ConditionResult {
790 let segs = ctx.find_segments("IMD");
791 if segs.is_empty() {
792 return ConditionResult::Unknown;
793 }
794 ConditionResult::from(segs.iter().any(|seg| {
795 seg.elements
796 .get(1)
797 .and_then(|e| e.first())
798 .is_some_and(|v| v == "Z33")
799 }))
800 }
801
802 /// [82] Wenn IMD DE7081 (Produkt-/Leistungsbeschreibung) mit Wert Z34 (Individuelles Angebot) vorhanden.
803 fn evaluate_82(&self, ctx: &EvaluationContext) -> ConditionResult {
804 let segs = ctx.find_segments("IMD");
805 if segs.is_empty() {
806 return ConditionResult::Unknown;
807 }
808 ConditionResult::from(segs.iter().any(|seg| {
809 seg.elements
810 .get(1)
811 .and_then(|e| e.first())
812 .is_some_and(|v| v == "Z34")
813 }))
814 }
815
816 /// [83] Wenn in derselben SG27 LIN ein PIA+Z02 (Artikel-ID) mit einer Artikel-ID im DE7140 bei der die letzten beiden Stellen mit dem Wert "01" (Kosten für das Einrichten des Messprodukts) vorhanden ist.
817 // REVIEW: PIA+Z02 where elements[0][0]=="Z02" (Artikel-ID qualifier) and elements[1][0] ends with "01" (Kosten für Einrichten). Group-scoped to SG27 but falls back to message-wide search; medium confidence due to lack of SG27 navigator API for flat groups. (medium confidence)
818 fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
819 let segs = ctx.find_segments("PIA");
820 if segs.is_empty() {
821 return ConditionResult::Unknown;
822 }
823 ConditionResult::from(segs.iter().any(|seg| {
824 let is_z02 = seg
825 .elements
826 .first()
827 .and_then(|e| e.first())
828 .is_some_and(|v| v == "Z02");
829 let ends_with_01 = seg
830 .elements
831 .get(1)
832 .and_then(|e| e.first())
833 .is_some_and(|v| v.ends_with("01"));
834 is_z02 && ends_with_01
835 }))
836 }
837
838 /// [84] Wenn in derselben SG27 LIN ein PIA+Z02 (Artikel-ID) mit einer Artikel-ID im DE7140 bei der die letzten beiden Stellen mit dem Wert "02" (Kosten für den Betrieb des Messprodukts) vorhanden ist.
839 // REVIEW: Checks for PIA+Z02 where DE7140 (elements[1][0]) ends with '02' (Betriebskosten). Falls back to message-wide search since group-scoped navigator API has no direct find_segments_in_group for top-level SG27 — semantically equivalent for this condition. (medium confidence)
840 fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
841 let pias = ctx.find_segments_with_qualifier("PIA", 0, "Z02");
842 ConditionResult::from(pias.iter().any(|s| {
843 s.elements
844 .get(1)
845 .and_then(|e| e.first())
846 .map(|v| v.ends_with("02"))
847 .unwrap_or(false)
848 }))
849 }
850
851 /// [85] Wenn in derselben SG27 LIN ein PIA+Z02 (Artikel-ID) mit einer Artikel-ID im DE7140 bei der die letzten beiden Stellen mit dem Wert "03" (Kosten für die anfallenden Transaktionen des Messprodukts) ...
852 // REVIEW: Checks for PIA+Z02 where DE7140 (elements[1][0]) ends with '03' (Transaktionskosten). Same pattern as condition 84. (medium confidence)
853 fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
854 let pias = ctx.find_segments_with_qualifier("PIA", 0, "Z02");
855 ConditionResult::from(pias.iter().any(|s| {
856 s.elements
857 .get(1)
858 .and_then(|e| e.first())
859 .map(|v| v.ends_with("03"))
860 .unwrap_or(false)
861 }))
862 }
863
864 /// [86] Wenn in derselben SG31 PRI (Preisangabe zur Position) das DE5387 mit dem Wert Z01 (Einrichtungspreis) vorhanden ist.
865 fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
866 ctx.any_group_has_qualifier("PRI", 0, "Z01", &["SG31"])
867 }
868
869 /// [87] Wenn in derselben SG31 PRI (Preisangabe zur Position) das DE5387 mit dem Wert Z02 (Transaktionspreis) vorhanden ist.
870 fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
871 ctx.any_group_has_qualifier("PRI", 0, "Z02", &["SG31"])
872 }
873
874 /// [88] Wenn in derselben SG31 PRI (Preisangabe zur Position) das DE5387 mit dem Wert Z03 (Betriebspreis) vorhanden ist.
875 fn evaluate_88(&self, ctx: &EvaluationContext) -> ConditionResult {
876 ctx.any_group_has_qualifier("PRI", 0, "Z03", &["SG31"])
877 }
878
879 /// [492] wenn MP-ID in NAD+MR aus Sparte Strom
880 /// EXTERNAL: Requires context from outside the message.
881 fn evaluate_492(&self, ctx: &EvaluationContext) -> ConditionResult {
882 ctx.external.evaluate("recipient_is_electricity_sector")
883 }
884
885 /// [493] wenn MP-ID in NAD+MR aus Sparte Gas
886 /// EXTERNAL: Requires context from outside the message.
887 fn evaluate_493(&self, ctx: &EvaluationContext) -> ConditionResult {
888 ctx.external.evaluate("recipient_is_gas_sector")
889 }
890
891 /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt.
892 /// EXTERNAL: Requires context from outside the message.
893 // REVIEW: Validating that a date is the document creation date or earlier requires knowing the actual current/creation timestamp at validation time. This is a temporal constraint that depends on runtime context outside the message. (medium confidence)
894 fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
895 ctx.external.evaluate("document_date_is_creation_or_past")
896 }
897
898 /// [500] Hinweis: Angabe eines technischen Ansprechpartners für die Geräteübernahme
899 fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
900 // Hinweis: Angabe eines technischen Ansprechpartners für die Geräteübernahme — informational note, always applies
901 ConditionResult::True
902 }
903
904 /// [501] Hinweis: Verwendung der ID der Marktlokation
905 fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
906 // Hinweis: Verwendung der ID der Marktlokation — informational note, always applies
907 ConditionResult::True
908 }
909
910 /// [502] Hinweis: Verwendung der ID der Messlokation
911 fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
912 // Hinweis: Verwendung der ID der Messlokation — informational note, always applies
913 ConditionResult::True
914 }
915
916 /// [503] Hinweis: Es ist die MP-ID des Eigentümers (MSB) zur bilateralen Klärung anzugeben, wenn z. B. der MSBA das Gerät / die Geräte selbst gepachtet hat.
917 fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
918 // Hinweis: Es ist die MP-ID des Eigentümers (MSB) zur bilateralen Klärung anzugeben, wenn z. B. der MSBA das Gerät / die Geräte selbst gepachtet hat.
919 ConditionResult::True
920 }
921
922 /// [504] Hinweis: Wert aus BGM+311 DE1004 der REQOTE mit der die Angebotsanfrage erfolgt ist.
923 fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
924 // Hinweis: Wert aus BGM+311 DE1004 der REQOTE mit der die Angebotsanfrage erfolgt ist.
925 ConditionResult::True
926 }
927
928 /// [505] Hinweis: Wert aus BGM+Z29 DE1004 der REQOTE, mit der die Anfrage Rechnungsabwicklung erfolgt ist.
929 fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
930 // Hinweis: Wert aus BGM+Z29 DE1004 der REQOTE, mit der die Anfrage Rechnungsabwicklung erfolgt ist.
931 ConditionResult::True
932 }
933
934 /// [506] Hinweis: Wenn zu einer Position (z. B. Messwandlersatz) mehrere Gerätenummern existieren, sind die Gerätenummern in derselben Position (LIN-Segment) mittels Wiederholung der SG32 RFF+Z09 anzugeben
935 fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
936 // Hinweis: Wenn zu einer Position mehrere Gerätenummern existieren, sind die Gerätenummern in derselben Position mittels Wiederholung der SG32 RFF+Z09 anzugeben.
937 ConditionResult::True
938 }
939
940 /// [507] Hinweis: Verwendung der ID der Tranche
941 fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
942 // Hinweis: Verwendung der ID der Tranche
943 ConditionResult::True
944 }
945
946 /// [511] Hinweis: Wert aus BGM+Z74 (Bestellung eines Angebots einer Konfiguration) DE1004 der REQOTE mit der die Anfrage einer Konfiguration erfolgt ist.
947 fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
948 // Hinweis: Wert aus BGM+Z74 DE1004 der REQOTE — informational note, always applies
949 ConditionResult::True
950 }
951
952 /// [512] Hinweis: Verwendung der ID der Netzlokation
953 fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
954 // Hinweis: Verwendung der ID der Netzlokation — informational note, always applies
955 ConditionResult::True
956 }
957
958 /// [513] Hinweis: Verwendung der ID der Steuerbaren Ressource
959 fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
960 // Hinweis: Verwendung der ID der Steuerbaren Ressource — informational note, always applies
961 ConditionResult::True
962 }
963
964 /// [514] Hinweis: Angabe gemäß Preisblatt des MSB. Bis zu einmal für die Parametrierung, bis zu einmal für den Betrieb und bis zu einmal für die Transaktion, sofern im Preisblatt des MSB vorhanden.
965 fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
966 // Hinweis: Angabe gemäß Preisblatt des MSB — informational note, always applies
967 ConditionResult::True
968 }
969
970 /// [516] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
971 fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
972 // Hinweis: Es darf nur eine Information im DE3148 übermittelt werden — informational note, always applies
973 ConditionResult::True
974 }
975
976 /// [903] Format: Möglicher Wert: 1
977 // REVIEW: Format condition requiring the value to equal exactly 1. Applies to a numeric field — most likely QTY[0][1] in QUOTES context. validate_numeric with == 1.0 covers this. Medium confidence because the exact segment binding isn't specified in the condition text alone. (medium confidence)
978 fn evaluate_903(&self, ctx: &EvaluationContext) -> ConditionResult {
979 ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, "==", 1.0))
980 }
981
982 /// [906] Format: max. 3 Nachkommastellen
983 // REVIEW: Format condition for maximum 3 decimal places. Uses validate_max_decimal_places helper. Targeting QTY[0][1] as the most likely numeric value field in QUOTES. Medium confidence because the specific data element binding is not stated in the condition text. (medium confidence)
984 fn evaluate_906(&self, ctx: &EvaluationContext) -> ConditionResult {
985 ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 3))
986 }
987
988 /// [908] Format: Mögliche Werte: 1 bis n
989 // REVIEW: Format condition for values in range 1 to n (any positive integer). validate_numeric with >= 1.0 captures the lower bound; the upper bound n is open-ended. Targeting QTY as the most common numeric field. Medium confidence due to unspecified exact segment. (medium confidence)
990 fn evaluate_908(&self, ctx: &EvaluationContext) -> ConditionResult {
991 ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">=", 1.0))
992 }
993
994 /// [911] Format: Mögliche Werte: 1 bis n, je Nachricht oder Segmentgruppe bei 1 beginnend und fortlaufend aufsteigend
995 fn evaluate_911(&self, _ctx: &EvaluationContext) -> ConditionResult {
996 // Hinweis: Mögliche Werte: 1 bis n, je Nachricht oder Segmentgruppe bei 1 beginnend
997 // und fortlaufend aufsteigend — informational annotation about sequential numbering,
998 // always applies unconditionally
999 ConditionResult::True
1000 }
1001
1002 /// [912] Format: max. 6 Nachkommastellen
1003 // REVIEW: Format condition for maximum 6 decimal places. Uses validate_max_decimal_places helper with limit 6. Targeting QTY[0][1] as the primary numeric value field in QUOTES. Medium confidence because the exact data element binding is not given in the condition text. (medium confidence)
1004 fn evaluate_912(&self, ctx: &EvaluationContext) -> ConditionResult {
1005 ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 6))
1006 }
1007
1008 /// [914] Format: Möglicher Wert: > 0
1009 // REVIEW: Format condition requiring value > 0. In QUOTES context this most likely applies to QTY element[0][1] (the quantity value). Medium confidence because the exact segment is not specified in the condition text — it depends on which data element this condition is attached to in the AHB. (medium confidence)
1010 fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
1011 ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">", 0.0))
1012 }
1013
1014 /// [931] Format: ZZZ = +00
1015 // REVIEW: Format condition validating that the timezone portion (ZZZ) of a DTM value equals +00 (UTC). Uses validate_timezone_utc helper. Medium confidence because the specific DTM qualifier this applies to is not stated in the condition text — the AHB attaches this to a specific DTM element. (medium confidence)
1016 fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
1017 ctx.format_check("DTM", 0, 1, validate_timezone_utc)
1018 }
1019
1020 /// [932] Format: HHMM = 2200
1021 // REVIEW: Format condition requiring the HHMM portion of a DTM value to equal 2200. Uses validate_hhmm_equals helper. Medium confidence because the specific DTM qualifier (e.g., 163, 164, 137) this applies to is not stated in the condition text alone — it depends on which DTM element in the QUOTES AHB carries this constraint. (medium confidence)
1022 fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
1023 ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
1024 }
1025
1026 /// [933] Format: HHMM = 2300
1027 // REVIEW: Format condition requiring the HHMM portion of a DTM value to equal 2300. Uses validate_hhmm_equals helper. Medium confidence for the same reason as 932 — specific DTM qualifier not identified from condition text alone. (medium confidence)
1028 fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
1029 ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
1030 }
1031
1032 /// [934] Format: HHMM = 0400
1033 // REVIEW: Format condition requiring the HHMM portion of a DTM value to equal 0400. Uses validate_hhmm_equals helper. Medium confidence for the same reason as 932/933 — specific DTM qualifier not identified from condition text alone. In QUOTES context, 0400 likely represents a gas day start time. (medium confidence)
1034 fn evaluate_934(&self, ctx: &EvaluationContext) -> ConditionResult {
1035 ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0400"))
1036 }
1037
1038 /// [935] Format: HHMM = 0500
1039 // REVIEW: Format condition checking HHMM = 0500 on a DTM segment. Without a specific qualifier reference provided for this QUOTES condition, falls back to the first DTM segment. validate_hhmm_equals extracts the HHMM portion of the datetime value and compares it to '0500'. (medium confidence)
1040 fn evaluate_935(&self, ctx: &EvaluationContext) -> ConditionResult {
1041 ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0500"))
1042 }
1043
1044 /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
1045 // REVIEW: Checks COM segments for email-format values (must contain '@' and '.'). COM is the standard EDIFACT segment for communication channels where email addresses appear (qualifier EM). Scans all COM values since context doesn't specify which occurrence to validate. (medium confidence)
1046 fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
1047 for seg in ctx.find_segments("COM") {
1048 if let Some(elem) = seg.elements.first() {
1049 if let Some(value) = elem.first() {
1050 if value.contains('@') && value.contains('.') {
1051 return ConditionResult::True;
1052 }
1053 }
1054 }
1055 }
1056 ConditionResult::False
1057 }
1058
1059 /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
1060 // REVIEW: Checks COM segments for international phone number format (starts with '+' followed only by digits). COM is the standard segment for telephone contacts. Scans all COM values since context doesn't specify which occurrence to validate. (medium confidence)
1061 fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
1062 for seg in ctx.find_segments("COM") {
1063 if let Some(elem) = seg.elements.first() {
1064 if let Some(value) = elem.first() {
1065 if value.starts_with('+') && value.chars().skip(1).all(|c| c.is_ascii_digit()) {
1066 return ConditionResult::True;
1067 }
1068 }
1069 }
1070 }
1071 ConditionResult::False
1072 }
1073
1074 /// [942] Format: n1-n2-n1-n3
1075 fn evaluate_942(&self, ctx: &EvaluationContext) -> ConditionResult {
1076 ctx.format_check("PIA", 1, 0, |val| {
1077 validate_artikel_pattern(val, &[1, 2, 1, 3])
1078 })
1079 }
1080
1081 /// [950] Format: Marktlokations-ID
1082 fn evaluate_950(&self, ctx: &EvaluationContext) -> ConditionResult {
1083 ctx.format_check_qualified("LOC", 0, "Z16", 1, 0, validate_malo_id)
1084 }
1085
1086 /// [951] Format: Zählpunktbezeichnung
1087 fn evaluate_951(&self, ctx: &EvaluationContext) -> ConditionResult {
1088 ctx.format_check_qualified("LOC", 0, "Z17", 1, 0, validate_zahlpunkt)
1089 }
1090
1091 /// [960] Format: Netzlokations-ID
1092 // HAND-EDITED: a Netzlokations-ID is "E" + ten alphanumerics (`E1688117482`),
1093 // not MaLo-shaped; `validate_malo_id` rejected every real one (#173).
1094 fn evaluate_960(&self, ctx: &EvaluationContext) -> ConditionResult {
1095 ctx.format_check_qualified("LOC", 0, "Z18", 1, 0, validate_nelo_id)
1096 }
1097
1098 /// [962] Format: max. 6 Vorkommastellen
1099 // REVIEW: Uses validate_max_integer_digits(value, 6) on the PRI price amount value. Standard EDIFACT PRI C509 places DE5118 (price amount) at elements[0][1]. PRI segment structure is not explicitly listed in the provided MIG reference so medium confidence on the element index. (medium confidence)
1100 fn evaluate_962(&self, ctx: &EvaluationContext) -> ConditionResult {
1101 ctx.format_check("PRI", 0, 1, |val| validate_max_integer_digits(val, 6))
1102 }
1103
1104 /// [2042] Innerhalb dieser LIN-Position muss das PIA+Z02 (Artikel-ID) mindestens ein Mal angegeben werden und kann bis zu drei Mal angegeben werden.
1105 // REVIEW: Counts PIA segments where elements[0][0] == 'Z02' (DE4347 Artikel-ID qualifier per segment structure reference) and verifies the count is between 1 and 3 inclusive. Medium confidence because the condition is scoped to 'within this LIN position' (SG27) but we count message-wide; in REQOTE the PIA+Z02 segments appear within SG27 position groups. (medium confidence)
1106 fn evaluate_2042(&self, ctx: &EvaluationContext) -> ConditionResult {
1107 let count = ctx
1108 .find_segments("PIA")
1109 .iter()
1110 .filter(|s| {
1111 s.elements
1112 .first()
1113 .and_then(|e| e.first())
1114 .is_some_and(|v| v == "Z02")
1115 })
1116 .count();
1117 ConditionResult::from(count >= 1 && count <= 3)
1118 }
1119
1120 /// [2060] Pro Nachricht ist die SG27 LIN+Z64 (Erforderliches Produkt Schaltzeitdefinitionen) maximal einmal anzugeben
1121 fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
1122 let count = ctx
1123 .find_segments("LIN")
1124 .iter()
1125 .filter(|s| {
1126 s.elements
1127 .get(1)
1128 .and_then(|e| e.first())
1129 .is_some_and(|v| v == "Z64")
1130 })
1131 .count();
1132 ConditionResult::from(count <= 1)
1133 }
1134
1135 /// [2061] Pro Nachricht ist die SG27 LIN++Z65 (Erforderliches Produkt Leistungskurvendefinitionen) maximal einmal anzugeben
1136 fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
1137 let count = ctx
1138 .find_segments("LIN")
1139 .iter()
1140 .filter(|s| {
1141 s.elements
1142 .get(1)
1143 .and_then(|e| e.first())
1144 .is_some_and(|v| v == "Z65")
1145 })
1146 .count();
1147 ConditionResult::from(count <= 1)
1148 }
1149
1150 /// [2062] Pro Nachricht ist die SG27 LIN++Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) maximal einmal anzugeben
1151 fn evaluate_2062(&self, ctx: &EvaluationContext) -> ConditionResult {
1152 let count = ctx.find_segments_with_qualifier("LIN", 1, "Z66").len();
1153 ConditionResult::from(count <= 1)
1154 }
1155
1156 /// [2063] Pro Nachricht ist die SG27 LIN++Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) maximal einmal anzugeben
1157 fn evaluate_2063(&self, ctx: &EvaluationContext) -> ConditionResult {
1158 let count = ctx.find_segments_with_qualifier("LIN", 1, "Z67").len();
1159 ConditionResult::from(count <= 1)
1160 }
1161
1162 /// [2064] Pro Nachricht ist die SG27 LIN++Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) maximal einmal anzugeben
1163 fn evaluate_2064(&self, ctx: &EvaluationContext) -> ConditionResult {
1164 let count = ctx.find_segments_with_qualifier("LIN", 1, "Z68").len();
1165 ConditionResult::from(count <= 1)
1166 }
1167
1168 /// [2068] Pro SG27 LIN ist die SG31 PRI genau einmal anzugeben.
1169 // REVIEW: Checks that every SG27 instance has exactly one SG31 child group (PRI). Uses group navigator to count SG31 children per SG27 instance. Returns True only when all SG27s have exactly one SG31. (medium confidence)
1170 fn evaluate_2068(&self, ctx: &EvaluationContext) -> ConditionResult {
1171 let nav = match ctx.navigator() {
1172 Some(n) => n,
1173 None => return ConditionResult::Unknown,
1174 };
1175 let sg27_count = nav.group_instance_count(&["SG27"]);
1176 if sg27_count == 0 {
1177 return ConditionResult::Unknown;
1178 }
1179 for i in 0..sg27_count {
1180 let sg31_count = nav.child_group_instance_count(&["SG27"], i, "SG31");
1181 if sg31_count != 1 {
1182 return ConditionResult::False;
1183 }
1184 }
1185 ConditionResult::True
1186 }
1187
1188 /// [2069] Pro SG27 LIN ist die SG31 PRI genau einmal anzugeben. Dabei muss in der SG31 PRI im DE5284 eine Mengenangabe mit dem Code H87 (Stück) im DE6411 angegeben sein.
1189 // REVIEW: Checks that count_in_group for PRI in SG31 equals exactly 1 and that the PRI contains H87 in DE6411 (C509 index 4 = elements[0][1] is price amount; index 4 is unit of measure per standard EDIFACT C509 layout: DE5125, DE5118, DE5375, DE5419, DE6411). PRI structure not in the provided MIG reference so medium confidence on the DE6411 index. (medium confidence)
1190 fn evaluate_2069(&self, ctx: &EvaluationContext) -> ConditionResult {
1191 // Pro SG27 LIN: SG31 PRI must appear exactly once with H87 (Stück) in DE6411
1192 let pri_count = ctx.count_in_group("PRI", &["SG27", "SG31"]);
1193 if pri_count != 1 {
1194 return ConditionResult::False;
1195 }
1196 // DE6411 is the 5th component of C509 = elements[0][4]
1197 let segs = ctx.find_segments("PRI");
1198 match segs.first() {
1199 Some(pri) => {
1200 let unit = pri
1201 .elements
1202 .first()
1203 .and_then(|e| e.get(4))
1204 .map(|s| s.as_str());
1205 ConditionResult::from(unit == Some("H87"))
1206 }
1207 None => ConditionResult::False, // segment absent → condition not applicable
1208 }
1209 }
1210
1211 /// [2070] Pro SG27 LIN ist die SG31 PRI zweimal anzugeben. Dabei muss genau eine SG31 PRI mit Preis- und Mengenangabe und dem Code H87 (Stück) im DE6411 und genau eine SG31 PRI mit Preis- und Mengenangabe u...
1212 // REVIEW: Checks that SG31 PRI appears exactly twice and that among those two entries one has H87 and one has DAY in DE6411 (C509 index 4 = elements[0][4]). Same caveat as 2069 — PRI structure not in the provided MIG reference so medium confidence on the component index for DE6411. (medium confidence)
1213 fn evaluate_2070(&self, ctx: &EvaluationContext) -> ConditionResult {
1214 // Pro SG27 LIN: SG31 PRI must appear exactly twice — one with H87 (Stück), one with DAY (Tag) in DE6411
1215 let pri_count = ctx.count_in_group("PRI", &["SG27", "SG31"]);
1216 if pri_count != 2 {
1217 return ConditionResult::False;
1218 }
1219 // DE6411 is elements[0][4] in standard EDIFACT C509
1220 let segs = ctx.find_segments("PRI");
1221 let has_h87 = segs.iter().any(|pri| {
1222 pri.elements
1223 .first()
1224 .and_then(|e| e.get(4))
1225 .is_some_and(|v| v == "H87")
1226 });
1227 let has_day = segs.iter().any(|pri| {
1228 pri.elements
1229 .first()
1230 .and_then(|e| e.get(4))
1231 .is_some_and(|v| v == "DAY")
1232 });
1233 ConditionResult::from(has_h87 && has_day)
1234 }
1235
1236 /// [2071] Diese SG31 PRI (Preisangabe zur Position) ist bis zu dreimal anzugeben. Es ist so oft anzugeben, wie innerhalb derselben SG27 LIN (Erforderliches Messprodukt für Werte nach Typ2 aus Backend) das P...
1237 fn evaluate_2071(&self, _ctx: &EvaluationContext) -> ConditionResult {
1238 // Hinweis: Cardinality rule — SG31 PRI (Preisangabe) is given up to three times,
1239 // as often as PIA+Z02 (Artikel-ID) appears in the same SG27 LIN (Erforderliches Messprodukt
1240 // für Werte nach Typ2 aus Backend). Informational cardinality annotation, always applies.
1241 ConditionResult::True
1242 }
1243
1244 /// [2072] Diese SG31 PRI (Preisangabe zur Position) ist bis zu dreimal anzugeben. Es ist so oft anzugeben, wie innerhalb derselben SG27 LIN (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ...
1245 fn evaluate_2072(&self, _ctx: &EvaluationContext) -> ConditionResult {
1246 // Hinweis: Cardinality rule — SG31 PRI (Preisangabe) is given up to three times,
1247 // as often as PIA+Z02 (Artikel-ID) appears in the same SG27 LIN (Erforderliches Produkt
1248 // Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW). Informational cardinality annotation, always applies.
1249 ConditionResult::True
1250 }
1251
1252 /// [2073] Das PIA (OBIS-Kennzahl für Werte nach Typ 2 Backend) ist mindestens einmal anzugeben. Es kann in Abhängigkeit zu den anderen PIA Segmenten innerhalb derselben SG27 LIN (Erforderliches Messprodukt...
1253 fn evaluate_2073(&self, _ctx: &EvaluationContext) -> ConditionResult {
1254 // Hinweis: Cardinality rule — PIA (OBIS-Kennzahl für Werte nach Typ 2 Backend) must appear
1255 // at least once. It can appear up to 23 times if the other PIA segments in the same SG27 LIN
1256 // appear at most once each. The maximum total number of PIA segments per SG27 LIN is 25.
1257 // Informational cardinality annotation, always applies.
1258 ConditionResult::True
1259 }
1260}