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#[allow(unused_imports)]
8use crate::eval::format_validators::*;
9use crate::eval::{ConditionEvaluator, ConditionResult, EvaluationContext};
10
11/// Generated condition evaluator for QUOTES FV2504.
12pub struct QuotesConditionEvaluatorFV2504 {
13 // External condition IDs that require runtime context.
14 external_conditions: std::collections::HashSet<u32>,
15}
16
17impl Default for QuotesConditionEvaluatorFV2504 {
18 fn default() -> Self {
19 let mut external_conditions = std::collections::HashSet::new();
20 external_conditions.insert(1);
21 external_conditions.insert(4);
22 external_conditions.insert(13);
23 external_conditions.insert(29);
24 external_conditions.insert(31);
25 external_conditions.insert(39);
26 external_conditions.insert(44);
27 external_conditions.insert(54);
28 external_conditions.insert(56);
29 external_conditions.insert(57);
30 external_conditions.insert(58);
31 external_conditions.insert(59);
32 external_conditions.insert(60);
33 external_conditions.insert(61);
34 external_conditions.insert(62);
35 external_conditions.insert(63);
36 external_conditions.insert(69);
37 external_conditions.insert(70);
38 external_conditions.insert(71);
39 external_conditions.insert(74);
40 external_conditions.insert(75);
41 external_conditions.insert(77);
42 external_conditions.insert(78);
43 external_conditions.insert(492);
44 external_conditions.insert(493);
45 external_conditions.insert(494);
46 external_conditions.insert(2066);
47 Self {
48 external_conditions,
49 }
50 }
51}
52
53impl ConditionEvaluator for QuotesConditionEvaluatorFV2504 {
54 fn message_type(&self) -> &str {
55 "QUOTES"
56 }
57
58 fn format_version(&self) -> &str {
59 "FV2504"
60 }
61
62 fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
63 match condition {
64 1 => self.evaluate_1(ctx),
65 2 => self.evaluate_2(ctx),
66 3 => self.evaluate_3(ctx),
67 4 => self.evaluate_4(ctx),
68 5 => self.evaluate_5(ctx),
69 8 => self.evaluate_8(ctx),
70 9 => self.evaluate_9(ctx),
71 10 => self.evaluate_10(ctx),
72 11 => self.evaluate_11(ctx),
73 12 => self.evaluate_12(ctx),
74 13 => self.evaluate_13(ctx),
75 15 => self.evaluate_15(ctx),
76 16 => self.evaluate_16(ctx),
77 17 => self.evaluate_17(ctx),
78 18 => self.evaluate_18(ctx),
79 20 => self.evaluate_20(ctx),
80 21 => self.evaluate_21(ctx),
81 22 => self.evaluate_22(ctx),
82 25 => self.evaluate_25(ctx),
83 26 => self.evaluate_26(ctx),
84 27 => self.evaluate_27(ctx),
85 28 => self.evaluate_28(ctx),
86 29 => self.evaluate_29(ctx),
87 30 => self.evaluate_30(ctx),
88 31 => self.evaluate_31(ctx),
89 39 => self.evaluate_39(ctx),
90 44 => self.evaluate_44(ctx),
91 49 => self.evaluate_49(ctx),
92 50 => self.evaluate_50(ctx),
93 51 => self.evaluate_51(ctx),
94 52 => self.evaluate_52(ctx),
95 53 => self.evaluate_53(ctx),
96 54 => self.evaluate_54(ctx),
97 55 => self.evaluate_55(ctx),
98 56 => self.evaluate_56(ctx),
99 57 => self.evaluate_57(ctx),
100 58 => self.evaluate_58(ctx),
101 59 => self.evaluate_59(ctx),
102 60 => self.evaluate_60(ctx),
103 61 => self.evaluate_61(ctx),
104 62 => self.evaluate_62(ctx),
105 63 => self.evaluate_63(ctx),
106 65 => self.evaluate_65(ctx),
107 66 => self.evaluate_66(ctx),
108 67 => self.evaluate_67(ctx),
109 68 => self.evaluate_68(ctx),
110 69 => self.evaluate_69(ctx),
111 70 => self.evaluate_70(ctx),
112 71 => self.evaluate_71(ctx),
113 72 => self.evaluate_72(ctx),
114 73 => self.evaluate_73(ctx),
115 74 => self.evaluate_74(ctx),
116 75 => self.evaluate_75(ctx),
117 77 => self.evaluate_77(ctx),
118 78 => self.evaluate_78(ctx),
119 79 => self.evaluate_79(ctx),
120 80 => self.evaluate_80(ctx),
121 81 => self.evaluate_81(ctx),
122 82 => self.evaluate_82(ctx),
123 83 => self.evaluate_83(ctx),
124 84 => self.evaluate_84(ctx),
125 85 => self.evaluate_85(ctx),
126 86 => self.evaluate_86(ctx),
127 87 => self.evaluate_87(ctx),
128 88 => self.evaluate_88(ctx),
129 490 => self.evaluate_490(ctx),
130 491 => self.evaluate_491(ctx),
131 492 => self.evaluate_492(ctx),
132 493 => self.evaluate_493(ctx),
133 494 => self.evaluate_494(ctx),
134 500 => self.evaluate_500(ctx),
135 501 => self.evaluate_501(ctx),
136 502 => self.evaluate_502(ctx),
137 503 => self.evaluate_503(ctx),
138 504 => self.evaluate_504(ctx),
139 505 => self.evaluate_505(ctx),
140 506 => self.evaluate_506(ctx),
141 507 => self.evaluate_507(ctx),
142 511 => self.evaluate_511(ctx),
143 512 => self.evaluate_512(ctx),
144 513 => self.evaluate_513(ctx),
145 514 => self.evaluate_514(ctx),
146 516 => self.evaluate_516(ctx),
147 903 => self.evaluate_903(ctx),
148 906 => self.evaluate_906(ctx),
149 908 => self.evaluate_908(ctx),
150 911 => self.evaluate_911(ctx),
151 912 => self.evaluate_912(ctx),
152 914 => self.evaluate_914(ctx),
153 931 => self.evaluate_931(ctx),
154 932 => self.evaluate_932(ctx),
155 933 => self.evaluate_933(ctx),
156 934 => self.evaluate_934(ctx),
157 935 => self.evaluate_935(ctx),
158 939 => self.evaluate_939(ctx),
159 940 => self.evaluate_940(ctx),
160 942 => self.evaluate_942(ctx),
161 950 => self.evaluate_950(ctx),
162 951 => self.evaluate_951(ctx),
163 960 => self.evaluate_960(ctx),
164 961 => self.evaluate_961(ctx),
165 962 => self.evaluate_962(ctx),
166 2042 => self.evaluate_2042(ctx),
167 2060 => self.evaluate_2060(ctx),
168 2061 => self.evaluate_2061(ctx),
169 2062 => self.evaluate_2062(ctx),
170 2063 => self.evaluate_2063(ctx),
171 2064 => self.evaluate_2064(ctx),
172 2066 => self.evaluate_2066(ctx),
173 2068 => self.evaluate_2068(ctx),
174 2069 => self.evaluate_2069(ctx),
175 2070 => self.evaluate_2070(ctx),
176 2071 => self.evaluate_2071(ctx),
177 2072 => self.evaluate_2072(ctx),
178 2073 => self.evaluate_2073(ctx),
179 _ => ConditionResult::Unknown,
180 }
181 }
182
183 fn is_external(&self, condition: u32) -> bool {
184 self.external_conditions.contains(&condition)
185 }
186 fn is_known(&self, condition: u32) -> bool {
187 matches!(
188 condition,
189 1 | 2
190 | 3
191 | 4
192 | 5
193 | 8
194 | 9
195 | 10
196 | 11
197 | 12
198 | 13
199 | 15
200 | 16
201 | 17
202 | 18
203 | 20
204 | 21
205 | 22
206 | 25
207 | 26
208 | 27
209 | 28
210 | 29
211 | 30
212 | 31
213 | 39
214 | 44
215 | 49
216 | 50
217 | 51
218 | 52
219 | 53
220 | 54
221 | 55
222 | 56
223 | 57
224 | 58
225 | 59
226 | 60
227 | 61
228 | 62
229 | 63
230 | 65
231 | 66
232 | 67
233 | 68
234 | 69
235 | 70
236 | 71
237 | 72
238 | 73
239 | 74
240 | 75
241 | 77
242 | 78
243 | 79
244 | 80
245 | 81
246 | 82
247 | 83
248 | 84
249 | 85
250 | 86
251 | 87
252 | 88
253 | 490
254 | 491
255 | 492
256 | 493
257 | 494
258 | 500
259 | 501
260 | 502
261 | 503
262 | 504
263 | 505
264 | 506
265 | 507
266 | 511
267 | 512
268 | 513
269 | 514
270 | 516
271 | 903
272 | 906
273 | 908
274 | 911
275 | 912
276 | 914
277 | 931
278 | 932
279 | 933
280 | 934
281 | 935
282 | 939
283 | 940
284 | 942
285 | 950
286 | 951
287 | 960
288 | 961
289 | 962
290 | 2042
291 | 2060
292 | 2061
293 | 2062
294 | 2063
295 | 2064
296 | 2066
297 | 2068
298 | 2069
299 | 2070
300 | 2071
301 | 2072
302 | 2073
303 )
304 }
305}
306
307impl QuotesConditionEvaluatorFV2504 {
308 /// [31] Es sind nur die Artikelnummern erlaubt, die in der Codeliste der Artikelnummern des BDEW mit dem entsprechenden Prüfidentifikator versehen sind.
309 /// EXTERNAL: Requires context from outside the message.
310 fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
311 ctx.external.evaluate("artikel_number_in_bdew_codelist")
312 }
313
314 /// [56] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Messlokation angegeben ist.
315 /// EXTERNAL: Requires context from outside the message.
316 // 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)
317 fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
318 ctx.external.evaluate("loc_172_id_is_messlokation")
319 }
320
321 /// [57] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Netzlokation angegeben ist.
322 /// EXTERNAL: Requires context from outside the message.
323 // 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)
324 fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
325 ctx.external.evaluate("loc_172_id_is_netzlokation")
326 }
327
328 /// [58] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Steuerbaren Ressource angegeben ist.
329 /// EXTERNAL: Requires context from outside the message.
330 // 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)
331 fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
332 ctx.external.evaluate("loc_172_id_is_steuerbare_ressource")
333 }
334
335 /// [59] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.1 „Konfigurationsprodukte Schaltzeitdefinition“ enthalten sind.
336 /// EXTERNAL: Requires context from outside the message.
337 fn evaluate_59(&self, ctx: &EvaluationContext) -> ConditionResult {
338 ctx.external
339 .evaluate("konfiguration_in_kapitel_4_1_codelist")
340 }
341
342 /// [60] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.2 „Konfigurationsprodukte Leistungskurvendefinition“ enthalten sind.
343 /// EXTERNAL: Requires context from outside the message.
344 fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
345 ctx.external.evaluate("config_product_leistungskurve")
346 }
347
348 /// [61] Es sind nur die Konfigurations-Produkte erlaubt, die in der Codeliste der Konfigurationen im Kapitel 4.3 „Konfigurationsprodukte Ad-hoc-Steuerkanal“ enthalten sind.
349 /// EXTERNAL: Requires context from outside the message.
350 fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
351 ctx.external.evaluate("config_product_adhoc_steuerkanal")
352 }
353
354 /// [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.
355 /// EXTERNAL: Requires context from outside the message.
356 fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
357 ctx.external.evaluate("messprodukts_typ2_backend_lf_nb")
358 }
359
360 /// [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.
361 /// EXTERNAL: Requires context from outside the message.
362 fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
363 ctx.external.evaluate("messprodukts_typ2_smgw")
364 }
365
366 /// [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.
367 /// EXTERNAL: Requires context from outside the message.
368 fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
369 ctx.external
370 .evaluate("artikel_id_abrechnung_messstellenbetrieb_strom")
371 }
372
373 /// [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.
374 /// EXTERNAL: Requires context from outside the message.
375 fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
376 ctx.external
377 .evaluate("messprodukt_position_code_valid_typ2")
378 }
379
380 /// [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...
381 fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
382 let dtm_segs = ctx.find_segments("DTM");
383 match dtm_segs
384 .first()
385 .and_then(|s| s.elements.first())
386 .and_then(|e| e.get(1))
387 {
388 Some(val) => is_mesz_utc(val),
389 None => ConditionResult::False, // segment absent → condition not applicable
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 let dtm_segs = ctx.find_segments("DTM");
396 match dtm_segs
397 .first()
398 .and_then(|s| s.elements.first())
399 .and_then(|e| e.get(1))
400 {
401 Some(val) => is_mez_utc(val),
402 None => ConditionResult::False, // segment absent → condition not applicable
403 }
404 }
405
406 /// [961] Format: SR-ID
407 // 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)
408 fn evaluate_961(&self, ctx: &EvaluationContext) -> ConditionResult {
409 let segs = ctx.find_segments_with_qualifier("LOC", 0, "Z19");
410 match segs
411 .first()
412 .and_then(|s| s.elements.get(1))
413 .and_then(|e| e.first())
414 {
415 Some(val) => validate_sr_id(val),
416 None => ConditionResult::False, // segment absent → condition not applicable
417 }
418 }
419
420 /// [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ü...
421 /// EXTERNAL: Requires context from outside the message.
422 fn evaluate_2066(&self, ctx: &EvaluationContext) -> ConditionResult {
423 ctx.external
424 .evaluate("sg28_repetition_count_for_schwellwert_produkt")
425 }
426
427 /// [1] Wenn Position nicht angeboten werden kann, weil rechtliche Regelungen oder Rechte Dritter dem entgegenstehen
428 /// EXTERNAL: Requires context from outside the message.
429 fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
430 ctx.external.evaluate("position_legally_blocked")
431 }
432
433 /// [2] Wenn in derselben SG27 LIN IMD DE7081 (Produkt-/Leistungsbeschreibung) mit Code Z09 (Kann nicht angeboten werden) nicht vorhanden.
434 // 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)
435 fn evaluate_2(&self, ctx: &EvaluationContext) -> ConditionResult {
436 match ctx.any_group_has_qualifier("IMD", 1, "Z09", &["SG27"]) {
437 ConditionResult::True => ConditionResult::False,
438 ConditionResult::False => ConditionResult::True,
439 ConditionResult::Unknown => ConditionResult::Unknown,
440 }
441 }
442
443 /// [3] Wenn CCI+++Z64 vorhanden ist
444 fn evaluate_3(&self, ctx: &EvaluationContext) -> ConditionResult {
445 ctx.has_qualifier("CCI", 2, "Z64")
446 }
447
448 /// [4] Wenn am Gerät vorhanden und abweichend von Gerätenummer
449 /// EXTERNAL: Requires context from outside the message.
450 fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
451 ctx.external
452 .evaluate("device_attribute_differs_from_device_number")
453 }
454
455 /// [5] Wenn in derselben SG27 LIN die Artikelnummer 9990001000649 vorhanden ist
456 // 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)
457 fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
458 ctx.any_group_has_qualifier("LIN", 2, "9990001000649", &["SG27"])
459 }
460
461 /// [8] Wenn SG28 CCI+++E13 CAV+EHZ vorhanden
462 // 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)
463 fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
464 ctx.any_group_has_co_occurrence("CCI", 2, &["E13"], "CAV", 0, 0, &["EHZ"], &["SG28"])
465 }
466
467 /// [9] Wenn in derselben SG27 LIN die Artikelnummer 9990001000657 vorhanden ist
468 // 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)
469 fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
470 ctx.any_group_has_qualifier("LIN", 2, "9990001000657", &["SG27"])
471 }
472
473 /// [10] Wenn SG28 CAV+MIW/MPW/MBW vorhanden
474 fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
475 ctx.any_group_has_any_qualifier("CAV", 0, &["MIW", "MPW", "MBW"], &["SG28"])
476 }
477
478 /// [11] Wenn in derselben SG27 LIN die Artikelnummer 9990001000665 vorhanden ist
479 // REVIEW: Checks if any SG27 group has LIN with article number 9990001000665 at elements[2][0]. Same pattern as condition 5. (medium confidence)
480 fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
481 ctx.any_group_has_qualifier("LIN", 2, "9990001000665", &["SG27"])
482 }
483
484 /// [12] Wenn in derselben SG27 LIN die Artikelnummer 9990001000673 vorhanden ist
485 // REVIEW: Checks if any SG27 group has LIN with article number 9990001000673 at elements[2][0]. Same pattern as condition 5. (medium confidence)
486 fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
487 ctx.any_group_has_qualifier("LIN", 2, "9990001000673", &["SG27"])
488 }
489
490 /// [13] Wenn am Gerät vorhanden
491 /// EXTERNAL: Requires context from outside the message.
492 fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
493 ctx.external.evaluate("device_attribute_present")
494 }
495
496 /// [15] Wenn in derselben SG27 LIN die Artikelnummer 9990001000772 vorhanden ist
497 // REVIEW: Checks if any SG27 group has LIN with article number 9990001000772 at elements[2][0]. Same pattern as condition 5. (medium confidence)
498 fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
499 ctx.any_group_has_qualifier("LIN", 2, "9990001000772", &["SG27"])
500 }
501
502 /// [16] Wenn in derselben SG27 LIN die Artikelnummer 9990001000780 vorhanden ist
503 // REVIEW: Checks if any SG27 group has LIN with article number 9990001000780 at elements[2][0]. Same pattern as condition 5. (medium confidence)
504 fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
505 ctx.any_group_has_qualifier("LIN", 2, "9990001000780", &["SG27"])
506 }
507
508 /// [17] Wenn das Angebot per REQOTE angefragt wurde
509 // 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)
510 fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
511 ctx.has_qualifier("RFF", 0, "AAV")
512 }
513
514 /// [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...
515 // 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)
516 fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
517 // Check if offer references additional market locations via RFF+Z18
518 // RFF+Z18 = 'Referenz auf ID weiterer Marktlokationen' — presence indicates multiple locations covered
519 // Note: the 'identical connection user' and 'iMS classification' sub-conditions require external context
520 ctx.has_qualifier("RFF", 0, "Z18")
521 }
522
523 /// [20] Wenn IMD++Z33 vorhanden
524 fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
525 ctx.has_qualifier("IMD", 1, "Z33")
526 }
527
528 /// [21] Wenn IMD++Z34 vorhanden
529 fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
530 ctx.has_qualifier("IMD", 1, "Z34")
531 }
532
533 /// [22] Wenn CCI+++Z75 vorhanden ist
534 fn evaluate_22(&self, ctx: &EvaluationContext) -> ConditionResult {
535 ctx.has_qualifier("CCI", 2, "Z75")
536 }
537
538 /// [25] Wenn SG28 CCI+++E13 CAV+MME vorhanden
539 fn evaluate_25(&self, ctx: &EvaluationContext) -> ConditionResult {
540 ctx.any_group_has_co_occurrence("CCI", 2, &["E13"], "CAV", 0, 0, &["MME"], &["SG28"])
541 }
542
543 /// [26] Wenn DTM+203 nicht vorhanden
544 fn evaluate_26(&self, ctx: &EvaluationContext) -> ConditionResult {
545 ctx.lacks_qualifier("DTM", 0, "203")
546 }
547
548 /// [27] Wenn DTM+469 nicht vorhanden
549 fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
550 ctx.lacks_qualifier("DTM", 0, "469")
551 }
552
553 /// [28] Wenn RFF+AAV vorhanden
554 fn evaluate_28(&self, ctx: &EvaluationContext) -> ConditionResult {
555 ctx.has_qualifier("RFF", 0, "AAV")
556 }
557
558 /// [29] Wenn DTM+469 in Anfrage (REQOTE) vorhanden war
559 /// EXTERNAL: Requires context from outside the message.
560 fn evaluate_29(&self, ctx: &EvaluationContext) -> ConditionResult {
561 ctx.external.evaluate("reqote_had_dtm_469")
562 }
563
564 /// [30] Wenn SG27 IMD+Z09 vorhanden
565 fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
566 ctx.any_group_has_qualifier("IMD", 0, "Z09", &["SG27"])
567 }
568
569 /// [39] MP-ID nur aus Sparte Strom
570 /// EXTERNAL: Requires context from outside the message.
571 fn evaluate_39(&self, ctx: &EvaluationContext) -> ConditionResult {
572 ctx.external.evaluate("mp_id_is_strom")
573 }
574
575 /// [44] Wenn MSBA nicht Eigentümer des Gerätes/der Geräte ist
576 /// EXTERNAL: Requires context from outside the message.
577 fn evaluate_44(&self, ctx: &EvaluationContext) -> ConditionResult {
578 ctx.external.evaluate("msba_is_not_device_owner")
579 }
580
581 /// [49] Wenn SG27 LIN++Z64 (Erforderliches Produkt Schaltzeitdefinitionen) vorhanden
582 fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
583 ctx.any_group_has_qualifier("LIN", 1, "Z64", &["SG27"])
584 }
585
586 /// [50] Wenn SG27 LIN++Z65 (Erforderliches Produkt Leistungskurvendefinitionen) vorhanden
587 fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
588 ctx.any_group_has_qualifier("LIN", 1, "Z65", &["SG27"])
589 }
590
591 /// [51] Wenn SG27 LIN++Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) vorhanden
592 fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
593 ctx.any_group_has_qualifier("LIN", 1, "Z66", &["SG27"])
594 }
595
596 /// [52] Wenn SG27 LIN++Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) vorhanden
597 fn evaluate_52(&self, ctx: &EvaluationContext) -> ConditionResult {
598 ctx.has_qualifier("LIN", 1, "Z67")
599 }
600
601 /// [53] Wenn SG27 LIN++Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ2 aus SMGW) vorhanden
602 fn evaluate_53(&self, ctx: &EvaluationContext) -> ConditionResult {
603 ctx.has_qualifier("LIN", 1, "Z68")
604 }
605
606 /// [54] Wenn in der Anfrage zum Angebot einer Konfiguration vorhanden
607 /// EXTERNAL: Requires context from outside the message.
608 fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
609 ctx.external.evaluate("configuration_in_request")
610 }
611
612 /// [55] Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Marktlokation angegeben ist.
613 fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
614 // Wenn in LOC+172 DE3225 (Meldepunkt) die ID einer Marktlokation angegeben ist.
615 // LOC elements[0][0] == "172" (qualifier), elements[1][0] == DE3225 (Identifikator)
616 let locs = ctx.find_segments_with_qualifier("LOC", 0, "172");
617 match locs
618 .first()
619 .and_then(|s| s.elements.get(1))
620 .and_then(|e| e.first())
621 {
622 Some(val) => validate_malo_id(val),
623 None => ConditionResult::False, // segment absent → condition not applicable
624 }
625 }
626
627 /// [65] Wenn DTM+203 (Ausführungsdatum) im DE2380 < 202312312300?+00
628 // 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)
629 fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
630 let segs = ctx.find_segments_with_qualifier("DTM", 0, "203");
631 match segs.first() {
632 Some(dtm) => {
633 match dtm
634 .elements
635 .first()
636 .and_then(|e| e.get(1))
637 .map(|s| s.as_str())
638 {
639 Some(value) if !value.is_empty() => {
640 ConditionResult::from(value < "202312312300+00")
641 }
642 _ => ConditionResult::Unknown,
643 }
644 }
645 None => ConditionResult::False, // segment absent → condition not applicable
646 }
647 }
648
649 /// [66] Wenn DTM+469 (Beginn zum (nächstmöglichen Termin)) im DE2380 < 202312312300?+00
650 // 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)
651 fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
652 let segs = ctx.find_segments_with_qualifier("DTM", 0, "469");
653 match segs.first() {
654 Some(dtm) => {
655 match dtm
656 .elements
657 .first()
658 .and_then(|e| e.get(1))
659 .map(|s| s.as_str())
660 {
661 Some(value) if !value.is_empty() => {
662 ConditionResult::from(value < "202312312300+00")
663 }
664 _ => ConditionResult::Unknown,
665 }
666 }
667 None => ConditionResult::False, // segment absent → condition not applicable
668 }
669 }
670
671 /// [67] Wenn DTM+203 (Ausführungsdatum) im DE2380 ≥ 202312312300?+00
672 // 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)
673 fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
674 let segs = ctx.find_segments_with_qualifier("DTM", 0, "203");
675 match segs.first() {
676 Some(dtm) => {
677 match dtm
678 .elements
679 .first()
680 .and_then(|e| e.get(1))
681 .map(|s| s.as_str())
682 {
683 Some(value) if !value.is_empty() => {
684 ConditionResult::from(value >= "202312312300+00")
685 }
686 _ => ConditionResult::Unknown,
687 }
688 }
689 None => ConditionResult::False, // segment absent → condition not applicable
690 }
691 }
692
693 /// [68] Wenn DTM+469 (Beginn zum (nächstmöglichen Termin)) im DE2380 ≥ 202312312300?+00
694 // 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)
695 fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
696 let segs = ctx.find_segments_with_qualifier("DTM", 0, "469");
697 if segs.is_empty() {
698 return ConditionResult::Unknown;
699 }
700 for seg in segs {
701 let val = seg
702 .elements
703 .get(0)
704 .and_then(|e| e.get(1))
705 .map(|s| s.as_str())
706 .unwrap_or("");
707 if val >= "202312312300+00" {
708 return ConditionResult::True;
709 }
710 }
711 ConditionResult::False
712 }
713
714 /// [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...
715 /// EXTERNAL: Requires context from outside the message.
716 fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
717 ctx.external.evaluate("pia5_product_is_threshold_trigger")
718 }
719
720 /// [72] wenn im DE3155 in demselben COM der Code EM vorhanden ist
721 fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
722 let segs = ctx.find_segments("COM");
723 if segs.is_empty() {
724 return ConditionResult::Unknown;
725 }
726 ConditionResult::from(segs.iter().any(|seg| {
727 seg.elements
728 .first()
729 .and_then(|e| e.get(1))
730 .is_some_and(|v| v == "EM")
731 }))
732 }
733
734 /// [73] wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
735 fn evaluate_73(&self, ctx: &EvaluationContext) -> ConditionResult {
736 let segs = ctx.find_segments("COM");
737 if segs.is_empty() {
738 return ConditionResult::Unknown;
739 }
740 ConditionResult::from(segs.iter().any(|seg| {
741 seg.elements
742 .first()
743 .and_then(|e| e.get(1))
744 .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
745 }))
746 }
747
748 /// [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.
749 /// EXTERNAL: Requires context from outside the message.
750 fn evaluate_74(&self, ctx: &EvaluationContext) -> ConditionResult {
751 ctx.external.evaluate("product_in_typ2_backend_codelist")
752 }
753
754 /// [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.
755 /// EXTERNAL: Requires context from outside the message.
756 fn evaluate_75(&self, ctx: &EvaluationContext) -> ConditionResult {
757 ctx.external.evaluate("product_in_typ2_smgw_codelist")
758 }
759
760 /// [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.
761 /// EXTERNAL: Requires context from outside the message.
762 fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
763 ctx.external
764 .evaluate("request_contained_typ2_backend_product")
765 }
766
767 /// [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.
768 /// EXTERNAL: Requires context from outside the message.
769 fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
770 ctx.external.evaluate("request_contained_typ2_smgw_product")
771 }
772
773 /// [79] Wenn IMD DE7081 (Produkt-/Leistungsbeschreibung) mit Wert Z07 (Kauf) vorhanden.
774 fn evaluate_79(&self, ctx: &EvaluationContext) -> ConditionResult {
775 let segs = ctx.find_segments("IMD");
776 if segs.is_empty() {
777 return ConditionResult::Unknown;
778 }
779 ConditionResult::from(segs.iter().any(|seg| {
780 seg.elements
781 .get(1)
782 .and_then(|e| e.first())
783 .is_some_and(|v| v == "Z07")
784 }))
785 }
786
787 /// [80] Wenn IMD DE7081 (Produkt-/Leistungsbeschreibung) mit Wert Z08 (Nutzungsüberlassung) vorhanden.
788 fn evaluate_80(&self, ctx: &EvaluationContext) -> ConditionResult {
789 let segs = ctx.find_segments("IMD");
790 if segs.is_empty() {
791 return ConditionResult::Unknown;
792 }
793 ConditionResult::from(segs.iter().any(|seg| {
794 seg.elements
795 .get(1)
796 .and_then(|e| e.first())
797 .is_some_and(|v| v == "Z08")
798 }))
799 }
800
801 /// [81] Wenn IMD DE7081 (Produkt-/Leistungsbeschreibung) mit Wert Z33 (Angebot auf Basis Preisblatt) vorhanden.
802 fn evaluate_81(&self, ctx: &EvaluationContext) -> ConditionResult {
803 let segs = ctx.find_segments("IMD");
804 if segs.is_empty() {
805 return ConditionResult::Unknown;
806 }
807 ConditionResult::from(segs.iter().any(|seg| {
808 seg.elements
809 .get(1)
810 .and_then(|e| e.first())
811 .is_some_and(|v| v == "Z33")
812 }))
813 }
814
815 /// [82] Wenn IMD DE7081 (Produkt-/Leistungsbeschreibung) mit Wert Z34 (Individuelles Angebot) vorhanden.
816 fn evaluate_82(&self, ctx: &EvaluationContext) -> ConditionResult {
817 let segs = ctx.find_segments("IMD");
818 if segs.is_empty() {
819 return ConditionResult::Unknown;
820 }
821 ConditionResult::from(segs.iter().any(|seg| {
822 seg.elements
823 .get(1)
824 .and_then(|e| e.first())
825 .is_some_and(|v| v == "Z34")
826 }))
827 }
828
829 /// [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.
830 // 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)
831 fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
832 let segs = ctx.find_segments("PIA");
833 if segs.is_empty() {
834 return ConditionResult::Unknown;
835 }
836 ConditionResult::from(segs.iter().any(|seg| {
837 let is_z02 = seg
838 .elements
839 .first()
840 .and_then(|e| e.first())
841 .is_some_and(|v| v == "Z02");
842 let ends_with_01 = seg
843 .elements
844 .get(1)
845 .and_then(|e| e.first())
846 .is_some_and(|v| v.ends_with("01"));
847 is_z02 && ends_with_01
848 }))
849 }
850
851 /// [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.
852 // 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)
853 fn evaluate_84(&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("02"))
860 .unwrap_or(false)
861 }))
862 }
863
864 /// [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) ...
865 // REVIEW: Checks for PIA+Z02 where DE7140 (elements[1][0]) ends with '03' (Transaktionskosten). Same pattern as condition 84. (medium confidence)
866 fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
867 let pias = ctx.find_segments_with_qualifier("PIA", 0, "Z02");
868 ConditionResult::from(pias.iter().any(|s| {
869 s.elements
870 .get(1)
871 .and_then(|e| e.first())
872 .map(|v| v.ends_with("03"))
873 .unwrap_or(false)
874 }))
875 }
876
877 /// [86] Wenn in derselben SG31 PRI (Preisangabe zur Position) das DE5387 mit dem Wert Z01 (Einrichtungspreis) vorhanden ist.
878 fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
879 ctx.any_group_has_qualifier("PRI", 0, "Z01", &["SG31"])
880 }
881
882 /// [87] Wenn in derselben SG31 PRI (Preisangabe zur Position) das DE5387 mit dem Wert Z02 (Transaktionspreis) vorhanden ist.
883 fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
884 ctx.any_group_has_qualifier("PRI", 0, "Z02", &["SG31"])
885 }
886
887 /// [88] Wenn in derselben SG31 PRI (Preisangabe zur Position) das DE5387 mit dem Wert Z03 (Betriebspreis) vorhanden ist.
888 fn evaluate_88(&self, ctx: &EvaluationContext) -> ConditionResult {
889 ctx.any_group_has_qualifier("PRI", 0, "Z03", &["SG31"])
890 }
891
892 /// [492] wenn MP-ID in NAD+MR aus Sparte Strom
893 /// EXTERNAL: Requires context from outside the message.
894 fn evaluate_492(&self, ctx: &EvaluationContext) -> ConditionResult {
895 ctx.external.evaluate("recipient_is_electricity_sector")
896 }
897
898 /// [493] wenn MP-ID in NAD+MR aus Sparte Gas
899 /// EXTERNAL: Requires context from outside the message.
900 fn evaluate_493(&self, ctx: &EvaluationContext) -> ConditionResult {
901 ctx.external.evaluate("recipient_is_gas_sector")
902 }
903
904 /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt.
905 /// EXTERNAL: Requires context from outside the message.
906 // 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)
907 fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
908 ctx.external.evaluate("document_date_is_creation_or_past")
909 }
910
911 /// [500] Hinweis: Angabe eines technischen Ansprechpartners für die Geräteübernahme
912 fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
913 // Hinweis: Angabe eines technischen Ansprechpartners für die Geräteübernahme — informational note, always applies
914 ConditionResult::True
915 }
916
917 /// [501] Hinweis: Verwendung der ID der Marktlokation
918 fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
919 // Hinweis: Verwendung der ID der Marktlokation — informational note, always applies
920 ConditionResult::True
921 }
922
923 /// [502] Hinweis: Verwendung der ID der Messlokation
924 fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
925 // Hinweis: Verwendung der ID der Messlokation — informational note, always applies
926 ConditionResult::True
927 }
928
929 /// [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.
930 fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
931 // 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.
932 ConditionResult::True
933 }
934
935 /// [504] Hinweis: Wert aus BGM+311 DE1004 der REQOTE mit der die Angebotsanfrage erfolgt ist.
936 fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
937 // Hinweis: Wert aus BGM+311 DE1004 der REQOTE mit der die Angebotsanfrage erfolgt ist.
938 ConditionResult::True
939 }
940
941 /// [505] Hinweis: Wert aus BGM+Z29 DE1004 der REQOTE, mit der die Anfrage Rechnungsabwicklung erfolgt ist.
942 fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
943 // Hinweis: Wert aus BGM+Z29 DE1004 der REQOTE, mit der die Anfrage Rechnungsabwicklung erfolgt ist.
944 ConditionResult::True
945 }
946
947 /// [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
948 fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
949 // Hinweis: Wenn zu einer Position mehrere Gerätenummern existieren, sind die Gerätenummern in derselben Position mittels Wiederholung der SG32 RFF+Z09 anzugeben.
950 ConditionResult::True
951 }
952
953 /// [507] Hinweis: Verwendung der ID der Tranche
954 fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
955 // Hinweis: Verwendung der ID der Tranche
956 ConditionResult::True
957 }
958
959 /// [511] Hinweis: Wert aus BGM+Z74 (Bestellung eines Angebots einer Konfiguration) DE1004 der REQOTE mit der die Anfrage einer Konfiguration erfolgt ist.
960 fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
961 // Hinweis: Wert aus BGM+Z74 DE1004 der REQOTE — informational note, always applies
962 ConditionResult::True
963 }
964
965 /// [512] Hinweis: Verwendung der ID der Netzlokation
966 fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
967 // Hinweis: Verwendung der ID der Netzlokation — informational note, always applies
968 ConditionResult::True
969 }
970
971 /// [513] Hinweis: Verwendung der ID der Steuerbaren Ressource
972 fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
973 // Hinweis: Verwendung der ID der Steuerbaren Ressource — informational note, always applies
974 ConditionResult::True
975 }
976
977 /// [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.
978 fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
979 // Hinweis: Angabe gemäß Preisblatt des MSB — informational note, always applies
980 ConditionResult::True
981 }
982
983 /// [516] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
984 fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
985 // Hinweis: Es darf nur eine Information im DE3148 übermittelt werden — informational note, always applies
986 ConditionResult::True
987 }
988
989 /// [903] Format: Möglicher Wert: 1
990 // 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)
991 fn evaluate_903(&self, ctx: &EvaluationContext) -> ConditionResult {
992 // Format: Möglicher Wert: 1 — the only permitted value is exactly 1
993 let segs = ctx.find_segments("QTY");
994 match segs
995 .first()
996 .and_then(|s| s.elements.first())
997 .and_then(|e| e.get(1))
998 {
999 Some(val) => validate_numeric(val, "==", 1.0),
1000 None => ConditionResult::False, // segment absent → condition not applicable
1001 }
1002 }
1003
1004 /// [906] Format: max. 3 Nachkommastellen
1005 // 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)
1006 fn evaluate_906(&self, ctx: &EvaluationContext) -> ConditionResult {
1007 // Format: max. 3 Nachkommastellen — at most 3 decimal places
1008 let segs = ctx.find_segments("QTY");
1009 match segs
1010 .first()
1011 .and_then(|s| s.elements.first())
1012 .and_then(|e| e.get(1))
1013 {
1014 Some(val) => validate_max_decimal_places(val, 3),
1015 None => ConditionResult::False, // segment absent → condition not applicable
1016 }
1017 }
1018
1019 /// [908] Format: Mögliche Werte: 1 bis n
1020 // 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)
1021 fn evaluate_908(&self, ctx: &EvaluationContext) -> ConditionResult {
1022 // Format: Mögliche Werte: 1 bis n — value must be >= 1 (positive integer range)
1023 let segs = ctx.find_segments("QTY");
1024 match segs
1025 .first()
1026 .and_then(|s| s.elements.first())
1027 .and_then(|e| e.get(1))
1028 {
1029 Some(val) => validate_numeric(val, ">=", 1.0),
1030 None => ConditionResult::False, // segment absent → condition not applicable
1031 }
1032 }
1033
1034 /// [911] Format: Mögliche Werte: 1 bis n, je Nachricht oder Segmentgruppe bei 1 beginnend und fortlaufend aufsteigend
1035 fn evaluate_911(&self, _ctx: &EvaluationContext) -> ConditionResult {
1036 // Hinweis: Mögliche Werte: 1 bis n, je Nachricht oder Segmentgruppe bei 1 beginnend
1037 // und fortlaufend aufsteigend — informational annotation about sequential numbering,
1038 // always applies unconditionally
1039 ConditionResult::True
1040 }
1041
1042 /// [912] Format: max. 6 Nachkommastellen
1043 // 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)
1044 fn evaluate_912(&self, ctx: &EvaluationContext) -> ConditionResult {
1045 // Format: max. 6 Nachkommastellen — at most 6 decimal places
1046 let segs = ctx.find_segments("QTY");
1047 match segs
1048 .first()
1049 .and_then(|s| s.elements.first())
1050 .and_then(|e| e.get(1))
1051 {
1052 Some(val) => validate_max_decimal_places(val, 6),
1053 None => ConditionResult::False, // segment absent → condition not applicable
1054 }
1055 }
1056
1057 /// [914] Format: Möglicher Wert: > 0
1058 // 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)
1059 fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
1060 // Format: Möglicher Wert: > 0 — numeric value must be greater than 0
1061 let segs = ctx.find_segments("QTY");
1062 match segs
1063 .first()
1064 .and_then(|s| s.elements.first())
1065 .and_then(|e| e.get(1))
1066 {
1067 Some(val) => validate_numeric(val, ">", 0.0),
1068 None => ConditionResult::False, // segment absent → condition not applicable
1069 }
1070 }
1071
1072 /// [931] Format: ZZZ = +00
1073 // 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)
1074 fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
1075 // Format: ZZZ = +00 — timezone must be UTC (+00)
1076 let dtm_segs = ctx.find_segments("DTM");
1077 match dtm_segs
1078 .first()
1079 .and_then(|s| s.elements.first())
1080 .and_then(|e| e.get(1))
1081 {
1082 Some(val) => validate_timezone_utc(val),
1083 None => ConditionResult::False, // segment absent → condition not applicable
1084 }
1085 }
1086
1087 /// [932] Format: HHMM = 2200
1088 // 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)
1089 fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
1090 // Format: HHMM = 2200 — time portion must be 2200
1091 let dtm_segs = ctx.find_segments("DTM");
1092 match dtm_segs
1093 .first()
1094 .and_then(|s| s.elements.first())
1095 .and_then(|e| e.get(1))
1096 {
1097 Some(val) => validate_hhmm_equals(val, "2200"),
1098 None => ConditionResult::False, // segment absent → condition not applicable
1099 }
1100 }
1101
1102 /// [933] Format: HHMM = 2300
1103 // 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)
1104 fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
1105 // Format: HHMM = 2300 — time portion must be 2300
1106 let dtm_segs = ctx.find_segments("DTM");
1107 match dtm_segs
1108 .first()
1109 .and_then(|s| s.elements.first())
1110 .and_then(|e| e.get(1))
1111 {
1112 Some(val) => validate_hhmm_equals(val, "2300"),
1113 None => ConditionResult::False, // segment absent → condition not applicable
1114 }
1115 }
1116
1117 /// [934] Format: HHMM = 0400
1118 // 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)
1119 fn evaluate_934(&self, ctx: &EvaluationContext) -> ConditionResult {
1120 // Format: HHMM = 0400 — time portion must be 0400
1121 let dtm_segs = ctx.find_segments("DTM");
1122 match dtm_segs
1123 .first()
1124 .and_then(|s| s.elements.first())
1125 .and_then(|e| e.get(1))
1126 {
1127 Some(val) => validate_hhmm_equals(val, "0400"),
1128 None => ConditionResult::False, // segment absent → condition not applicable
1129 }
1130 }
1131
1132 /// [935] Format: HHMM = 0500
1133 // 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)
1134 fn evaluate_935(&self, ctx: &EvaluationContext) -> ConditionResult {
1135 // Format: HHMM = 0500 — validate time portion of DTM value equals 0500
1136 let dtm_segs = ctx.find_segments("DTM");
1137 match dtm_segs
1138 .first()
1139 .and_then(|s| s.elements.first())
1140 .and_then(|e| e.get(1))
1141 {
1142 Some(val) => validate_hhmm_equals(val, "0500"),
1143 None => ConditionResult::False, // segment absent → condition not applicable
1144 }
1145 }
1146
1147 /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
1148 // 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)
1149 fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
1150 for seg in ctx.find_segments("COM") {
1151 if let Some(elem) = seg.elements.first() {
1152 if let Some(value) = elem.first() {
1153 if value.contains('@') && value.contains('.') {
1154 return ConditionResult::True;
1155 }
1156 }
1157 }
1158 }
1159 ConditionResult::False
1160 }
1161
1162 /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
1163 // 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)
1164 fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
1165 for seg in ctx.find_segments("COM") {
1166 if let Some(elem) = seg.elements.first() {
1167 if let Some(value) = elem.first() {
1168 if value.starts_with('+') && value.chars().skip(1).all(|c| c.is_ascii_digit()) {
1169 return ConditionResult::True;
1170 }
1171 }
1172 }
1173 }
1174 ConditionResult::False
1175 }
1176
1177 /// [942] Format: n1-n2-n1-n3
1178 fn evaluate_942(&self, ctx: &EvaluationContext) -> ConditionResult {
1179 // Format: n1-n2-n1-n3 — Artikelnummer dash-separated digit segment pattern
1180 let segs = ctx.find_segments("PIA");
1181 match segs
1182 .first()
1183 .and_then(|s| s.elements.get(1))
1184 .and_then(|e| e.first())
1185 {
1186 Some(val) => validate_artikel_pattern(val, &[1, 2, 1, 3]),
1187 None => ConditionResult::False, // segment absent → condition not applicable
1188 }
1189 }
1190
1191 /// [950] Format: Marktlokations-ID
1192 fn evaluate_950(&self, ctx: &EvaluationContext) -> ConditionResult {
1193 // Format: Marktlokations-ID — 11 digits with Luhn check digit
1194 let segs = ctx.find_segments_with_qualifier("LOC", 0, "Z16");
1195 match segs
1196 .first()
1197 .and_then(|s| s.elements.get(1))
1198 .and_then(|e| e.first())
1199 {
1200 Some(val) => validate_malo_id(val),
1201 None => ConditionResult::False, // segment absent → condition not applicable
1202 }
1203 }
1204
1205 /// [951] Format: Zählpunktbezeichnung
1206 fn evaluate_951(&self, ctx: &EvaluationContext) -> ConditionResult {
1207 // Format: Zählpunktbezeichnung — 33 alphanumeric characters
1208 let segs = ctx.find_segments_with_qualifier("LOC", 0, "Z17");
1209 match segs
1210 .first()
1211 .and_then(|s| s.elements.get(1))
1212 .and_then(|e| e.first())
1213 {
1214 Some(val) => validate_zahlpunkt(val),
1215 None => ConditionResult::False, // segment absent → condition not applicable
1216 }
1217 }
1218
1219 /// [960] Format: Netzlokations-ID
1220 // REVIEW: Netzlokations-ID is associated with LOC+Z18 (Netzlokation qualifier). The Netzlokations-ID shares the same 11-digit structure with Luhn check as the Marktlokations-ID. No dedicated validate_netzlokation_id helper exists, so validate_malo_id is used as the structurally equivalent validator. (medium confidence)
1221 fn evaluate_960(&self, ctx: &EvaluationContext) -> ConditionResult {
1222 // Format: Netzlokations-ID — 11 digits with Luhn check digit (same format as MaLo-ID)
1223 let segs = ctx.find_segments_with_qualifier("LOC", 0, "Z18");
1224 match segs
1225 .first()
1226 .and_then(|s| s.elements.get(1))
1227 .and_then(|e| e.first())
1228 {
1229 Some(val) => validate_malo_id(val),
1230 None => ConditionResult::False, // segment absent → condition not applicable
1231 }
1232 }
1233
1234 /// [962] Format: max. 6 Vorkommastellen
1235 // 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)
1236 fn evaluate_962(&self, ctx: &EvaluationContext) -> ConditionResult {
1237 // Format: max. 6 Vorkommastellen — applies to PRI price amount (C509.DE5118 = elements[0][1])
1238 let segs = ctx.find_segments("PRI");
1239 match segs
1240 .first()
1241 .and_then(|s| s.elements.first())
1242 .and_then(|e| e.get(1))
1243 {
1244 Some(val) => validate_max_integer_digits(val, 6),
1245 None => ConditionResult::False, // segment absent → condition not applicable
1246 }
1247 }
1248
1249 /// [2042] Innerhalb dieser LIN-Position muss das PIA+Z02 (Artikel-ID) mindestens ein Mal angegeben werden und kann bis zu drei Mal angegeben werden.
1250 // 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)
1251 fn evaluate_2042(&self, ctx: &EvaluationContext) -> ConditionResult {
1252 let count = ctx
1253 .find_segments("PIA")
1254 .iter()
1255 .filter(|s| {
1256 s.elements
1257 .first()
1258 .and_then(|e| e.first())
1259 .is_some_and(|v| v == "Z02")
1260 })
1261 .count();
1262 ConditionResult::from(count >= 1 && count <= 3)
1263 }
1264
1265 /// [2060] Pro Nachricht ist die SG27 LIN+Z64 (Erforderliches Produkt Schaltzeitdefinitionen) maximal einmal anzugeben
1266 fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
1267 let count = ctx
1268 .find_segments("LIN")
1269 .iter()
1270 .filter(|s| {
1271 s.elements
1272 .get(1)
1273 .and_then(|e| e.first())
1274 .is_some_and(|v| v == "Z64")
1275 })
1276 .count();
1277 ConditionResult::from(count <= 1)
1278 }
1279
1280 /// [2061] Pro Nachricht ist die SG27 LIN++Z65 (Erforderliches Produkt Leistungskurvendefinitionen) maximal einmal anzugeben
1281 fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
1282 let count = ctx
1283 .find_segments("LIN")
1284 .iter()
1285 .filter(|s| {
1286 s.elements
1287 .get(1)
1288 .and_then(|e| e.first())
1289 .is_some_and(|v| v == "Z65")
1290 })
1291 .count();
1292 ConditionResult::from(count <= 1)
1293 }
1294
1295 /// [2062] Pro Nachricht ist die SG27 LIN++Z66 (Erforderliches Produkt Ad-hoc-Steuerkanal) maximal einmal anzugeben
1296 fn evaluate_2062(&self, ctx: &EvaluationContext) -> ConditionResult {
1297 let count = ctx.find_segments_with_qualifier("LIN", 1, "Z66").len();
1298 ConditionResult::from(count <= 1)
1299 }
1300
1301 /// [2063] Pro Nachricht ist die SG27 LIN++Z67 (Erforderliches Messprodukt für Werte nach Typ 2 aus Backend) maximal einmal anzugeben
1302 fn evaluate_2063(&self, ctx: &EvaluationContext) -> ConditionResult {
1303 let count = ctx.find_segments_with_qualifier("LIN", 1, "Z67").len();
1304 ConditionResult::from(count <= 1)
1305 }
1306
1307 /// [2064] Pro Nachricht ist die SG27 LIN++Z68 (Erforderliches Produkt Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW) maximal einmal anzugeben
1308 fn evaluate_2064(&self, ctx: &EvaluationContext) -> ConditionResult {
1309 let count = ctx.find_segments_with_qualifier("LIN", 1, "Z68").len();
1310 ConditionResult::from(count <= 1)
1311 }
1312
1313 /// [2068] Pro SG27 LIN ist die SG31 PRI genau einmal anzugeben.
1314 // 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)
1315 fn evaluate_2068(&self, ctx: &EvaluationContext) -> ConditionResult {
1316 let nav = match ctx.navigator() {
1317 Some(n) => n,
1318 None => return ConditionResult::Unknown,
1319 };
1320 let sg27_count = nav.group_instance_count(&["SG27"]);
1321 if sg27_count == 0 {
1322 return ConditionResult::Unknown;
1323 }
1324 for i in 0..sg27_count {
1325 let sg31_count = nav.child_group_instance_count(&["SG27"], i, "SG31");
1326 if sg31_count != 1 {
1327 return ConditionResult::False;
1328 }
1329 }
1330 ConditionResult::True
1331 }
1332
1333 /// [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.
1334 // 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)
1335 fn evaluate_2069(&self, ctx: &EvaluationContext) -> ConditionResult {
1336 // Pro SG27 LIN: SG31 PRI must appear exactly once with H87 (Stück) in DE6411
1337 let pri_count = ctx.count_in_group("PRI", &["SG27", "SG31"]);
1338 if pri_count != 1 {
1339 return ConditionResult::False;
1340 }
1341 // DE6411 is the 5th component of C509 = elements[0][4]
1342 let segs = ctx.find_segments("PRI");
1343 match segs.first() {
1344 Some(pri) => {
1345 let unit = pri
1346 .elements
1347 .first()
1348 .and_then(|e| e.get(4))
1349 .map(|s| s.as_str());
1350 ConditionResult::from(unit == Some("H87"))
1351 }
1352 None => ConditionResult::False, // segment absent → condition not applicable
1353 }
1354 }
1355
1356 /// [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...
1357 // 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)
1358 fn evaluate_2070(&self, ctx: &EvaluationContext) -> ConditionResult {
1359 // Pro SG27 LIN: SG31 PRI must appear exactly twice — one with H87 (Stück), one with DAY (Tag) in DE6411
1360 let pri_count = ctx.count_in_group("PRI", &["SG27", "SG31"]);
1361 if pri_count != 2 {
1362 return ConditionResult::False;
1363 }
1364 // DE6411 is elements[0][4] in standard EDIFACT C509
1365 let segs = ctx.find_segments("PRI");
1366 let has_h87 = segs.iter().any(|pri| {
1367 pri.elements
1368 .first()
1369 .and_then(|e| e.get(4))
1370 .is_some_and(|v| v == "H87")
1371 });
1372 let has_day = segs.iter().any(|pri| {
1373 pri.elements
1374 .first()
1375 .and_then(|e| e.get(4))
1376 .is_some_and(|v| v == "DAY")
1377 });
1378 ConditionResult::from(has_h87 && has_day)
1379 }
1380
1381 /// [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...
1382 fn evaluate_2071(&self, _ctx: &EvaluationContext) -> ConditionResult {
1383 // Hinweis: Cardinality rule — SG31 PRI (Preisangabe) is given up to three times,
1384 // as often as PIA+Z02 (Artikel-ID) appears in the same SG27 LIN (Erforderliches Messprodukt
1385 // für Werte nach Typ2 aus Backend). Informational cardinality annotation, always applies.
1386 ConditionResult::True
1387 }
1388
1389 /// [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...
1390 fn evaluate_2072(&self, _ctx: &EvaluationContext) -> ConditionResult {
1391 // Hinweis: Cardinality rule — SG31 PRI (Preisangabe) is given up to three times,
1392 // as often as PIA+Z02 (Artikel-ID) appears in the same SG27 LIN (Erforderliches Produkt
1393 // Konfigurationserlaubnis für Werte nach Typ 2 aus SMGW). Informational cardinality annotation, always applies.
1394 ConditionResult::True
1395 }
1396
1397 /// [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...
1398 fn evaluate_2073(&self, _ctx: &EvaluationContext) -> ConditionResult {
1399 // Hinweis: Cardinality rule — PIA (OBIS-Kennzahl für Werte nach Typ 2 Backend) must appear
1400 // at least once. It can appear up to 23 times if the other PIA segments in the same SG27 LIN
1401 // appear at most once each. The maximum total number of PIA segments per SG27 LIN is 25.
1402 // Informational cardinality annotation, always applies.
1403 ConditionResult::True
1404 }
1405}