automapper_validation/generated/fv2510/invoic_conditions_fv2510.rs
1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2510/INVOIC_AHB_1_0_Fehlerkorrektur_20250623.xml
4// Generated: 2026-03-12T11:09:04Z
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 INVOIC FV2510.
17pub struct InvoicConditionEvaluatorFV2510 {
18 // External condition IDs that require runtime context.
19 external_conditions: std::collections::HashSet<u32>,
20}
21
22impl Default for InvoicConditionEvaluatorFV2510 {
23 fn default() -> Self {
24 let mut external_conditions = std::collections::HashSet::new();
25 external_conditions.insert(1);
26 external_conditions.insert(2);
27 external_conditions.insert(3);
28 external_conditions.insert(7);
29 external_conditions.insert(8);
30 external_conditions.insert(11);
31 external_conditions.insert(13);
32 external_conditions.insert(15);
33 external_conditions.insert(16);
34 external_conditions.insert(24);
35 external_conditions.insert(25);
36 external_conditions.insert(28);
37 external_conditions.insert(30);
38 external_conditions.insert(31);
39 external_conditions.insert(34);
40 external_conditions.insert(37);
41 external_conditions.insert(40);
42 external_conditions.insert(45);
43 external_conditions.insert(67);
44 external_conditions.insert(68);
45 external_conditions.insert(69);
46 external_conditions.insert(72);
47 external_conditions.insert(76);
48 external_conditions.insert(492);
49 external_conditions.insert(493);
50 Self {
51 external_conditions,
52 }
53 }
54}
55
56impl ConditionEvaluator for InvoicConditionEvaluatorFV2510 {
57 fn message_type(&self) -> &str {
58 "INVOIC"
59 }
60
61 fn format_version(&self) -> &str {
62 "FV2510"
63 }
64
65 fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
66 match condition {
67 1 => self.evaluate_1(ctx),
68 2 => self.evaluate_2(ctx),
69 3 => self.evaluate_3(ctx),
70 4 => self.evaluate_4(ctx),
71 5 => self.evaluate_5(ctx),
72 6 => self.evaluate_6(ctx),
73 7 => self.evaluate_7(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 14 => self.evaluate_14(ctx),
81 15 => self.evaluate_15(ctx),
82 16 => self.evaluate_16(ctx),
83 17 => self.evaluate_17(ctx),
84 18 => self.evaluate_18(ctx),
85 19 => self.evaluate_19(ctx),
86 20 => self.evaluate_20(ctx),
87 21 => self.evaluate_21(ctx),
88 22 => self.evaluate_22(ctx),
89 23 => self.evaluate_23(ctx),
90 24 => self.evaluate_24(ctx),
91 25 => self.evaluate_25(ctx),
92 26 => self.evaluate_26(ctx),
93 27 => self.evaluate_27(ctx),
94 28 => self.evaluate_28(ctx),
95 29 => self.evaluate_29(ctx),
96 30 => self.evaluate_30(ctx),
97 31 => self.evaluate_31(ctx),
98 32 => self.evaluate_32(ctx),
99 34 => self.evaluate_34(ctx),
100 36 => self.evaluate_36(ctx),
101 37 => self.evaluate_37(ctx),
102 40 => self.evaluate_40(ctx),
103 41 => self.evaluate_41(ctx),
104 42 => self.evaluate_42(ctx),
105 44 => self.evaluate_44(ctx),
106 45 => self.evaluate_45(ctx),
107 47 => self.evaluate_47(ctx),
108 48 => self.evaluate_48(ctx),
109 49 => self.evaluate_49(ctx),
110 50 => self.evaluate_50(ctx),
111 51 => self.evaluate_51(ctx),
112 53 => self.evaluate_53(ctx),
113 54 => self.evaluate_54(ctx),
114 55 => self.evaluate_55(ctx),
115 56 => self.evaluate_56(ctx),
116 57 => self.evaluate_57(ctx),
117 58 => self.evaluate_58(ctx),
118 59 => self.evaluate_59(ctx),
119 60 => self.evaluate_60(ctx),
120 61 => self.evaluate_61(ctx),
121 64 => self.evaluate_64(ctx),
122 65 => self.evaluate_65(ctx),
123 66 => self.evaluate_66(ctx),
124 67 => self.evaluate_67(ctx),
125 68 => self.evaluate_68(ctx),
126 69 => self.evaluate_69(ctx),
127 70 => self.evaluate_70(ctx),
128 71 => self.evaluate_71(ctx),
129 72 => self.evaluate_72(ctx),
130 73 => self.evaluate_73(ctx),
131 74 => self.evaluate_74(ctx),
132 75 => self.evaluate_75(ctx),
133 76 => self.evaluate_76(ctx),
134 77 => self.evaluate_77(ctx),
135 78 => self.evaluate_78(ctx),
136 79 => self.evaluate_79(ctx),
137 80 => self.evaluate_80(ctx),
138 81 => self.evaluate_81(ctx),
139 82 => self.evaluate_82(ctx),
140 83 => self.evaluate_83(ctx),
141 84 => self.evaluate_84(ctx),
142 85 => self.evaluate_85(ctx),
143 86 => self.evaluate_86(ctx),
144 87 => self.evaluate_87(ctx),
145 88 => self.evaluate_88(ctx),
146 490 => self.evaluate_490(ctx),
147 491 => self.evaluate_491(ctx),
148 492 => self.evaluate_492(ctx),
149 493 => self.evaluate_493(ctx),
150 501 => self.evaluate_501(ctx),
151 502 => self.evaluate_502(ctx),
152 503 => self.evaluate_503(ctx),
153 504 => self.evaluate_504(ctx),
154 507 => self.evaluate_507(ctx),
155 508 => self.evaluate_508(ctx),
156 509 => self.evaluate_509(ctx),
157 510 => self.evaluate_510(ctx),
158 512 => self.evaluate_512(ctx),
159 513 => self.evaluate_513(ctx),
160 514 => self.evaluate_514(ctx),
161 515 => self.evaluate_515(ctx),
162 516 => self.evaluate_516(ctx),
163 517 => self.evaluate_517(ctx),
164 518 => self.evaluate_518(ctx),
165 519 => self.evaluate_519(ctx),
166 520 => self.evaluate_520(ctx),
167 521 => self.evaluate_521(ctx),
168 522 => self.evaluate_522(ctx),
169 523 => self.evaluate_523(ctx),
170 524 => self.evaluate_524(ctx),
171 525 => self.evaluate_525(ctx),
172 526 => self.evaluate_526(ctx),
173 902 => self.evaluate_902(ctx),
174 906 => self.evaluate_906(ctx),
175 908 => self.evaluate_908(ctx),
176 910 => self.evaluate_910(ctx),
177 911 => self.evaluate_911(ctx),
178 912 => self.evaluate_912(ctx),
179 914 => self.evaluate_914(ctx),
180 927 => self.evaluate_927(ctx),
181 930 => self.evaluate_930(ctx),
182 931 => self.evaluate_931(ctx),
183 932 => self.evaluate_932(ctx),
184 933 => self.evaluate_933(ctx),
185 934 => self.evaluate_934(ctx),
186 935 => self.evaluate_935(ctx),
187 937 => self.evaluate_937(ctx),
188 939 => self.evaluate_939(ctx),
189 940 => self.evaluate_940(ctx),
190 946 => self.evaluate_946(ctx),
191 950 => self.evaluate_950(ctx),
192 951 => self.evaluate_951(ctx),
193 960 => self.evaluate_960(ctx),
194 961 => self.evaluate_961(ctx),
195 _ => ConditionResult::Unknown,
196 }
197 }
198
199 fn is_external(&self, condition: u32) -> bool {
200 self.external_conditions.contains(&condition)
201 }
202 fn is_known(&self, condition: u32) -> bool {
203 matches!(
204 condition,
205 1 | 2
206 | 3
207 | 4
208 | 5
209 | 6
210 | 7
211 | 8
212 | 9
213 | 10
214 | 11
215 | 12
216 | 13
217 | 14
218 | 15
219 | 16
220 | 17
221 | 18
222 | 19
223 | 20
224 | 21
225 | 22
226 | 23
227 | 24
228 | 25
229 | 26
230 | 27
231 | 28
232 | 29
233 | 30
234 | 31
235 | 32
236 | 34
237 | 36
238 | 37
239 | 40
240 | 41
241 | 42
242 | 44
243 | 45
244 | 47
245 | 48
246 | 49
247 | 50
248 | 51
249 | 53
250 | 54
251 | 55
252 | 56
253 | 57
254 | 58
255 | 59
256 | 60
257 | 61
258 | 64
259 | 65
260 | 66
261 | 67
262 | 68
263 | 69
264 | 70
265 | 71
266 | 72
267 | 73
268 | 74
269 | 75
270 | 76
271 | 77
272 | 78
273 | 79
274 | 80
275 | 81
276 | 82
277 | 83
278 | 84
279 | 85
280 | 86
281 | 87
282 | 88
283 | 490
284 | 491
285 | 492
286 | 493
287 | 501
288 | 502
289 | 503
290 | 504
291 | 507
292 | 508
293 | 509
294 | 510
295 | 512
296 | 513
297 | 514
298 | 515
299 | 516
300 | 517
301 | 518
302 | 519
303 | 520
304 | 521
305 | 522
306 | 523
307 | 524
308 | 525
309 | 526
310 | 902
311 | 906
312 | 908
313 | 910
314 | 911
315 | 912
316 | 914
317 | 927
318 | 930
319 | 931
320 | 932
321 | 933
322 | 934
323 | 935
324 | 937
325 | 939
326 | 940
327 | 946
328 | 950
329 | 951
330 | 960
331 | 961
332 )
333 }
334}
335
336impl InvoicConditionEvaluatorFV2510 {
337 /// [1] Wenn in zu stornierender Rechnung gefüllt
338 /// EXTERNAL: Requires context from outside the message.
339 fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
340 ctx.external.evaluate("original_invoice_field_filled")
341 }
342
343 /// [2] Wenn es sich um eine Nutzungsüberlassung (Pacht) eines Gerätes handelt
344 /// EXTERNAL: Requires context from outside the message.
345 fn evaluate_2(&self, ctx: &EvaluationContext) -> ConditionResult {
346 ctx.external.evaluate("device_is_leased")
347 }
348
349 /// [3] Wenn es sich um einen Kauf eines Gerätes handelt
350 /// EXTERNAL: Requires context from outside the message.
351 fn evaluate_3(&self, ctx: &EvaluationContext) -> ConditionResult {
352 ctx.external.evaluate("device_is_purchased")
353 }
354
355 /// [7] Sofern keine Großkundenpostleitzahl verwendet wird
356 /// EXTERNAL: Requires context from outside the message.
357 fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
358 ctx.external.evaluate("no_grosskundenpzl_used")
359 }
360
361 /// [8] Bei zeitabhängigen Preisen
362 /// EXTERNAL: Requires context from outside the message.
363 fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
364 ctx.external.evaluate("time_dependent_prices")
365 }
366
367 /// [24] Wert muss mindestens 10 WT nach Wert aus DTM+137 DE2380 liegen
368 /// EXTERNAL: Requires context from outside the message.
369 fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
370 ctx.external
371 .evaluate("value_at_least_10_werktage_after_nachrichtendatum")
372 }
373
374 /// [25] Wert darf maximal 10 WT nach Wert aus DTM+137 DE2380 liegen
375 /// EXTERNAL: Requires context from outside the message.
376 fn evaluate_25(&self, ctx: &EvaluationContext) -> ConditionResult {
377 ctx.external
378 .evaluate("value_at_most_10_werktage_after_nachrichtendatum")
379 }
380
381 /// [67] Es sind nur die Artikelnummern erlaubt, die im Kapitel 2 „Codeliste der Artikelnummer“ in der Codeliste der Artikelnummern und Artikel-ID ein „X“ für den entsprechenden Prüfidentifikator ...
382 /// EXTERNAL: Requires context from outside the message.
383 fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
384 ctx.external
385 .evaluate("invoic_artikelnummer_allowed_for_pid")
386 }
387
388 /// [68] Es sind nur die Artikel-ID erlaubt, die im Kapitel 3 „Codeliste der Gruppenartikel-ID und Artikel-ID“ in der Codeliste der Artikelnummern und Artikel-ID ein „X“ in der Spalte „INVOIC Cod...
389 /// EXTERNAL: Requires context from outside the message.
390 fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
391 ctx.external.evaluate("invoic_artikel_id_x_codeverwendung")
392 }
393
394 /// [69] Es sind nur die Artikel-ID erlaubt, die im Kapitel 3 „Codeliste der Gruppenartikel-ID und Artikel-ID“ in der Codeliste der Artikelnummern und Artikel-ID ein „SOR“ in der Spalte „INVOIC C...
395 /// EXTERNAL: Requires context from outside the message.
396 fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
397 ctx.external
398 .evaluate("invoic_artikel_id_sor_codeverwendung")
399 }
400
401 /// [83] Wenn GEI+Z10 nicht vorhanden
402 fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
403 ctx.lacks_qualifier("GEI", 0, "Z10")
404 }
405
406 /// [87] Wenn IMD++TEC vorhanden
407 fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
408 ctx.has_qualifier("IMD", 1, "TEC")
409 }
410
411 /// [4] Wenn Steuerschuldnerschaft des Leistungsempfängers vorliegt
412 // REVIEW: "Wenn Steuerschuldnerschaft des Leistungsempfängers vorliegt" is the reverse charge mechanism. In UN/EDIFACT INVOIC, TAX category code "AE" (VAT Reverse Charge) signals this. The TAX segment composite C241 component 3 holds the tax category code. Medium confidence because element indices are derived from standard UN/EDIFACT TAX structure without a specific MIG segment structure reference to confirm the exact index for this INVOIC variant. (medium confidence)
413 fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
414 // Steuerschuldnerschaft des Leistungsempfängers = reverse charge (VAT category AE)
415 // In EDIFACT INVOIC the TAX segment carries duty/tax type category code.
416 // UN/EDIFACT code "AE" = VAT Reverse Charge (Steuerschuldnerschaft des Leistungsempfängers).
417 // TAX+7+VAT+++:::AE — elements[4][3] holds the tax category code.
418 let tax_segs = ctx.find_segments("TAX");
419 for seg in &tax_segs {
420 // elements[4] is the duty/tax account detail composite (C241 in UN/EDIFACT TAX)
421 // component 3 (index 3) is the duty/tax rate category code
422 if let Some(code) = seg.elements.get(4).and_then(|e| e.get(3)) {
423 if code == "AE" {
424 return ConditionResult::True;
425 }
426 }
427 }
428 ConditionResult::False
429 }
430
431 /// [5] Wenn NAD+MR DE3207 <> „DE“
432 fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
433 let nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
434 match nads.first() {
435 Some(nad) => {
436 match nad
437 .elements
438 .get(8)
439 .and_then(|e| e.first())
440 .map(|s| s.as_str())
441 {
442 Some(country) => ConditionResult::from(country != "DE"),
443 None => ConditionResult::False, // segment absent → condition not applicable
444 }
445 }
446 None => ConditionResult::False, // segment absent → condition not applicable
447 }
448 }
449
450 /// [6] Wenn NAD+MR DE3207 = „DE“
451 fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
452 let nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
453 match nads.first() {
454 Some(nad) => {
455 match nad
456 .elements
457 .get(8)
458 .and_then(|e| e.first())
459 .map(|s| s.as_str())
460 {
461 Some(country) => ConditionResult::from(country == "DE"),
462 None => ConditionResult::False, // segment absent → condition not applicable
463 }
464 }
465 None => ConditionResult::False, // segment absent → condition not applicable
466 }
467 }
468
469 /// [9] Wenn SG26 DTM+203 nicht vorhanden
470 // REVIEW: Checks whether any SG26 instance lacks DTM+203 (Ausführungsdatum). Uses group navigator when available, falling back to message-wide absence check. DTM qualifier at elements[0][0]. (medium confidence)
471 fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
472 // DTM+203 (Ausführungsdatum) absence in the current SG26 context.
473 // Falls back to message-wide check when no group navigator available.
474 let nav = match ctx.navigator() {
475 Some(n) => n,
476 None => return ctx.lacks_qualifier("DTM", 0, "203"),
477 };
478 let sg26_count = nav.group_instance_count(&["SG26"]);
479 for i in 0..sg26_count {
480 let dtms = nav.find_segments_in_group("DTM", &["SG26"], i);
481 let has_203 = dtms.iter().any(|s| {
482 s.elements
483 .first()
484 .and_then(|e| e.first())
485 .is_some_and(|v| v == "203")
486 });
487 if !has_203 {
488 return ConditionResult::True;
489 }
490 }
491 if sg26_count == 0 {
492 return ctx.lacks_qualifier("DTM", 0, "203");
493 }
494 ConditionResult::False
495 }
496
497 /// [10] Wenn SG26 DTM+155/156 nicht vorhanden
498 // REVIEW: Both DTM+155 (Abrechnungszeitraum Beginn) and DTM+156 (Ende) must be absent in the SG26. Uses group navigator; both must be absent simultaneously for the condition to be True. (medium confidence)
499 fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
500 // DTM+155 (Abrechnungszeitraum Beginn) and DTM+156 (Ende) both absent in SG26.
501 // Falls back to message-wide check when no group navigator available.
502 let nav = match ctx.navigator() {
503 Some(n) => n,
504 None => {
505 let no_155 = ctx.lacks_qualifier("DTM", 0, "155");
506 let no_156 = ctx.lacks_qualifier("DTM", 0, "156");
507 return match (no_155, no_156) {
508 (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
509 (ConditionResult::False, _) | (_, ConditionResult::False) => {
510 ConditionResult::False
511 }
512 _ => ConditionResult::Unknown,
513 };
514 }
515 };
516 let sg26_count = nav.group_instance_count(&["SG26"]);
517 for i in 0..sg26_count {
518 let dtms = nav.find_segments_in_group("DTM", &["SG26"], i);
519 let has_155 = dtms.iter().any(|s| {
520 s.elements
521 .first()
522 .and_then(|e| e.first())
523 .is_some_and(|v| v == "155")
524 });
525 let has_156 = dtms.iter().any(|s| {
526 s.elements
527 .first()
528 .and_then(|e| e.first())
529 .is_some_and(|v| v == "156")
530 });
531 if !has_155 && !has_156 {
532 return ConditionResult::True;
533 }
534 }
535 if sg26_count == 0 {
536 let no_155 = ctx.lacks_qualifier("DTM", 0, "155");
537 let no_156 = ctx.lacks_qualifier("DTM", 0, "156");
538 return match (no_155, no_156) {
539 (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
540 (ConditionResult::False, _) | (_, ConditionResult::False) => ConditionResult::False,
541 _ => ConditionResult::Unknown,
542 };
543 }
544 ConditionResult::False
545 }
546
547 /// [11] Wenn Abschlag anfällt
548 /// EXTERNAL: Requires context from outside the message.
549 // REVIEW: Whether a deposit/advance payment (Abschlag) applies is business context that cannot be determined from the EDIFACT message structure alone — it depends on the contract type and billing arrangement. (medium confidence)
550 fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
551 ctx.external.evaluate("advance_payment_applies")
552 }
553
554 /// [12] Wenn SG26 QTY+136 vorhanden
555 fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
556 ctx.has_qualifier("QTY", 0, "136")
557 }
558
559 /// [13] Wenn vorausbezahlter Betrag vorliegt
560 /// EXTERNAL: Requires context from outside the message.
561 // REVIEW: Whether a prepaid amount (vorausbezahlter Betrag) is present is billing business context that cannot be derived solely from the EDIFACT segments in the message. (medium confidence)
562 fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
563 ctx.external.evaluate("prepaid_amount_present")
564 }
565
566 /// [14] Wenn in selben SG26 LIN DE7140 = "9990001000748" (Mehrmenge)
567 fn evaluate_14(&self, ctx: &EvaluationContext) -> ConditionResult {
568 // Check if any SG26 LIN has DE7140 (Produkt-/Leistungsnummer) = "9990001000748" (Mehrmenge).
569 // LIN is the entry segment of SG26; elements[2][0] = DE7140.
570 let lins = ctx.find_segments("LIN");
571 ConditionResult::from(lins.iter().any(|s| {
572 s.elements
573 .get(2)
574 .and_then(|e| e.first())
575 .is_some_and(|v| v == "9990001000748")
576 }))
577 }
578
579 /// [15] Wenn eine Bilanzierung erfolgt ist
580 /// EXTERNAL: Requires context from outside the message.
581 // REVIEW: Whether a balancing (Bilanzierung) has occurred is operational/business context external to the EDIFACT message itself. (medium confidence)
582 fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
583 ctx.external.evaluate("balancing_performed")
584 }
585
586 /// [16] Wenn eine Netznutzung erfolgt ist
587 /// EXTERNAL: Requires context from outside the message.
588 // REVIEW: Whether network usage (Netznutzung) has occurred is business context that cannot be determined solely from the EDIFACT message content. (medium confidence)
589 fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
590 ctx.external.evaluate("network_usage_performed")
591 }
592
593 /// [17] Wenn DTM+Z11 vorhanden
594 fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
595 ctx.has_qualifier("DTM", 0, "Z11")
596 }
597
598 /// [18] Wenn IMD++WIM nicht vorhanden
599 fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
600 ctx.lacks_qualifier("IMD", 1, "WIM")
601 }
602
603 /// [19] Wenn IMD++WIM vorhanden
604 fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
605 ctx.has_qualifier("IMD", 1, "WIM")
606 }
607
608 /// [20] Wenn fälliger Betrag (SG50 MOA+9) ≥ 0
609 // REVIEW: MOA+9 is fälliger Betrag (due amount): elements[0][0]='9', elements[0][1]=amount. Parses the numeric value and checks >= 0. (medium confidence)
610 fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
611 let moas = ctx.find_segments_with_qualifier("MOA", 0, "9");
612 match moas.first() {
613 Some(moa) => match moa.elements.first().and_then(|e| e.get(1)) {
614 Some(val) => match val.parse::<f64>() {
615 Ok(n) => ConditionResult::from(n >= 0.0),
616 Err(_) => ConditionResult::Unknown,
617 },
618 None => ConditionResult::False, // segment absent → condition not applicable
619 },
620 None => ConditionResult::False, // segment absent → condition not applicable
621 }
622 }
623
624 /// [21] Wenn fälliger Betrag (SG50 MOA+9) < 0
625 // REVIEW: MOA+9 is fälliger Betrag. Parses numeric value and checks < 0. Inverse of condition 20. (medium confidence)
626 fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
627 let moas = ctx.find_segments_with_qualifier("MOA", 0, "9");
628 match moas.first() {
629 Some(moa) => match moa.elements.first().and_then(|e| e.get(1)) {
630 Some(val) => match val.parse::<f64>() {
631 Ok(n) => ConditionResult::from(n < 0.0),
632 Err(_) => ConditionResult::Unknown,
633 },
634 None => ConditionResult::False, // segment absent → condition not applicable
635 },
636 None => ConditionResult::False, // segment absent → condition not applicable
637 }
638 }
639
640 /// [22] Wenn vorhanden
641 fn evaluate_22(&self, _ctx: &EvaluationContext) -> ConditionResult {
642 // Wenn vorhanden — self-referential presence condition, always True when evaluated
643 ConditionResult::True
644 }
645
646 /// [23] Wenn im selben NAD DE3124 nicht vorhanden
647 // REVIEW: DE3124 resides in C058 at elements[2][0] for all NAD variants. 'Im selben NAD' means within the same NAD instance; evaluated message-wide as: any NAD where this component is absent or empty. (medium confidence)
648 fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
649 let nads = ctx.find_segments("NAD");
650 if nads.is_empty() {
651 return ConditionResult::Unknown;
652 }
653 // True if any NAD has DE3124 (elements[2][0] from C058) absent or empty
654 ConditionResult::from(nads.iter().any(|nad| {
655 nad.elements
656 .get(2)
657 .and_then(|e| e.first())
658 .map_or(true, |v| v.is_empty())
659 }))
660 }
661
662 /// [26] Wenn SG39 ALC+A+:Z04 vorhanden
663 fn evaluate_26(&self, ctx: &EvaluationContext) -> ConditionResult {
664 let alcs = ctx.find_segments_with_qualifier("ALC", 0, "A");
665 ConditionResult::from(alcs.iter().any(|alc| {
666 alc.elements
667 .get(1)
668 .and_then(|e| e.get(1))
669 .map_or(false, |v| v == "Z04")
670 }))
671 }
672
673 /// [27] Wenn SG39 ALC+C vorhanden
674 fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
675 ctx.has_qualifier("ALC", 0, "C")
676 }
677
678 /// [28] Wenn Zuschlag anfällt
679 /// EXTERNAL: Requires context from outside the message.
680 // REVIEW: 'Wenn Zuschlag anfällt' (if a surcharge applies) is a business-context determination that cannot be derived from EDIFACT segment content alone. It depends on contract terms or tariff configuration held outside the message. (medium confidence)
681 fn evaluate_28(&self, ctx: &EvaluationContext) -> ConditionResult {
682 ctx.external.evaluate("surcharge_applies")
683 }
684
685 /// [29] [Wenn DTM+155 (Abrechnungszeitraum Beginn) nicht größer 31.12.2015
686 // REVIEW: DTM+155 is Abrechnungszeitraum Beginn with format 303 (starts YYYYMMDD). 'nicht größer 31.12.2015' means <= 31.12.2015. Lexicographic comparison of 8-char ISO prefix is correct for this date format. (medium confidence)
687 fn evaluate_29(&self, ctx: &EvaluationContext) -> ConditionResult {
688 let dtms = ctx.find_segments_with_qualifier("DTM", 0, "155");
689 match dtms.first() {
690 Some(dtm) => {
691 match dtm.elements.first().and_then(|e| e.get(1)) {
692 Some(val) if val.len() >= 8 => {
693 // Format 303: CCYYMMDDHHMMSSZZZ — compare YYYYMMDD prefix
694 let date_prefix = &val[..8];
695 ConditionResult::from(date_prefix <= "20151231")
696 }
697 _ => ConditionResult::Unknown,
698 }
699 }
700 None => ConditionResult::False, // segment absent → condition not applicable
701 }
702 }
703
704 /// [30] Wenn MP-ID in NAD+MR nicht in der Rolle MGV oder KN
705 /// EXTERNAL: Requires context from outside the message.
706 // REVIEW: 'Wenn MP-ID in NAD+MR nicht in der Rolle MGV oder KN' requires knowledge of which market-participant IDs correspond to the MGV (Messstellenbetreiber Gasvertrieb) or KN (Kunde/Netz) roles. Role assignment is maintained in an external registry and cannot be determined from the NAD segment alone. (medium confidence)
707 fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
708 ctx.external.evaluate("recipient_not_mgv_or_kn")
709 }
710
711 /// [31] Wenn MP-ID in NAD+MR in der Rolle MGV
712 /// EXTERNAL: Requires context from outside the message.
713 fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
714 ctx.external.evaluate("recipient_is_mgv")
715 }
716
717 /// [32] Wenn SG39 ALC+A+:Z01 (Gemeinderabatt) vorhanden
718 fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
719 ctx.has_qualified_value("ALC", 0, "A", 1, 1, &["Z01"])
720 }
721
722 /// [34] Wenn in Ursprungsrechnung vorhanden
723 /// EXTERNAL: Requires context from outside the message.
724 // REVIEW: "Wenn in Ursprungsrechnung vorhanden" means the condition is met when the corresponding value was present in the referenced original invoice. That original document is not available inside the current EDIFACT message, so this is an external business-context check. (medium confidence)
725 fn evaluate_34(&self, ctx: &EvaluationContext) -> ConditionResult {
726 ctx.external.evaluate("in_original_invoice")
727 }
728
729 /// [36] Wenn DTM+156 (Abrechnungszeitraum Ende) ≥ 01.12.2019
730 // REVIEW: DTM+156 (Abrechnungszeitraum Ende) uses format 303 (CCYYMMDDHHMM), so lexicographic string comparison is correct. German date 01.12.2019 = December 1, 2019 = "201912010000". (medium confidence)
731 fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
732 let dtm_segs = ctx.find_segments_with_qualifier("DTM", 0, "156");
733 match dtm_segs.first() {
734 Some(dtm) => {
735 match dtm
736 .elements
737 .first()
738 .and_then(|e| e.get(1))
739 .map(|s| s.as_str())
740 {
741 Some(value) if !value.is_empty() => {
742 // Format 303 = CCYYMMDDHHMM; lexicographic comparison works for zero-padded timestamps.
743 // 01.12.2019 (DD.MM.YYYY) = December 1, 2019 = "201912010000"
744 ConditionResult::from(value >= "201912010000")
745 }
746 _ => ConditionResult::Unknown,
747 }
748 }
749 None => ConditionResult::False, // segment absent → condition not applicable
750 }
751 }
752
753 /// [37] Wenn Lieferschein zuvor ausgetauscht wurde
754 /// EXTERNAL: Requires context from outside the message.
755 // REVIEW: "Wenn Lieferschein zuvor ausgetauscht wurde" depends on whether a delivery note (Lieferschein) was exchanged in a prior business transaction. This cannot be determined from the current EDIFACT message alone; it requires external business context. (medium confidence)
756 fn evaluate_37(&self, ctx: &EvaluationContext) -> ConditionResult {
757 ctx.external.evaluate("delivery_note_previously_exchanged")
758 }
759
760 /// [40] Es sind nur die Artikelnummern erlaubt, die in der Codeliste der Artikelnummern und Artikel-ID mit dem entsprechenden Prüfidentifikator versehen sind oder Artikel-ID aus der Codeliste der Artikeln...
761 /// EXTERNAL: Requires context from outside the message.
762 // REVIEW: Condition requires validating that the article number/ID appears in the external "Codeliste der Artikelnummern und Artikel-ID" for the given Prüfidentifikator. Code-list membership cannot be verified from the EDIFACT message structure alone; it is an external reference-data check. (medium confidence)
763 fn evaluate_40(&self, ctx: &EvaluationContext) -> ConditionResult {
764 ctx.external.evaluate("artikel_id_in_codeliste")
765 }
766
767 /// [41] Wenn IMD++SOR vorhanden
768 fn evaluate_41(&self, ctx: &EvaluationContext) -> ConditionResult {
769 let imds = ctx.find_segments("IMD");
770 ConditionResult::from(imds.iter().any(|s| {
771 s.elements
772 .get(1)
773 .and_then(|e| e.first())
774 .is_some_and(|v| v == "SOR")
775 }))
776 }
777
778 /// [42] Wenn DTM+155 (Abrechnungszeitraum Beginn) ≥ 1.1.2023 0:00 gesetzlicher deutscher Zeit
779 // REVIEW: DTM+155 (Abrechnungszeitraum Beginn) uses format 303 (CCYYMMDDHHMM). January 1, 2023 at 00:00 = "202301010000". Lexicographic comparison is correct for zero-padded CCYYMMDDHHMM. (medium confidence)
780 fn evaluate_42(&self, ctx: &EvaluationContext) -> ConditionResult {
781 let dtm_segs = ctx.find_segments_with_qualifier("DTM", 0, "155");
782 match dtm_segs.first() {
783 Some(dtm) => {
784 match dtm
785 .elements
786 .first()
787 .and_then(|e| e.get(1))
788 .map(|s| s.as_str())
789 {
790 Some(value) if !value.is_empty() => {
791 // Format 303 = CCYYMMDDHHMM; 1.1.2023 0:00 gesetzlicher Zeit = "202301010000"
792 ConditionResult::from(value >= "202301010000")
793 }
794 _ => ConditionResult::Unknown,
795 }
796 }
797 None => ConditionResult::False, // segment absent → condition not applicable
798 }
799 }
800
801 /// [44] Wenn IMD++ABS nicht vorhanden
802 fn evaluate_44(&self, ctx: &EvaluationContext) -> ConditionResult {
803 let imds = ctx.find_segments("IMD");
804 ConditionResult::from(!imds.iter().any(|s| {
805 s.elements
806 .get(1)
807 .and_then(|e| e.first())
808 .is_some_and(|v| v == "ABS")
809 }))
810 }
811
812 /// [45] Nur MP-ID aus Sparte Strom
813 /// EXTERNAL: Requires context from outside the message.
814 // REVIEW: "Nur MP-ID aus Sparte Strom" means only market-participant IDs that belong to the electricity sector are allowed. Determining which IDs are registered for Sparte Strom requires an external code list or market master data lookup and cannot be inferred from the EDIFACT message content. (medium confidence)
815 fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
816 ctx.external.evaluate("mp_id_sparte_strom")
817 }
818
819 /// [47] Wenn IMD++Z43 vorhanden
820 fn evaluate_47(&self, ctx: &EvaluationContext) -> ConditionResult {
821 let imds = ctx.find_segments("IMD");
822 ConditionResult::from(imds.iter().any(|s| {
823 s.elements
824 .get(1)
825 .and_then(|e| e.first())
826 .is_some_and(|v| v == "Z43")
827 }))
828 }
829
830 /// [48] Wenn IMD++Z44 vorhanden
831 fn evaluate_48(&self, ctx: &EvaluationContext) -> ConditionResult {
832 let imds = ctx.find_segments("IMD");
833 ConditionResult::from(imds.iter().any(|s| {
834 s.elements
835 .get(1)
836 .and_then(|e| e.first())
837 .is_some_and(|v| v == "Z44")
838 }))
839 }
840
841 /// [49] Wenn IMD++Z43 und IMD+Z44 nicht vorhanden
842 fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
843 let imds = ctx.find_segments("IMD");
844 let has_z43 = imds.iter().any(|s| {
845 s.elements
846 .get(1)
847 .and_then(|e| e.first())
848 .is_some_and(|v| v == "Z43")
849 });
850 let has_z44 = imds.iter().any(|s| {
851 s.elements
852 .get(1)
853 .and_then(|e| e.first())
854 .is_some_and(|v| v == "Z44")
855 });
856 ConditionResult::from(!has_z43 && !has_z44)
857 }
858
859 /// [50] Wenn IMD++SOR nicht vorhanden
860 fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
861 let imds = ctx.find_segments("IMD");
862 ConditionResult::from(!imds.iter().any(|s| {
863 s.elements
864 .get(1)
865 .and_then(|e| e.first())
866 .is_some_and(|v| v == "SOR")
867 }))
868 }
869
870 /// [51] Wenn SG26 DTM+156 (Positionsbezogener Abrechnungszeitraum Ende) ≤ 1.1.2023 0:00 gesetzlicher deutscher Zeit
871 // REVIEW: DTM+156 in SG26 (Positionsbezogener Abrechnungszeitraum Ende) uses format 303 (CCYYMMDDHHMM). January 1, 2023 at 00:00 = "202301010000". Without SG26-scoped group navigation, message-wide search is used; qualifier 156 in the SG26 context is distinct from the message-level 156, but without group navigation this is the best available approximation. (medium confidence)
872 fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
873 let dtm_segs = ctx.find_segments_with_qualifier("DTM", 0, "156");
874 match dtm_segs.first() {
875 Some(dtm) => {
876 match dtm
877 .elements
878 .first()
879 .and_then(|e| e.get(1))
880 .map(|s| s.as_str())
881 {
882 Some(value) if !value.is_empty() => {
883 // Format 303 = CCYYMMDDHHMM; 1.1.2023 0:00 = "202301010000"
884 ConditionResult::from(value <= "202301010000")
885 }
886 _ => ConditionResult::Unknown,
887 }
888 }
889 None => ConditionResult::False, // segment absent → condition not applicable
890 }
891 }
892
893 /// [53] Wenn IMD++Z45 vorhanden
894 fn evaluate_53(&self, ctx: &EvaluationContext) -> ConditionResult {
895 ctx.has_qualifier("IMD", 1, "Z45")
896 }
897
898 /// [54] Wenn IMD++ABR/JVR/ZVR vorhanden
899 fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
900 {
901 let segs = ctx.find_segments("IMD");
902 if segs.is_empty() {
903 return ConditionResult::False;
904 }
905 ConditionResult::from(segs.iter().any(|s| {
906 s.elements
907 .get(1)
908 .and_then(|e| e.first())
909 .is_some_and(|v| matches!(v.as_str(), "ABR" | "JVR" | "ZVR"))
910 }))
911 }
912 }
913
914 /// [55] Wenn IMD++ABS vorhanden
915 fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
916 ctx.has_qualifier("IMD", 1, "ABS")
917 }
918
919 /// [56] Wenn DTM+137 (Nachrichtendatum ≥ 1.1.2023 0:00 gesetzlicher deutscher Zeit
920 // REVIEW: DTM+137 Nachrichtendatum >= 2023-01-01. Parses the date value (format 303 = YYYYMMDDHHmm) and compares the first 8 characters against '20230101'. String comparison is valid for ISO date format. (medium confidence)
921 fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
922 {
923 let segs = ctx.find_segments_with_qualifier("DTM", 0, "137");
924 match segs.first() {
925 Some(dtm) => match dtm.elements.first().and_then(|e| e.get(1)) {
926 Some(val) if val.len() >= 8 => {
927 ConditionResult::from(val[..8].cmp("20230101") != std::cmp::Ordering::Less)
928 }
929 _ => ConditionResult::Unknown,
930 },
931 None => ConditionResult::False, // segment absent → condition not applicable
932 }
933 }
934 }
935
936 /// [57] Wenn DTM+156 (Abrechnungszeitraum Ende) ≤ 1.1.2023 0:00 gesetzlicher deutscher Zeit
937 // REVIEW: DTM+156 (Abrechnungszeitraum Ende) <= 2023-01-01 0:00. Compares first 8 chars of date value against '20230101'. Returns True when the end date is on or before Jan 1 2023. (medium confidence)
938 fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
939 {
940 let segs = ctx.find_segments_with_qualifier("DTM", 0, "156");
941 match segs.first() {
942 Some(dtm) => match dtm.elements.first().and_then(|e| e.get(1)) {
943 Some(val) if val.len() >= 8 => ConditionResult::from(
944 val[..8].cmp("20230101") != std::cmp::Ordering::Greater,
945 ),
946 _ => ConditionResult::Unknown,
947 },
948 None => ConditionResult::False, // segment absent → condition not applicable
949 }
950 }
951 }
952
953 /// [58] Wenn in dieser SG52 MOA+113 vorhanden
954 fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
955 ctx.any_group_has_qualifier("MOA", 0, "113", &["SG52"])
956 }
957
958 /// [59] Wenn SG26 DTM+155 (Positionsbezogener Abrechnungszeitraum Beginn) ≥ 1.1.2023 0:00 gesetzlicher deutscher Zeit
959 // REVIEW: SG26 DTM+155 (Positionsbezogener Abrechnungszeitraum Beginn) >= 2023-01-01 0:00. Finds DTM with qualifier 155 and compares first 8 chars of the date value against '20230101'. (medium confidence)
960 fn evaluate_59(&self, ctx: &EvaluationContext) -> ConditionResult {
961 {
962 let segs = ctx.find_segments_with_qualifier("DTM", 0, "155");
963 match segs.first() {
964 Some(dtm) => match dtm.elements.first().and_then(|e| e.get(1)) {
965 Some(val) if val.len() >= 8 => {
966 ConditionResult::from(val[..8].cmp("20230101") != std::cmp::Ordering::Less)
967 }
968 _ => ConditionResult::Unknown,
969 },
970 None => ConditionResult::False, // segment absent → condition not applicable
971 }
972 }
973 }
974
975 /// [60] Wenn DTM+203 DE2379 in demselben Segment mit Wert 303 vorhanden
976 fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
977 ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "303"))
978 }
979
980 /// [61] Wenn DTM+203 DE2379 in demselben Segment mit Wert 102 vorhanden
981 fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
982 ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "102"))
983 }
984
985 /// [64] Wenn DTM+156 (Abrechnungszeitraum Ende) ≤ 1.1.2024 0:00 gesetzlicher deutscher Zeit
986 // REVIEW: DTM+156 (Abrechnungszeitraum Ende) <= 2024-01-01 0:00. Same pattern as condition 57 but with threshold '20240101'. (medium confidence)
987 fn evaluate_64(&self, ctx: &EvaluationContext) -> ConditionResult {
988 {
989 let segs = ctx.find_segments_with_qualifier("DTM", 0, "156");
990 match segs.first() {
991 Some(dtm) => match dtm.elements.first().and_then(|e| e.get(1)) {
992 Some(val) if val.len() >= 8 => ConditionResult::from(
993 val[..8].cmp("20240101") != std::cmp::Ordering::Greater,
994 ),
995 _ => ConditionResult::Unknown,
996 },
997 None => ConditionResult::False, // segment absent → condition not applicable
998 }
999 }
1000 }
1001
1002 /// [65] Wenn SG26 DTM+155 (Positionsbezogener Abrechnungszeitraum Beginn) ≥ 1.1.2024 0:00 gesetzlicher deutscher Zeit
1003 // REVIEW: SG26 DTM+155 (Positionsbezogener Abrechnungszeitraum Beginn) >= 2024-01-01 0:00. Same pattern as condition 59 but with threshold '20240101'. (medium confidence)
1004 fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
1005 {
1006 let segs = ctx.find_segments_with_qualifier("DTM", 0, "155");
1007 match segs.first() {
1008 Some(dtm) => match dtm.elements.first().and_then(|e| e.get(1)) {
1009 Some(val) if val.len() >= 8 => {
1010 ConditionResult::from(val[..8].cmp("20240101") != std::cmp::Ordering::Less)
1011 }
1012 _ => ConditionResult::Unknown,
1013 },
1014 None => ConditionResult::False, // segment absent → condition not applicable
1015 }
1016 }
1017 }
1018
1019 /// [66] Wenn IMD++KON vorhanden
1020 fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
1021 ctx.has_qualifier("IMD", 1, "KON")
1022 }
1023
1024 /// [70] Wenn IMD++Z45 nicht vorhanden
1025 fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
1026 let imd_segments = ctx.find_segments("IMD");
1027 let found = imd_segments.iter().any(|s| {
1028 s.elements
1029 .get(1)
1030 .and_then(|e| e.first())
1031 .is_some_and(|v| v == "Z45")
1032 });
1033 ConditionResult::from(!found)
1034 }
1035
1036 /// [71] Wenn DTM+155 (Abrechnungszeitraum Beginn) ≥ 1.1.2024 0:00 gesetzlicher deutscher Zeit
1037 // REVIEW: DTM+155 means elements[0][0]=="155". Value is at elements[0][1] in 303 format (YYYYMMDDhhmm). String comparison works for chronological ordering. Timezone nuance (German legal time) ignored — medium confidence. (medium confidence)
1038 fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
1039 let dtm_segments = ctx.find_segments_with_qualifier("DTM", 0, "155");
1040 match dtm_segments.first() {
1041 Some(dtm) => {
1042 match dtm
1043 .elements
1044 .first()
1045 .and_then(|e| e.get(1))
1046 .map(|s| s.as_str())
1047 {
1048 Some(value) if !value.is_empty() => {
1049 // 303 format: YYYYMMDDhhmm — lexicographic comparison is chronological for zero-padded strings
1050 ConditionResult::from(value >= "202401010000")
1051 }
1052 _ => ConditionResult::Unknown,
1053 }
1054 }
1055 None => ConditionResult::False, // segment absent → condition not applicable
1056 }
1057 }
1058
1059 /// [72] Wenn MP-ID in NAD+MR in der Rolle LF
1060 /// EXTERNAL: Requires context from outside the message.
1061 // REVIEW: Whether the MP-ID in NAD+MR has the market role LF (Lieferant) cannot be determined from the EDIFACT message alone — it requires a business registry lookup. The NAD+MR segment only provides the party identifier, not the assigned market role. (medium confidence)
1062 fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
1063 ctx.external.evaluate("recipient_is_lf")
1064 }
1065
1066 /// [73] Wenn IMD++MSB vorhanden
1067 fn evaluate_73(&self, ctx: &EvaluationContext) -> ConditionResult {
1068 let imd_segments = ctx.find_segments("IMD");
1069 let found = imd_segments.iter().any(|s| {
1070 s.elements
1071 .get(1)
1072 .and_then(|e| e.first())
1073 .is_some_and(|v| v == "MSB")
1074 });
1075 ConditionResult::from(found)
1076 }
1077
1078 /// [74] wenn im DE3155 in demselben COM der Code EM vorhanden ist
1079 fn evaluate_74(&self, ctx: &EvaluationContext) -> ConditionResult {
1080 let com_segments = ctx.find_segments("COM");
1081 let found = com_segments.iter().any(|s| {
1082 s.elements
1083 .first()
1084 .and_then(|e| e.get(1))
1085 .is_some_and(|v| v == "EM")
1086 });
1087 ConditionResult::from(found)
1088 }
1089
1090 /// [75] wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
1091 fn evaluate_75(&self, ctx: &EvaluationContext) -> ConditionResult {
1092 let com_segments = ctx.find_segments("COM");
1093 let found = com_segments.iter().any(|s| {
1094 s.elements
1095 .first()
1096 .and_then(|e| e.get(1))
1097 .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
1098 });
1099 ConditionResult::from(found)
1100 }
1101
1102 /// [76] Wenn MP-ID in NAD+MR in der Rolle NB
1103 /// EXTERNAL: Requires context from outside the message.
1104 // REVIEW: Whether the MP-ID in NAD+MR has the market role NB (Netzbetreiber) cannot be determined from the EDIFACT message alone — it requires a business registry lookup. The NAD+MR segment only provides the party identifier, not the assigned market role. (medium confidence)
1105 fn evaluate_76(&self, ctx: &EvaluationContext) -> ConditionResult {
1106 ctx.external.evaluate("recipient_is_nb")
1107 }
1108
1109 /// [77] Wenn SG26 DTM+156 (Positionsbezogener Abrechnungszeitraum Ende) ≤ 1.1.2024 0:00 gesetzlicher deutscher Zeit
1110 // REVIEW: SG26 DTM+156 means elements[0][0]=="156". Value at elements[0][1] in 303 format. Compare <= "202401010000" (Jan 1 2024 00:00). Medium confidence due to timezone nuance. (medium confidence)
1111 fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
1112 let dtm_segments = ctx.find_segments_with_qualifier("DTM", 0, "156");
1113 match dtm_segments.first() {
1114 Some(dtm) => {
1115 match dtm
1116 .elements
1117 .first()
1118 .and_then(|e| e.get(1))
1119 .map(|s| s.as_str())
1120 {
1121 Some(value) if !value.is_empty() => {
1122 // 303 format: YYYYMMDDhhmm — lexicographic comparison is chronological
1123 ConditionResult::from(value <= "202401010000")
1124 }
1125 _ => ConditionResult::Unknown,
1126 }
1127 }
1128 None => ConditionResult::False, // segment absent → condition not applicable
1129 }
1130 }
1131
1132 /// [78] Wenn GEI+Z08 nicht vorhanden
1133 // REVIEW: GEI schema shows elements[0] qualifier is always Z01, while the specification code is at elements[1][0] (DE7365) with codes Z01-Z06 +3 more. 'GEI+Z08' in AHB shorthand refers to elements[1][0]=="Z08". Absence check returns True when not found. (medium confidence)
1134 fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
1135 let found = ctx.find_segments("GEI").iter().any(|s| {
1136 s.elements
1137 .get(1)
1138 .and_then(|e| e.first())
1139 .is_some_and(|v| v == "Z08")
1140 });
1141 ConditionResult::from(!found)
1142 }
1143
1144 /// [79] Wenn GEI+Z09 nicht vorhanden
1145 // REVIEW: Same pattern as condition 78. 'GEI+Z09' in AHB shorthand refers to elements[1][0]=="Z09". (medium confidence)
1146 fn evaluate_79(&self, ctx: &EvaluationContext) -> ConditionResult {
1147 let found = ctx.find_segments("GEI").iter().any(|s| {
1148 s.elements
1149 .get(1)
1150 .and_then(|e| e.first())
1151 .is_some_and(|v| v == "Z09")
1152 });
1153 ConditionResult::from(!found)
1154 }
1155
1156 /// [80] Wenn GEI+Z01 nicht vorhanden
1157 // REVIEW: 'GEI+Z01' refers to elements[1][0]=="Z01" (the specification code, not the fixed qualifier at elements[0]). Absence check. (medium confidence)
1158 fn evaluate_80(&self, ctx: &EvaluationContext) -> ConditionResult {
1159 let found = ctx.find_segments("GEI").iter().any(|s| {
1160 s.elements
1161 .get(1)
1162 .and_then(|e| e.first())
1163 .is_some_and(|v| v == "Z01")
1164 });
1165 ConditionResult::from(!found)
1166 }
1167
1168 /// [81] Wenn GEI+Z04 nicht vorhanden
1169 // REVIEW: Same pattern as condition 78. 'GEI+Z04' refers to elements[1][0]=="Z04". (medium confidence)
1170 fn evaluate_81(&self, ctx: &EvaluationContext) -> ConditionResult {
1171 let found = ctx.find_segments("GEI").iter().any(|s| {
1172 s.elements
1173 .get(1)
1174 .and_then(|e| e.first())
1175 .is_some_and(|v| v == "Z04")
1176 });
1177 ConditionResult::from(!found)
1178 }
1179
1180 /// [82] Wenn GEI+Z05 nicht vorhanden
1181 // REVIEW: Same pattern as condition 78. 'GEI+Z05' refers to elements[1][0]=="Z05". (medium confidence)
1182 fn evaluate_82(&self, ctx: &EvaluationContext) -> ConditionResult {
1183 let found = ctx.find_segments("GEI").iter().any(|s| {
1184 s.elements
1185 .get(1)
1186 .and_then(|e| e.first())
1187 .is_some_and(|v| v == "Z05")
1188 });
1189 ConditionResult::from(!found)
1190 }
1191
1192 /// [84] Wenn in dieser SG39 ALC+C+:Z02 / Z03 / Z04 vorhanden
1193 fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
1194 ctx.any_group_has_qualified_value("ALC", 0, "C", 1, 1, &["Z02", "Z03", "Z04"], &["SG39"])
1195 }
1196
1197 /// [85] Wenn in diesem Segment DE6411 = DAY
1198 // REVIEW: DE6411 is the measurement unit code, appearing in QTY segments as C186 composite: elements[0][0]=DE6063 (qualifier), elements[0][1]=DE6060 (quantity), elements[0][2]=DE6411 (unit code). 'DAY' is the unit code for daily quantities. Medium confidence as the specific segment context ('in diesem Segment') may require group scoping. (medium confidence)
1199 fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
1200 let qty_segments = ctx.find_segments("QTY");
1201 let found = qty_segments.iter().any(|s| {
1202 s.elements
1203 .first()
1204 .and_then(|e| e.get(2))
1205 .is_some_and(|v| v == "DAY")
1206 });
1207 ConditionResult::from(found)
1208 }
1209
1210 /// [86] Wenn in diesem Segment DE6411 = MON/ANN
1211 // REVIEW: DE6411 is the unit of measure code in QTY segments at elements[0][2] (C186 component 2). 'In diesem Segment' means within the current QTY context. Check message-wide for QTY with MON or ANN unit code. (medium confidence)
1212 fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
1213 let qty_segments = ctx.find_segments("QTY");
1214 ConditionResult::from(qty_segments.iter().any(|s| {
1215 s.elements
1216 .first()
1217 .and_then(|e| e.get(2))
1218 .map(|v| v == "MON" || v == "ANN")
1219 .unwrap_or(false)
1220 }))
1221 }
1222
1223 /// [88] Wenn DTM+155 (Abrechnungszeitraum Beginn) ≥ 1.1.2026 0:00 gesetzlicher deutscher Zeit
1224 fn evaluate_88(&self, ctx: &EvaluationContext) -> ConditionResult {
1225 let dtm_segs = ctx.find_segments_with_qualifier("DTM", 0, "155");
1226 match dtm_segs.first() {
1227 Some(dtm) => {
1228 match dtm
1229 .elements
1230 .first()
1231 .and_then(|e| e.get(1))
1232 .map(|s| s.as_str())
1233 {
1234 Some(value) if value.len() >= 8 => {
1235 let date_part = &value[..8];
1236 ConditionResult::from(date_part >= "20260101")
1237 }
1238 _ => ConditionResult::Unknown,
1239 }
1240 }
1241 None => ConditionResult::False, // segment absent → condition not applicable
1242 }
1243 }
1244
1245 /// [490] wenn Wert in diesem DE, an der Stelle CCYYMMDD ein Datum aus dem angegeben Zeitraum der Tabelle Kapitel 3.5 „Prozesszeitpunkt bei MESZ mit UTC“ ist
1246 fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
1247 match ctx.resolved_value {
1248 Some(val) => is_mesz_utc(val),
1249 None => ConditionResult::False,
1250 }
1251 }
1252
1253 /// [491] wenn Wert in diesem DE, an der Stelle CCYYMMDD ein Datum aus dem angegeben Zeitraum der Tabelle Kapitel 3.6 „Prozesszeitpunkt bei MEZ mit UTC“ ist
1254 fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
1255 match ctx.resolved_value {
1256 Some(val) => is_mez_utc(val),
1257 None => ConditionResult::False,
1258 }
1259 }
1260
1261 /// [492] Wenn MP-ID in NAD+MR (Nachrichtenempfänger) aus Sparte Strom
1262 /// EXTERNAL: Requires context from outside the message.
1263 // REVIEW: Checks whether the NAD+MR (message recipient) MP-ID belongs to the electricity (Strom) sector. The NAD+MR element structure is known (elements[0]=MR, elements[1][0]=MP-ID), but sector membership cannot be determined from the EDIFACT message content alone — it requires an external market participant registry. Delegated to external provider. (medium confidence)
1264 fn evaluate_492(&self, ctx: &EvaluationContext) -> ConditionResult {
1265 ctx.external.evaluate("recipient_is_strom")
1266 }
1267
1268 /// [493] Wenn MP-ID in NAD+MR (Nachrichtenempfänger) aus Sparte Gas
1269 /// EXTERNAL: Requires context from outside the message.
1270 // REVIEW: Checks whether the NAD+MR (message recipient) MP-ID belongs to the gas (Gas) sector. Same reasoning as 492 — sector membership cannot be determined from the EDIFACT message alone; requires external market participant registry. Delegated to external provider. (medium confidence)
1271 fn evaluate_493(&self, ctx: &EvaluationContext) -> ConditionResult {
1272 ctx.external.evaluate("recipient_is_gas")
1273 }
1274
1275 /// [501] Hinweis: Dokumentennummer der ORDERS
1276 fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
1277 // Hinweis: Dokumentennummer der ORDERS — informational note, always applies
1278 ConditionResult::True
1279 }
1280
1281 /// [502] Hinweis: Dokumentennummer der Bilanzierungs-MSCONS
1282 fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
1283 // Hinweis: Dokumentennummer der Bilanzierungs-MSCONS — informational note, always applies
1284 ConditionResult::True
1285 }
1286
1287 /// [503] Hinweis: Ein positiver Betrag ist eine Forderung des Rechnungsstellers.
1288 fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
1289 // Hinweis: Ein positiver Betrag ist eine Forderung des Rechnungsstellers — informational note, always applies
1290 ConditionResult::True
1291 }
1292
1293 /// [504] Hinweis: Ein positiver Betrag ist eine Forderung des Rechnungsempfängers.
1294 fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
1295 // Hinweis: Ein positiver Betrag ist eine Forderung des Rechnungsempfängers — informational note, always applies
1296 ConditionResult::True
1297 }
1298
1299 /// [507] Hinweis: Dokumentennummer der SSQNOT
1300 fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
1301 // Hinweis: Dokumentennummer der SSQNOT — informational note, always applies
1302 ConditionResult::True
1303 }
1304
1305 /// [508] Hinweis: Dokumentennummer der QUOTES
1306 fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
1307 // Hinweis: Dokumentennummer der QUOTES — informational note, always applies
1308 ConditionResult::True
1309 }
1310
1311 /// [509] Hinweis: Verwendung der ID der Marktlokation
1312 fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
1313 // Hinweis: Verwendung der ID der Marktlokation — informational note, always applies
1314 ConditionResult::True
1315 }
1316
1317 /// [510] Hinweis: Verwendung der ID der Messlokation
1318 fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
1319 // Hinweis: Verwendung der ID der Messlokation — informational note, always applies
1320 ConditionResult::True
1321 }
1322
1323 /// [512] Hinweis: Hier ist entweder der Betrag aus MOA+203 oder der um den gültigen Steuerbetrag erhöhte Betrag aus MOA+203 anzugeben.
1324 fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
1325 // Hinweis: Hier ist entweder der Betrag aus MOA+203 oder der um den gültigen Steuerbetrag erhöhte Betrag aus MOA+203 anzugeben — informational note, always applies
1326 ConditionResult::True
1327 }
1328
1329 /// [513] Hinweis: Hier ist das Ergebnis der Multiplikation von MOA+25 mit PCD+3 anzugeben.
1330 fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
1331 // Hinweis: Hier ist das Ergebnis der Multiplikation von MOA+25 mit PCD+3 anzugeben — informational note, always applies
1332 ConditionResult::True
1333 }
1334
1335 /// [514] Hinweis: Dokumentennummer der Lieferschein-MSCONS
1336 fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
1337 // Hinweis: Dokumentennummer der Lieferschein-MSCONS — informational note, always applies
1338 ConditionResult::True
1339 }
1340
1341 /// [515] Hinweis: BGM DE1004 aus INVOIC-Nachricht, die storniert werden soll
1342 fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
1343 // Hinweis: BGM DE1004 aus INVOIC-Nachricht, die storniert werden soll — informational note, always applies
1344 ConditionResult::True
1345 }
1346
1347 /// [516] Hinweis: Ein Lieferschein zu einer Rechnung ist für alle Abrechnungszeiträume, die erstmals nach dem 1.12.2019 abgerechnet werden und für alle Abrechnungszeiträume, für die sich nach dem 1.12....
1348 fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
1349 // Hinweis: Ein Lieferschein zu einer Rechnung ist für alle Abrechnungszeiträume, die erstmals nach dem 1.12.2019 abgerechnet werden und für alle Abrechnungszeiträume, für die sich nach dem 1.12.2019 geänderte Mengen oder Leistungswerte ergeben, nötig.
1350 ConditionResult::True
1351 }
1352
1353 /// [517] Hinweis: Dokumentennummer der PDF-Kapazitätsrechnung
1354 fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
1355 // Hinweis: Dokumentennummer der PDF-Kapazitätsrechnung — informational annotation, always applies
1356 ConditionResult::True
1357 }
1358
1359 /// [518] Hinweis: Im Fall der Stornierung des Auftrags der Unterbrechung: Der Tag an dem der NB die Stornierung empfangen hat Bei erfolgreicher Sperrung: Tag der durchgeführten Sperrung Bei erfolgloser Sp...
1360 fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
1361 // Hinweis: Date semantics for cancellation/blocking scenarios — informational annotation, always applies
1362 ConditionResult::True
1363 }
1364
1365 /// [519] Hinweis: Stornierte Abschlagsrechnungen sind nicht aufzuführen
1366 fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
1367 // Hinweis: Stornierte Abschlagsrechnungen sind nicht aufzuführen — informational annotation, always applies
1368 ConditionResult::True
1369 }
1370
1371 /// [520] Hinweis: Es sind nur die Artikel-IDs aus dem Preisblatt erlaubt
1372 fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
1373 // Hinweis: Es sind nur die Artikel-IDs aus dem Preisblatt erlaubt — informational annotation, always applies
1374 ConditionResult::True
1375 }
1376
1377 /// [521] Hinweis: BGM DE1004 aus der INVOIC-Nachricht, für die Verzugskosten erhoben werden
1378 fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
1379 // Hinweis: BGM DE1004 aus der INVOIC-Nachricht, für die Verzugskosten erhoben werden — informational note, always applies
1380 ConditionResult::True
1381 }
1382
1383 /// [522] Hinweis: Verwendung der ID der Netzlokation
1384 fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
1385 // Hinweis: Verwendung der ID der Netzlokation — informational note, always applies
1386 ConditionResult::True
1387 }
1388
1389 /// [523] Hinweis: Verwendung der ID der Steuerbaren Ressource
1390 fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
1391 // Hinweis: Verwendung der ID der Steuerbaren Ressource — informational note, always applies
1392 ConditionResult::True
1393 }
1394
1395 /// [524] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
1396 fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
1397 // Hinweis: Es darf nur eine Information im DE3148 übermittelt werden — informational note, always applies
1398 ConditionResult::True
1399 }
1400
1401 /// [525] Hinweis: Verwendung, sofern Netzentgelte geringer als die Pauschale Netzentgeltreduzierung
1402 fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
1403 // Hinweis: Verwendung, sofern Netzentgelte geringer als die Pauschale Netzentgeltreduzierung — informational note, always applies
1404 ConditionResult::True
1405 }
1406
1407 /// [526] Hinweis: Der hier angegebene Geldbetrag muss mit dem identisch sein, der in SG50 MOA+77 im DE5004 der Abschlagsrechnung steht, die die Rechnungsnummer hat, die in dieser SG50 in SG51-RFF+AFL in DE1...
1408 fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
1409 // Hinweis: Der hier angegebene Geldbetrag muss mit dem identisch sein, der in SG50 MOA+77
1410 // im DE5004 der Abschlagsrechnung steht, die die Rechnungsnummer hat, die in dieser SG50
1411 // in SG51-RFF+AFL in DE1154 genannt ist.
1412 // Informational note — always applies
1413 ConditionResult::True
1414 }
1415
1416 /// [902] Format: Möglicher Wert: ≥ 0
1417 // REVIEW: Format condition requiring value >= 0, applied to MOA amount (DE5004 at elements[0][1]). Medium confidence because exact segment/group context is inferred from INVOIC structure — the full AHB table row would clarify which specific MOA qualifier this applies to. (medium confidence)
1418 fn evaluate_902(&self, ctx: &EvaluationContext) -> ConditionResult {
1419 ctx.format_check("MOA", 0, 1, |val| validate_numeric(val, ">=", 0.0))
1420 }
1421
1422 /// [906] Format: max. 3 Nachkommastellen
1423 // REVIEW: Format condition requiring at most 3 decimal places. Most likely applies to MOA DE5004 (Geldbetrag) in INVOIC. Medium confidence — the exact MOA qualifier or group (SG27/SG42/SG50/SG52) depends on the AHB table row context not included in the condition description alone. (medium confidence)
1424 fn evaluate_906(&self, ctx: &EvaluationContext) -> ConditionResult {
1425 ctx.format_check("MOA", 0, 1, |val| validate_max_decimal_places(val, 3))
1426 }
1427
1428 /// [908] Format: Mögliche Werte: 1 bis n
1429 // REVIEW: Format condition requiring value in range 1..n (strictly positive). Applied to QTY quantity values in INVOIC (DE6060 at elements[0][1]). Medium confidence — the exact segment context depends on the full AHB table row. (medium confidence)
1430 fn evaluate_908(&self, ctx: &EvaluationContext) -> ConditionResult {
1431 ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">=", 1.0))
1432 }
1433
1434 /// [910] Format: Möglicher Wert: < 0 oder ≥ 0
1435 // REVIEW: Format condition accepting any numeric value (< 0 OR >= 0 is universally true). This documents that the field accepts negative values (unlike condition 902 which restricts to >= 0). Validates the value is a parseable number. Medium confidence on the exact segment — likely a MOA that can be a credit/deduction amount. (medium confidence)
1436 fn evaluate_910(&self, ctx: &EvaluationContext) -> ConditionResult {
1437 // Format: Möglicher Wert: < 0 oder ≥ 0 — any numeric value is valid (no sign constraint)
1438 // Applied to MOA amounts that may be negative (e.g., credits, deductions)
1439 let segs = ctx.find_segments("MOA");
1440 match segs
1441 .first()
1442 .and_then(|s| s.elements.first())
1443 .and_then(|e| e.get(1))
1444 {
1445 Some(val) if !val.is_empty() => {
1446 // < 0 or >= 0 covers all real numbers — validate it is a parseable number
1447 match val.trim_start_matches('-').parse::<f64>() {
1448 Ok(_) => ConditionResult::True,
1449 Err(_) => ConditionResult::False,
1450 }
1451 }
1452 _ => ConditionResult::Unknown,
1453 }
1454 }
1455
1456 /// [911] Format: Mögliche Werte: 1 bis n, je Nachricht oder Segmentgruppe bei 1 beginnend und fortlaufend aufsteigend
1457 // REVIEW: Sequential numbering validation (1 to n, ascending) — only the >= 1 integer constraint can be checked on a single value. Full sequential ordering cannot be verified without stateful iteration across all instances. Applies to LIN line item numbers in INVOIC SG26. Medium confidence due to segment assumption and inability to verify sequential ordering in isolation. (medium confidence)
1458 fn evaluate_911(&self, ctx: &EvaluationContext) -> ConditionResult {
1459 // Format: Mögliche Werte: 1 bis n, je Nachricht oder Segmentgruppe bei 1 beginnend und fortlaufend aufsteigend
1460 // Validates that the value is a positive integer >= 1 (sequential enforcement requires external state)
1461 let segs = ctx.find_segments("LIN");
1462 match segs
1463 .first()
1464 .and_then(|s| s.elements.first())
1465 .and_then(|e| e.first())
1466 {
1467 Some(val) => match val.parse::<i64>() {
1468 Ok(n) if n >= 1 => ConditionResult::True,
1469 Ok(_) => ConditionResult::False,
1470 Err(_) => ConditionResult::False,
1471 },
1472 None => ConditionResult::False, // segment absent → condition not applicable
1473 }
1474 }
1475
1476 /// [912] Format: max. 6 Nachkommastellen
1477 fn evaluate_912(&self, ctx: &EvaluationContext) -> ConditionResult {
1478 ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 6))
1479 }
1480
1481 /// [914] Format: Möglicher Wert: > 0
1482 fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
1483 ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, ">", 0.0))
1484 }
1485
1486 /// [927] Format: Möglicher Wert: -1
1487 // REVIEW: Value must be exactly -1. Used for special indicator values (e.g., a reversal or placeholder quantity). validate_numeric with == operator handles exact match. Medium confidence due to segment assumption — the -1 constraint is unusual and the exact segment context (QTY vs MOA) depends on which AHB data element references this condition. (medium confidence)
1488 fn evaluate_927(&self, ctx: &EvaluationContext) -> ConditionResult {
1489 ctx.format_check("QTY", 0, 1, |val| validate_numeric(val, "==", -1.0))
1490 }
1491
1492 /// [930] Format: max. 2 Nachkommastellen
1493 fn evaluate_930(&self, ctx: &EvaluationContext) -> ConditionResult {
1494 ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 2))
1495 }
1496
1497 /// [931] Format: ZZZ = +00
1498 fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
1499 ctx.format_check("DTM", 0, 1, validate_timezone_utc)
1500 }
1501
1502 /// [932] Format: HHMM = 2200
1503 fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
1504 ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
1505 }
1506
1507 /// [933] Format: HHMM = 2300
1508 fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
1509 ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
1510 }
1511
1512 /// [934] Format: HHMM = 0400
1513 fn evaluate_934(&self, ctx: &EvaluationContext) -> ConditionResult {
1514 ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0400"))
1515 }
1516
1517 /// [935] Format: HHMM = 0500
1518 fn evaluate_935(&self, ctx: &EvaluationContext) -> ConditionResult {
1519 ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "0500"))
1520 }
1521
1522 /// [937] Format: keine Nachkommastelle
1523 fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
1524 ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 0))
1525 }
1526
1527 /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
1528 fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
1529 ctx.format_check("COM", 0, 0, validate_email)
1530 }
1531
1532 /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
1533 fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
1534 ctx.format_check("COM", 0, 0, validate_phone)
1535 }
1536
1537 /// [946] Format: max. 11 Nachkommastellen
1538 fn evaluate_946(&self, ctx: &EvaluationContext) -> ConditionResult {
1539 ctx.format_check("QTY", 0, 1, |val| validate_max_decimal_places(val, 11))
1540 }
1541
1542 /// [950] Format: Marktlokations-ID
1543 fn evaluate_950(&self, ctx: &EvaluationContext) -> ConditionResult {
1544 ctx.format_check_qualified("LOC", 0, "Z16", 1, 0, validate_malo_id)
1545 }
1546
1547 /// [951] Format: Zählpunktbezeichnung
1548 fn evaluate_951(&self, ctx: &EvaluationContext) -> ConditionResult {
1549 ctx.format_check_qualified("LOC", 0, "Z17", 1, 0, validate_zahlpunkt)
1550 }
1551
1552 /// [960] Format: Netzlokations-ID
1553 fn evaluate_960(&self, ctx: &EvaluationContext) -> ConditionResult {
1554 ctx.format_check_qualified("LOC", 0, "Z18", 1, 0, validate_malo_id)
1555 }
1556
1557 /// [961] Format: SR-ID
1558 fn evaluate_961(&self, ctx: &EvaluationContext) -> ConditionResult {
1559 ctx.format_check_qualified("LOC", 0, "Z19", 1, 0, validate_malo_id)
1560 }
1561}