swift_mt_message/messages/
mt104.rs

1use crate::errors::SwiftValidationError;
2use crate::fields::*;
3use crate::parser::utils::*;
4use serde::{Deserialize, Serialize};
5
6/// Sequence B - Transaction details
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
8pub struct MT104Transaction {
9    /// Transaction reference (Field 21)
10    #[serde(rename = "21")]
11    pub field_21: Field21NoOption,
12
13    /// Instruction code (Field 23E)
14    #[serde(rename = "23E")]
15    pub field_23e: Option<Field23E>,
16
17    /// Mandate reference (Field 21C)
18    #[serde(rename = "21C")]
19    pub field_21c: Option<Field21C>,
20
21    /// Direct debit reference (Field 21D)
22    #[serde(rename = "21D")]
23    pub field_21d: Option<Field21D>,
24
25    /// Registration reference (Field 21E)
26    #[serde(rename = "21E")]
27    pub field_21e: Option<Field21E>,
28
29    /// Currency and amount (Field 32B)
30    #[serde(rename = "32B")]
31    pub field_32b: Field32B,
32
33    /// Instructing party (Field 50C/L)
34    #[serde(flatten)]
35    pub instructing_party_tx: Option<Field50InstructingParty>,
36
37    /// Creditor (Field 50A/K)
38    #[serde(flatten)]
39    pub creditor_tx: Option<Field50Creditor>,
40
41    /// Creditor's bank (Field 52)
42    #[serde(flatten)]
43    pub field_52: Option<Field52CreditorBank>,
44
45    /// Debtor's bank (Field 57)
46    #[serde(flatten)]
47    pub field_57: Option<Field57DebtorBank>,
48
49    /// Debtor (Field 59)
50    #[serde(flatten)]
51    pub field_59: Field59Debtor,
52
53    /// Remittance information (Field 70)
54    #[serde(rename = "70")]
55    pub field_70: Option<Field70>,
56
57    /// Transaction type code (Field 26T)
58    #[serde(rename = "26T")]
59    pub field_26t: Option<Field26T>,
60
61    /// Regulatory reporting (Field 77B)
62    #[serde(rename = "77B")]
63    pub field_77b: Option<Field77B>,
64
65    /// Original ordered amount (Field 33B)
66    #[serde(rename = "33B")]
67    pub field_33b: Option<Field33B>,
68
69    /// Details of charges (Field 71A)
70    #[serde(rename = "71A")]
71    pub field_71a: Option<Field71A>,
72
73    /// Sender's charges (Field 71F)
74    #[serde(rename = "71F")]
75    pub field_71f: Option<Field71F>,
76
77    /// Receiver's charges (Field 71G)
78    #[serde(rename = "71G")]
79    pub field_71g: Option<Field71G>,
80
81    /// Exchange rate (Field 36)
82    #[serde(rename = "36")]
83    pub field_36: Option<Field36>,
84}
85
86/// **MT104: Direct Debit and Request for Debit Transfer**
87///
88/// Creditor instruction to collect funds from one or more debtors via banks.
89/// Supports batch direct debit processing with settlement details.
90///
91/// **Usage:** Direct debits, batch collections, SEPA debits
92/// **Category:** Category 1 (Customer Payments)
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
94pub struct MT104 {
95    /// Sender's reference (Field 20)
96    #[serde(rename = "20")]
97    pub field_20: Field20,
98
99    /// Customer specified reference (Field 21R)
100    #[serde(rename = "21R")]
101    pub field_21r: Option<Field21R>,
102
103    /// Instruction code (Field 23E)
104    #[serde(rename = "23E")]
105    pub field_23e: Option<Field23E>,
106
107    /// Registration reference (Field 21E)
108    #[serde(rename = "21E")]
109    pub field_21e: Option<Field21E>,
110
111    /// Requested execution date (Field 30)
112    #[serde(rename = "30")]
113    pub field_30: Field30,
114
115    /// Sending institution (Field 51A)
116    #[serde(rename = "51A")]
117    pub field_51a: Option<Field51A>,
118
119    /// Instructing party (Field 50C/L)
120    #[serde(flatten)]
121    pub instructing_party: Option<Field50InstructingParty>,
122
123    /// Creditor (Field 50A/K)
124    #[serde(flatten)]
125    pub creditor: Option<Field50Creditor>,
126
127    /// Creditor's bank (Field 52)
128    #[serde(flatten)]
129    pub field_52: Option<Field52CreditorBank>,
130
131    /// Transaction type code (Field 26T)
132    #[serde(rename = "26T")]
133    pub field_26t: Option<Field26T>,
134
135    /// Regulatory reporting (Field 77B)
136    #[serde(rename = "77B")]
137    pub field_77b: Option<Field77B>,
138
139    /// Details of charges (Field 71A)
140    #[serde(rename = "71A")]
141    pub field_71a: Option<Field71A>,
142
143    /// Sender to receiver information (Field 72)
144    #[serde(rename = "72")]
145    pub field_72: Option<Field72>,
146
147    /// Transaction details (Sequence B)
148    #[serde(rename = "#")]
149    pub transactions: Vec<MT104Transaction>,
150
151    /// Settlement amount (Field 32B, Sequence C)
152    #[serde(rename = "32B")]
153    pub field_32b: Option<Field32B>,
154
155    /// Sum of amounts (Field 19)
156    #[serde(rename = "19")]
157    pub field_19: Option<Field19>,
158
159    /// Sum of sender's charges (Field 71F)
160    #[serde(rename = "71F")]
161    pub field_71f: Option<Field71F>,
162
163    /// Sum of receiver's charges (Field 71G)
164    #[serde(rename = "71G")]
165    pub field_71g: Option<Field71G>,
166
167    /// Sender's correspondent (Field 53)
168    #[serde(flatten)]
169    pub field_53: Option<Field53SenderCorrespondent>,
170}
171
172impl MT104 {
173    /// Parse message from Block 4 content
174    pub fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
175        let mut parser = crate::parser::MessageParser::new(block4, "104");
176
177        // Parse Sequence A fields
178        let field_20 = parser.parse_field::<Field20>("20")?;
179        let field_21r = parser.parse_optional_field::<Field21R>("21R")?;
180        let field_23e = parser.parse_optional_field::<Field23E>("23E")?;
181        let field_21e = parser.parse_optional_field::<Field21E>("21E")?;
182        let field_30 = parser.parse_field::<Field30>("30")?;
183        let field_51a = parser.parse_optional_field::<Field51A>("51A")?;
184
185        // Parse optional ordering parties - check variant to determine field type
186        let mut instructing_party = None;
187        let mut creditor = None;
188
189        // Check if field 50 exists and determine its type based on the variant
190        if let Some(variant) = parser.peek_field_variant("50") {
191            match variant.as_str() {
192                "C" | "L" => {
193                    // These variants are for Field50InstructingParty
194                    instructing_party =
195                        parser.parse_optional_variant_field::<Field50InstructingParty>("50")?;
196                }
197                "A" | "K" => {
198                    // These variants are for Field50Creditor
199                    creditor = parser.parse_optional_variant_field::<Field50Creditor>("50")?;
200                }
201                _ => {
202                    // Unknown variant, try both
203                    if let Ok(ip) =
204                        parser.parse_optional_variant_field::<Field50InstructingParty>("50")
205                    {
206                        instructing_party = ip;
207                    } else {
208                        creditor = parser.parse_optional_variant_field::<Field50Creditor>("50")?;
209                    }
210                }
211            }
212        }
213
214        let field_52 = parser.parse_optional_variant_field::<Field52CreditorBank>("52")?;
215        let field_26t = parser.parse_optional_field::<Field26T>("26T")?;
216        let field_77b = parser.parse_optional_field::<Field77B>("77B")?;
217        let field_71a = parser.parse_optional_field::<Field71A>("71A")?;
218        let field_72 = parser.parse_optional_field::<Field72>("72")?;
219
220        // Parse Sequence B (transactions) - enable duplicates
221        let mut transactions = Vec::new();
222        parser = parser.with_duplicates(true);
223
224        while parser.detect_field("21") {
225            let field_21 = parser.parse_field::<Field21NoOption>("21")?;
226            let field_23e_tx = parser.parse_optional_field::<Field23E>("23E")?;
227            let field_21c = parser.parse_optional_field::<Field21C>("21C")?;
228            let field_21d = parser.parse_optional_field::<Field21D>("21D")?;
229            let field_21e_tx = parser.parse_optional_field::<Field21E>("21E")?;
230            let field_32b = parser.parse_field::<Field32B>("32B")?;
231
232            // Transaction-level optional parties - check variant to determine field type
233            let mut instructing_party_tx = None;
234            let mut creditor_tx = None;
235
236            // Check if field 50 exists and determine its type based on the variant
237            if let Some(variant) = parser.peek_field_variant("50") {
238                match variant.as_str() {
239                    "C" | "L" => {
240                        // These variants are for Field50InstructingParty
241                        instructing_party_tx =
242                            parser.parse_optional_variant_field::<Field50InstructingParty>("50")?;
243                    }
244                    "A" | "K" => {
245                        // These variants are for Field50Creditor
246                        creditor_tx =
247                            parser.parse_optional_variant_field::<Field50Creditor>("50")?;
248                    }
249                    _ => {
250                        // Unknown variant, try both
251                        if let Ok(ip) =
252                            parser.parse_optional_variant_field::<Field50InstructingParty>("50")
253                        {
254                            instructing_party_tx = ip;
255                        } else {
256                            creditor_tx =
257                                parser.parse_optional_variant_field::<Field50Creditor>("50")?;
258                        }
259                    }
260                }
261            }
262
263            let field_52_tx = parser.parse_optional_variant_field::<Field52CreditorBank>("52")?;
264            let field_57 = parser.parse_optional_variant_field::<Field57DebtorBank>("57")?;
265            let field_59 = parser.parse_variant_field::<Field59Debtor>("59")?;
266            let field_70 = parser.parse_optional_field::<Field70>("70")?;
267            let field_26t_tx = parser.parse_optional_field::<Field26T>("26T")?;
268            let field_77b_tx = parser.parse_optional_field::<Field77B>("77B")?;
269            let field_33b = parser.parse_optional_field::<Field33B>("33B")?;
270            let field_71a_tx = parser.parse_optional_field::<Field71A>("71A")?;
271            let field_71f = parser.parse_optional_field::<Field71F>("71F")?;
272            let field_71g = parser.parse_optional_field::<Field71G>("71G")?;
273            let field_36 = parser.parse_optional_field::<Field36>("36")?;
274
275            transactions.push(MT104Transaction {
276                field_21,
277                field_23e: field_23e_tx,
278                field_21c,
279                field_21d,
280                field_21e: field_21e_tx,
281                field_32b,
282                instructing_party_tx,
283                creditor_tx,
284                field_52: field_52_tx,
285                field_57,
286                field_59,
287                field_70,
288                field_26t: field_26t_tx,
289                field_77b: field_77b_tx,
290                field_33b,
291                field_71a: field_71a_tx,
292                field_71f,
293                field_71g,
294                field_36,
295            });
296        }
297
298        // Parse Sequence C (optional settlement details)
299        let field_32b = parser.parse_optional_field::<Field32B>("32B")?;
300        let field_19 = parser.parse_optional_field::<Field19>("19")?;
301        let field_71f = parser.parse_optional_field::<Field71F>("71F")?;
302        let field_71g = parser.parse_optional_field::<Field71G>("71G")?;
303        let field_53 = parser.parse_optional_variant_field::<Field53SenderCorrespondent>("53")?;
304
305        // Verify all content is consumed
306        verify_parser_complete(&parser)?;
307
308        Ok(Self {
309            field_20,
310            field_21r,
311            field_23e,
312            field_21e,
313            field_30,
314            field_51a,
315            instructing_party,
316            creditor,
317            field_52,
318            field_26t,
319            field_77b,
320            field_71a,
321            field_72,
322            transactions,
323            field_32b,
324            field_19,
325            field_71f,
326            field_71g,
327            field_53,
328        })
329    }
330
331    /// Parse from generic SWIFT input
332    pub fn parse(input: &str) -> Result<Self, crate::errors::ParseError> {
333        let block4 = extract_block4(input)?;
334        Self::parse_from_block4(&block4)
335    }
336
337    /// Convert to SWIFT MT text format
338    pub fn to_mt_string(&self) -> String {
339        let mut result = String::new();
340
341        // Sequence A fields
342        append_field(&mut result, &self.field_20);
343        append_optional_field(&mut result, &self.field_21r);
344        append_optional_field(&mut result, &self.field_23e);
345        append_optional_field(&mut result, &self.field_21e);
346        append_field(&mut result, &self.field_30);
347        append_optional_field(&mut result, &self.field_51a);
348        append_optional_field(&mut result, &self.instructing_party);
349        append_optional_field(&mut result, &self.creditor);
350        append_optional_field(&mut result, &self.field_52);
351        append_optional_field(&mut result, &self.field_26t);
352        append_optional_field(&mut result, &self.field_77b);
353        append_optional_field(&mut result, &self.field_71a);
354        append_optional_field(&mut result, &self.field_72);
355
356        // Sequence B (transactions)
357        for transaction in &self.transactions {
358            append_field(&mut result, &transaction.field_21);
359            append_optional_field(&mut result, &transaction.field_23e);
360            append_optional_field(&mut result, &transaction.field_21c);
361            append_optional_field(&mut result, &transaction.field_21d);
362            append_optional_field(&mut result, &transaction.field_21e);
363            append_field(&mut result, &transaction.field_32b);
364            append_optional_field(&mut result, &transaction.instructing_party_tx);
365            append_optional_field(&mut result, &transaction.creditor_tx);
366            append_optional_field(&mut result, &transaction.field_52);
367            append_optional_field(&mut result, &transaction.field_57);
368            append_field(&mut result, &transaction.field_59);
369            append_optional_field(&mut result, &transaction.field_70);
370            append_optional_field(&mut result, &transaction.field_26t);
371            append_optional_field(&mut result, &transaction.field_77b);
372            append_optional_field(&mut result, &transaction.field_33b);
373            append_optional_field(&mut result, &transaction.field_71a);
374            append_optional_field(&mut result, &transaction.field_71f);
375            append_optional_field(&mut result, &transaction.field_71g);
376            append_optional_field(&mut result, &transaction.field_36);
377        }
378
379        // Sequence C (optional settlement)
380        append_optional_field(&mut result, &self.field_32b);
381        append_optional_field(&mut result, &self.field_19);
382        append_optional_field(&mut result, &self.field_71f);
383        append_optional_field(&mut result, &self.field_71g);
384        append_optional_field(&mut result, &self.field_53);
385
386        result.push('-');
387        result
388    }
389
390    // ========================================================================
391    // NETWORK VALIDATION RULES (SR 2025 MT104)
392    // ========================================================================
393
394    /// Field 23E valid instruction codes for MT104 Sequence A
395    const MT104_VALID_23E_CODES_SEQ_A: &'static [&'static str] =
396        &["AUTH", "NAUT", "OTHR", "RFDD", "RTND"];
397
398    /// Field 23E valid instruction codes for MT104 Sequence B
399    const MT104_VALID_23E_CODES_SEQ_B: &'static [&'static str] = &["AUTH", "NAUT", "OTHR"];
400
401    /// Field 23E code that allows additional information
402    const CODE_WITH_ADDITIONAL_INFO: &'static str = "OTHR";
403
404    // ========================================================================
405    // HELPER METHODS
406    // ========================================================================
407
408    /// Check if Sequence C is present (has at least field 32B)
409    fn has_sequence_c(&self) -> bool {
410        self.field_32b.is_some()
411    }
412
413    /// Check if field 23E in Sequence A contains RFDD
414    fn has_rfdd_in_seq_a(&self) -> bool {
415        self.field_23e
416            .as_ref()
417            .is_some_and(|f| f.instruction_code == "RFDD")
418    }
419
420    /// Check if field 23E in Sequence A contains RTND
421    fn has_rtnd_in_seq_a(&self) -> bool {
422        self.field_23e
423            .as_ref()
424            .is_some_and(|f| f.instruction_code == "RTND")
425    }
426
427    /// Check if creditor (A/K) is present in Sequence A
428    fn has_creditor_in_seq_a(&self) -> bool {
429        self.creditor.is_some()
430    }
431
432    /// Check if creditor (A/K) is present in all Sequence B transactions
433    fn has_creditor_in_all_seq_b(&self) -> bool {
434        !self.transactions.is_empty() && self.transactions.iter().all(|tx| tx.creditor_tx.is_some())
435    }
436
437    /// Check if creditor (A/K) is present in any Sequence B transaction
438    fn has_creditor_in_any_seq_b(&self) -> bool {
439        self.transactions.iter().any(|tx| tx.creditor_tx.is_some())
440    }
441
442    /// Check if instructing party (C/L) is present in Sequence A
443    fn has_instructing_party_in_seq_a(&self) -> bool {
444        self.instructing_party.is_some()
445    }
446
447    /// Check if instructing party (C/L) is present in any Sequence B transaction
448    fn has_instructing_party_in_any_seq_b(&self) -> bool {
449        self.transactions
450            .iter()
451            .any(|tx| tx.instructing_party_tx.is_some())
452    }
453
454    /// Check if field 21E is present in Sequence A
455    fn has_21e_in_seq_a(&self) -> bool {
456        self.field_21e.is_some()
457    }
458
459    /// Check if field 21E is present in any Sequence B transaction
460    fn has_21e_in_any_seq_b(&self) -> bool {
461        self.transactions.iter().any(|tx| tx.field_21e.is_some())
462    }
463
464    /// Check if field 26T is present in Sequence A
465    fn has_26t_in_seq_a(&self) -> bool {
466        self.field_26t.is_some()
467    }
468
469    /// Check if field 26T is present in any Sequence B transaction
470    fn has_26t_in_any_seq_b(&self) -> bool {
471        self.transactions.iter().any(|tx| tx.field_26t.is_some())
472    }
473
474    /// Check if field 52a is present in Sequence A
475    fn has_52a_in_seq_a(&self) -> bool {
476        self.field_52.is_some()
477    }
478
479    /// Check if field 52a is present in any Sequence B transaction
480    fn has_52a_in_any_seq_b(&self) -> bool {
481        self.transactions.iter().any(|tx| tx.field_52.is_some())
482    }
483
484    /// Check if field 71A is present in Sequence A
485    fn has_71a_in_seq_a(&self) -> bool {
486        self.field_71a.is_some()
487    }
488
489    /// Check if field 71A is present in any Sequence B transaction
490    fn has_71a_in_any_seq_b(&self) -> bool {
491        self.transactions.iter().any(|tx| tx.field_71a.is_some())
492    }
493
494    /// Check if field 77B is present in Sequence A
495    fn has_77b_in_seq_a(&self) -> bool {
496        self.field_77b.is_some()
497    }
498
499    /// Check if field 77B is present in any Sequence B transaction
500    fn has_77b_in_any_seq_b(&self) -> bool {
501        self.transactions.iter().any(|tx| tx.field_77b.is_some())
502    }
503
504    /// Check if field 71F is present in any Sequence B transaction
505    fn has_71f_in_any_seq_b(&self) -> bool {
506        self.transactions.iter().any(|tx| tx.field_71f.is_some())
507    }
508
509    /// Check if field 71F is present in Sequence C
510    fn has_71f_in_seq_c(&self) -> bool {
511        self.field_71f.is_some()
512    }
513
514    /// Check if field 71G is present in any Sequence B transaction
515    fn has_71g_in_any_seq_b(&self) -> bool {
516        self.transactions.iter().any(|tx| tx.field_71g.is_some())
517    }
518
519    /// Check if field 71G is present in Sequence C
520    fn has_71g_in_seq_c(&self) -> bool {
521        self.field_71g.is_some()
522    }
523
524    // ========================================================================
525    // VALIDATION RULES (C1-C13)
526    // ========================================================================
527
528    /// C1: Field 23E in Sequence A and Sequence B Dependencies (Error code: C75)
529    fn validate_c1_field_23e_dependencies(&self) -> Vec<SwiftValidationError> {
530        let mut errors = Vec::new();
531
532        if let Some(ref field_23e_a) = self.field_23e {
533            if field_23e_a.instruction_code == "RFDD" {
534                // Present and equal to RFDD → Mandatory in all occurrences of Seq B
535                for (idx, transaction) in self.transactions.iter().enumerate() {
536                    if transaction.field_23e.is_none() {
537                        errors.push(SwiftValidationError::content_error(
538                            "C75",
539                            "23E",
540                            "",
541                            &format!(
542                                "Transaction {}: Field 23E is mandatory in Sequence B when field 23E in Sequence A contains RFDD",
543                                idx + 1
544                            ),
545                            "If field 23E is present in sequence A and contains RFDD then field 23E must be present in all occurrences of sequence B",
546                        ));
547                    }
548                }
549            } else {
550                // Present and not equal to RFDD → Not allowed in Seq B
551                for (idx, transaction) in self.transactions.iter().enumerate() {
552                    if transaction.field_23e.is_some() {
553                        errors.push(SwiftValidationError::content_error(
554                            "C75",
555                            "23E",
556                            "",
557                            &format!(
558                                "Transaction {}: Field 23E must not be present in Sequence B when field 23E in Sequence A does not contain RFDD",
559                                idx + 1
560                            ),
561                            "If field 23E is present in sequence A and does not contain RFDD then field 23E must not be present in any occurrence of sequence B",
562                        ));
563                    }
564                }
565            }
566        } else {
567            // Not present in Seq A → Mandatory in all occurrences of Seq B
568            for (idx, transaction) in self.transactions.iter().enumerate() {
569                if transaction.field_23e.is_none() {
570                    errors.push(SwiftValidationError::content_error(
571                        "C75",
572                        "23E",
573                        "",
574                        &format!(
575                            "Transaction {}: Field 23E is mandatory in Sequence B when field 23E is not present in Sequence A",
576                            idx + 1
577                        ),
578                        "If field 23E is not present in sequence A then field 23E must be present in all occurrences of sequence B",
579                    ));
580                }
581            }
582        }
583
584        errors
585    }
586
587    /// C2: Creditor Field (Field 50a options A or K) (Error code: C76)
588    fn validate_c2_creditor_field(&self) -> Option<SwiftValidationError> {
589        let in_seq_a = self.has_creditor_in_seq_a();
590        let in_all_seq_b = self.has_creditor_in_all_seq_b();
591        let in_any_seq_b = self.has_creditor_in_any_seq_b();
592
593        if in_seq_a && in_any_seq_b {
594            // Present in both sequences - NOT ALLOWED
595            return Some(SwiftValidationError::content_error(
596                "C76",
597                "50a",
598                "",
599                "Field 50a (Creditor A/K) must not be present in both Sequence A and Sequence B",
600                "Field 50a (option A or K), must be present in either sequence A or in each occurrence of sequence B, but must never be present in both sequences",
601            ));
602        }
603
604        if !in_seq_a && !in_all_seq_b {
605            if in_any_seq_b {
606                // Present in some but not all Seq B transactions
607                return Some(SwiftValidationError::content_error(
608                    "C76",
609                    "50a",
610                    "",
611                    "Field 50a (Creditor A/K) must be present in every Sequence B transaction when not in Sequence A",
612                    "Field 50a (option A or K), must be present in each occurrence of sequence B when not present in sequence A",
613                ));
614            } else {
615                // Not present anywhere - NOT ALLOWED
616                return Some(SwiftValidationError::content_error(
617                    "C76",
618                    "50a",
619                    "",
620                    "Field 50a (Creditor A/K) must be present in either Sequence A or in every Sequence B transaction",
621                    "Field 50a (option A or K), must be present in either sequence A or in each occurrence of sequence B, but must never be absent from both sequences",
622                ));
623            }
624        }
625
626        None
627    }
628
629    /// C3: Mutual Exclusivity of Fields 21E, 26T, 52a, 71A, 77B, and 50a (C or L) (Error code: D73)
630    fn validate_c3_mutual_exclusivity(&self) -> Vec<SwiftValidationError> {
631        let mut errors = Vec::new();
632
633        // Check field 21E
634        if self.has_21e_in_seq_a() && self.has_21e_in_any_seq_b() {
635            errors.push(SwiftValidationError::content_error(
636                "D73",
637                "21E",
638                "",
639                "Field 21E must not be present in both Sequence A and Sequence B",
640                "When present in sequence A, field 21E must not be present in any occurrence of sequence B. When present in one or more occurrences of sequence B, field 21E must not be present in sequence A",
641            ));
642        }
643
644        // Check field 26T
645        if self.has_26t_in_seq_a() && self.has_26t_in_any_seq_b() {
646            errors.push(SwiftValidationError::content_error(
647                "D73",
648                "26T",
649                "",
650                "Field 26T must not be present in both Sequence A and Sequence B",
651                "When present in sequence A, field 26T must not be present in any occurrence of sequence B. When present in one or more occurrences of sequence B, field 26T must not be present in sequence A",
652            ));
653        }
654
655        // Check field 52a
656        if self.has_52a_in_seq_a() && self.has_52a_in_any_seq_b() {
657            errors.push(SwiftValidationError::content_error(
658                "D73",
659                "52a",
660                "",
661                "Field 52a must not be present in both Sequence A and Sequence B",
662                "When present in sequence A, field 52a must not be present in any occurrence of sequence B. When present in one or more occurrences of sequence B, field 52a must not be present in sequence A",
663            ));
664        }
665
666        // Check field 71A
667        if self.has_71a_in_seq_a() && self.has_71a_in_any_seq_b() {
668            errors.push(SwiftValidationError::content_error(
669                "D73",
670                "71A",
671                "",
672                "Field 71A must not be present in both Sequence A and Sequence B",
673                "When present in sequence A, field 71A must not be present in any occurrence of sequence B. When present in one or more occurrences of sequence B, field 71A must not be present in sequence A",
674            ));
675        }
676
677        // Check field 77B
678        if self.has_77b_in_seq_a() && self.has_77b_in_any_seq_b() {
679            errors.push(SwiftValidationError::content_error(
680                "D73",
681                "77B",
682                "",
683                "Field 77B must not be present in both Sequence A and Sequence B",
684                "When present in sequence A, field 77B must not be present in any occurrence of sequence B. When present in one or more occurrences of sequence B, field 77B must not be present in sequence A",
685            ));
686        }
687
688        // Check field 50a (C or L) - Instructing Party
689        if self.has_instructing_party_in_seq_a() && self.has_instructing_party_in_any_seq_b() {
690            errors.push(SwiftValidationError::content_error(
691                "D73",
692                "50a",
693                "",
694                "Field 50a (Instructing Party C/L) must not be present in both Sequence A and Sequence B",
695                "When present in sequence A, field 50a (option C or L) must not be present in any occurrence of sequence B. When present in one or more occurrences of sequence B, field 50a (option C or L) must not be present in sequence A",
696            ));
697        }
698
699        errors
700    }
701
702    /// C4: Registration Reference and Creditor Dependencies (Error code: D77)
703    fn validate_c4_registration_reference(&self) -> Vec<SwiftValidationError> {
704        let mut errors = Vec::new();
705
706        // Check Sequence A
707        if self.field_21e.is_some() && self.creditor.is_none() {
708            errors.push(SwiftValidationError::content_error(
709                "D77",
710                "50a",
711                "",
712                "Field 50a (Creditor A/K) is mandatory in Sequence A when field 21E is present",
713                "If field 21E is present in sequence A, field 50a (option A or K), must also be present in sequence A",
714            ));
715        }
716
717        // Check each occurrence of Sequence B
718        for (idx, transaction) in self.transactions.iter().enumerate() {
719            if transaction.field_21e.is_some() && transaction.creditor_tx.is_none() {
720                errors.push(SwiftValidationError::content_error(
721                    "D77",
722                    "50a",
723                    "",
724                    &format!(
725                        "Transaction {}: Field 50a (Creditor A/K) is mandatory when field 21E is present",
726                        idx + 1
727                    ),
728                    "In each occurrence of sequence B, if field 21E is present, then field 50a (option A or K), must also be present in the same occurrence",
729                ));
730            }
731        }
732
733        errors
734    }
735
736    /// C5: Field 72 and RTND Code Dependency (Error code: C82)
737    fn validate_c5_field_72_rtnd(&self) -> Option<SwiftValidationError> {
738        let has_rtnd = self.has_rtnd_in_seq_a();
739        let has_field_72 = self.field_72.is_some();
740
741        if has_rtnd && !has_field_72 {
742            return Some(SwiftValidationError::content_error(
743                "C82",
744                "72",
745                "",
746                "Field 72 is mandatory when field 23E in Sequence A contains RTND",
747                "In sequence A, if field 23E is present and contains RTND then field 72 must be present",
748            ));
749        }
750
751        if !has_rtnd && has_field_72 {
752            return Some(SwiftValidationError::content_error(
753                "C82",
754                "72",
755                "",
756                "Field 72 is not allowed when field 23E in Sequence A does not contain RTND or is not present",
757                "In sequence A, if field 23E not present, or field 23E does not contain RTND - field 72 is not allowed",
758            ));
759        }
760
761        None
762    }
763
764    /// C6: Charges Fields Dependencies Between Sequences B and C (Error code: D79)
765    fn validate_c6_charges_dependencies(&self) -> Vec<SwiftValidationError> {
766        let mut errors = Vec::new();
767
768        let has_71f_b = self.has_71f_in_any_seq_b();
769        let has_71f_c = self.has_71f_in_seq_c();
770        let has_71g_b = self.has_71g_in_any_seq_b();
771        let has_71g_c = self.has_71g_in_seq_c();
772
773        // Field 71F validation
774        if has_71f_b && !has_71f_c {
775            errors.push(SwiftValidationError::content_error(
776                "D79",
777                "71F",
778                "",
779                "Field 71F is mandatory in Sequence C when present in Sequence B",
780                "If field 71F is present in one or more occurrence of sequence B, then it must also be present in sequence C",
781            ));
782        }
783
784        if !has_71f_b && has_71f_c {
785            errors.push(SwiftValidationError::content_error(
786                "D79",
787                "71F",
788                "",
789                "Field 71F is not allowed in Sequence C when not present in Sequence B",
790                "If field 71F is not present in sequence B, then it must not be present in sequence C",
791            ));
792        }
793
794        // Field 71G validation
795        if has_71g_b && !has_71g_c {
796            errors.push(SwiftValidationError::content_error(
797                "D79",
798                "71G",
799                "",
800                "Field 71G is mandatory in Sequence C when present in Sequence B",
801                "If field 71G is present in one or more occurrence of sequence B, then it must also be present in sequence C",
802            ));
803        }
804
805        if !has_71g_b && has_71g_c {
806            errors.push(SwiftValidationError::content_error(
807                "D79",
808                "71G",
809                "",
810                "Field 71G is not allowed in Sequence C when not present in Sequence B",
811                "If field 71G is not present in sequence B, then it must not be present in sequence C",
812            ));
813        }
814
815        errors
816    }
817
818    /// C7: Fields 33B and 32B Currency/Amount Difference (Error code: D21)
819    fn validate_c7_currency_amount_difference(&self) -> Vec<SwiftValidationError> {
820        let mut errors = Vec::new();
821
822        for (idx, transaction) in self.transactions.iter().enumerate() {
823            if let Some(ref field_33b) = transaction.field_33b {
824                let currency_32b = &transaction.field_32b.currency;
825                let currency_33b = &field_33b.currency;
826                let amount_32b = transaction.field_32b.amount;
827                let amount_33b = field_33b.amount;
828
829                // Check if both currency and amount are the same
830                if currency_32b == currency_33b && (amount_32b - amount_33b).abs() < 0.01 {
831                    errors.push(SwiftValidationError::content_error(
832                        "D21",
833                        "33B",
834                        &format!("{}{}", currency_33b, amount_33b),
835                        &format!(
836                            "Transaction {}: Currency code or amount, or both, must be different between fields 33B and 32B",
837                            idx + 1
838                        ),
839                        "In each occurrence of sequence B, if field 33B is present then the currency code or the amount, or both, must be different between fields 33B and 32B",
840                    ));
841                }
842            }
843        }
844
845        errors
846    }
847
848    /// C8: Exchange Rate and Currency Dependency (Error code: D75)
849    fn validate_c8_exchange_rate(&self) -> Vec<SwiftValidationError> {
850        let mut errors = Vec::new();
851
852        for (idx, transaction) in self.transactions.iter().enumerate() {
853            if let Some(ref field_33b) = transaction.field_33b {
854                let currency_32b = &transaction.field_32b.currency;
855                let currency_33b = &field_33b.currency;
856
857                if currency_32b != currency_33b {
858                    // Different currencies → field 36 must be present
859                    if transaction.field_36.is_none() {
860                        errors.push(SwiftValidationError::content_error(
861                            "D75",
862                            "36",
863                            "",
864                            &format!(
865                                "Transaction {}: Field 36 (Exchange Rate) is mandatory when currencies in fields 32B and 33B are different",
866                                idx + 1
867                            ),
868                            "In any occurrence of sequence B, if field 33B is present and the currency codes in fields 32B and 33B are different, then field 36 must be present",
869                        ));
870                    }
871                } else {
872                    // Same currencies → field 36 must not be present
873                    if transaction.field_36.is_some() {
874                        errors.push(SwiftValidationError::content_error(
875                            "D75",
876                            "36",
877                            "",
878                            &format!(
879                                "Transaction {}: Field 36 (Exchange Rate) is not allowed when currencies in fields 32B and 33B are the same",
880                                idx + 1
881                            ),
882                            "If field 33B is present and the currency codes in fields 32B and 33B are the same, then field 36 must not be present",
883                        ));
884                    }
885                }
886            } else {
887                // Field 33B not present → field 36 must not be present
888                if transaction.field_36.is_some() {
889                    errors.push(SwiftValidationError::content_error(
890                        "D75",
891                        "36",
892                        "",
893                        &format!(
894                            "Transaction {}: Field 36 (Exchange Rate) is not allowed when field 33B is not present",
895                            idx + 1
896                        ),
897                        "Field 36 must not be present when field 33B is not present",
898                    ));
899                }
900            }
901        }
902
903        errors
904    }
905
906    /// C9: Field 19 and Settlement Amount Calculation (Error code: D80)
907    fn validate_c9_field_19(&self) -> Option<SwiftValidationError> {
908        if !self.has_sequence_c() {
909            return None; // Rule doesn't apply if Sequence C is not present
910        }
911
912        let field_32b_c = self.field_32b.as_ref()?;
913        let settlement_amount = field_32b_c.amount;
914
915        // Calculate sum of amounts in Sequence B
916        let sum_of_amounts: f64 = self.transactions.iter().map(|tx| tx.field_32b.amount).sum();
917
918        let amounts_equal = (settlement_amount - sum_of_amounts).abs() < 0.01;
919
920        if amounts_equal && self.field_19.is_some() {
921            return Some(SwiftValidationError::content_error(
922                "D80",
923                "19",
924                "",
925                "Field 19 must not be present when amount in field 32B of Sequence C equals the sum of amounts in field 32B of Sequence B",
926                "If sequence C is present and if the amount in field 32B of sequence C is equal to the sum of the amounts of the fields 32B of sequence B, then field 19 must not be present",
927            ));
928        }
929
930        if !amounts_equal && self.field_19.is_none() {
931            return Some(SwiftValidationError::content_error(
932                "D80",
933                "19",
934                "",
935                "Field 19 must be present when amount in field 32B of Sequence C is not equal to the sum of amounts in field 32B of Sequence B",
936                "If the amount in field 32B of sequence C is not equal to the sum of the amounts of the fields 32B of sequence B, then field 19 must be present",
937            ));
938        }
939
940        None
941    }
942
943    /// C10: Field 19 Amount Validation (Error code: C01)
944    fn validate_c10_field_19_amount(&self) -> Option<SwiftValidationError> {
945        if let Some(ref field_19) = self.field_19 {
946            // Calculate sum of amounts in Sequence B
947            let sum_of_amounts: f64 = self.transactions.iter().map(|tx| tx.field_32b.amount).sum();
948
949            if (field_19.amount - sum_of_amounts).abs() > 0.01 {
950                return Some(SwiftValidationError::content_error(
951                    "C01",
952                    "19",
953                    &field_19.amount.to_string(),
954                    &format!(
955                        "Field 19 amount ({}) must equal the sum of amounts in all occurrences of field 32B in Sequence B ({})",
956                        field_19.amount, sum_of_amounts
957                    ),
958                    "If field 19 is present in sequence C then it must be equal to the sum of the amounts in all occurrences of field 32B in sequence B",
959                ));
960            }
961        }
962
963        None
964    }
965
966    /// C11: Currency Code Consistency (Error code: C02)
967    fn validate_c11_currency_consistency(&self) -> Vec<SwiftValidationError> {
968        let mut errors = Vec::new();
969
970        // Collect all 32B currencies from Sequence B
971        let mut currencies_32b: Vec<&String> = self
972            .transactions
973            .iter()
974            .map(|tx| &tx.field_32b.currency)
975            .collect();
976
977        // Add 32B currency from Sequence C if present
978        if let Some(ref field_32b_c) = self.field_32b {
979            currencies_32b.push(&field_32b_c.currency);
980        }
981
982        // Check if all 32B currencies are the same
983        if !currencies_32b.is_empty() {
984            let first_currency_32b = currencies_32b[0];
985            for currency in currencies_32b.iter().skip(1) {
986                if *currency != first_currency_32b {
987                    errors.push(SwiftValidationError::content_error(
988                        "C02",
989                        "32B",
990                        currency,
991                        &format!(
992                            "Currency code in field 32B ({}) must be the same for all occurrences in the message (expected: {})",
993                            currency, first_currency_32b
994                        ),
995                        "The currency code in fields 32B must be the same for all occurrences of these fields in the message",
996                    ));
997                    break; // Report only once
998                }
999            }
1000        }
1001
1002        // Collect all 71G currencies from Sequence B
1003        let mut currencies_71g: Vec<&String> = self
1004            .transactions
1005            .iter()
1006            .filter_map(|tx| tx.field_71g.as_ref().map(|f| &f.currency))
1007            .collect();
1008
1009        // Add 71G currency from Sequence C if present
1010        if let Some(ref field_71g_c) = self.field_71g {
1011            currencies_71g.push(&field_71g_c.currency);
1012        }
1013
1014        // Check if all 71G currencies are the same
1015        if !currencies_71g.is_empty() {
1016            let first_currency_71g = currencies_71g[0];
1017            for currency in currencies_71g.iter().skip(1) {
1018                if *currency != first_currency_71g {
1019                    errors.push(SwiftValidationError::content_error(
1020                        "C02",
1021                        "71G",
1022                        currency,
1023                        &format!(
1024                            "Currency code in field 71G ({}) must be the same for all occurrences in the message (expected: {})",
1025                            currency, first_currency_71g
1026                        ),
1027                        "The currency code in field 71G in sequences B and C must be the same for all occurrences of these fields in the message",
1028                    ));
1029                    break; // Report only once
1030                }
1031            }
1032        }
1033
1034        // Collect all 71F currencies from Sequence B
1035        let mut currencies_71f: Vec<&String> = self
1036            .transactions
1037            .iter()
1038            .filter_map(|tx| tx.field_71f.as_ref().map(|f| &f.currency))
1039            .collect();
1040
1041        // Add 71F currency from Sequence C if present
1042        if let Some(ref field_71f_c) = self.field_71f {
1043            currencies_71f.push(&field_71f_c.currency);
1044        }
1045
1046        // Check if all 71F currencies are the same
1047        if !currencies_71f.is_empty() {
1048            let first_currency_71f = currencies_71f[0];
1049            for currency in currencies_71f.iter().skip(1) {
1050                if *currency != first_currency_71f {
1051                    errors.push(SwiftValidationError::content_error(
1052                        "C02",
1053                        "71F",
1054                        currency,
1055                        &format!(
1056                            "Currency code in field 71F ({}) must be the same for all occurrences in the message (expected: {})",
1057                            currency, first_currency_71f
1058                        ),
1059                        "The currency code in the charges fields 71F (in sequences B and C) must be the same for all occurrences of these fields in the message",
1060                    ));
1061                    break; // Report only once
1062                }
1063            }
1064        }
1065
1066        errors
1067    }
1068
1069    /// C12: Request for Direct Debit (RFDD) Comprehensive Rules (Error code: C96)
1070    fn validate_c12_rfdd_comprehensive(&self) -> Vec<SwiftValidationError> {
1071        let mut errors = Vec::new();
1072        let has_rfdd = self.has_rfdd_in_seq_a();
1073
1074        if has_rfdd {
1075            // Field 23E = RFDD in Sequence A
1076            // In Sequence B: 21E, 50a (A/K), 52a, 71F, 71G must NOT be present
1077            for (idx, transaction) in self.transactions.iter().enumerate() {
1078                if transaction.field_21e.is_some() {
1079                    errors.push(SwiftValidationError::content_error(
1080                        "C96",
1081                        "21E",
1082                        "",
1083                        &format!(
1084                            "Transaction {}: Field 21E is not allowed in Sequence B when field 23E in Sequence A contains RFDD",
1085                            idx + 1
1086                        ),
1087                        "In sequence A, if field 23E is present and contains RFDD, then in sequence B the fields 21E, 50a (option A or K), 52a, 71F, 71G must not be present",
1088                    ));
1089                }
1090
1091                if transaction.creditor_tx.is_some() {
1092                    errors.push(SwiftValidationError::content_error(
1093                        "C96",
1094                        "50a",
1095                        "",
1096                        &format!(
1097                            "Transaction {}: Field 50a (Creditor A/K) is not allowed in Sequence B when field 23E in Sequence A contains RFDD",
1098                            idx + 1
1099                        ),
1100                        "In sequence A, if field 23E is present and contains RFDD, then in sequence B the fields 21E, 50a (option A or K), 52a, 71F, 71G must not be present",
1101                    ));
1102                }
1103
1104                if transaction.field_52.is_some() {
1105                    errors.push(SwiftValidationError::content_error(
1106                        "C96",
1107                        "52a",
1108                        "",
1109                        &format!(
1110                            "Transaction {}: Field 52a is not allowed in Sequence B when field 23E in Sequence A contains RFDD",
1111                            idx + 1
1112                        ),
1113                        "In sequence A, if field 23E is present and contains RFDD, then in sequence B the fields 21E, 50a (option A or K), 52a, 71F, 71G must not be present",
1114                    ));
1115                }
1116
1117                if transaction.field_71f.is_some() {
1118                    errors.push(SwiftValidationError::content_error(
1119                        "C96",
1120                        "71F",
1121                        "",
1122                        &format!(
1123                            "Transaction {}: Field 71F is not allowed in Sequence B when field 23E in Sequence A contains RFDD",
1124                            idx + 1
1125                        ),
1126                        "In sequence A, if field 23E is present and contains RFDD, then in sequence B the fields 21E, 50a (option A or K), 52a, 71F, 71G must not be present",
1127                    ));
1128                }
1129
1130                if transaction.field_71g.is_some() {
1131                    errors.push(SwiftValidationError::content_error(
1132                        "C96",
1133                        "71G",
1134                        "",
1135                        &format!(
1136                            "Transaction {}: Field 71G is not allowed in Sequence B when field 23E in Sequence A contains RFDD",
1137                            idx + 1
1138                        ),
1139                        "In sequence A, if field 23E is present and contains RFDD, then in sequence B the fields 21E, 50a (option A or K), 52a, 71F, 71G must not be present",
1140                    ));
1141                }
1142            }
1143
1144            // Sequence C must NOT be present
1145            if self.has_sequence_c() {
1146                errors.push(SwiftValidationError::content_error(
1147                    "C96",
1148                    "32B",
1149                    "",
1150                    "Sequence C is not allowed when field 23E in Sequence A contains RFDD",
1151                    "In sequence A, if field 23E is present and contains RFDD, then sequence C must not be present",
1152                ));
1153            }
1154        } else {
1155            // Field 23E does NOT contain RFDD or is not present
1156            // Field 21R must NOT be present in Sequence A
1157            if self.field_21r.is_some() {
1158                errors.push(SwiftValidationError::content_error(
1159                    "C96",
1160                    "21R",
1161                    "",
1162                    "Field 21R is not allowed in Sequence A when field 23E does not contain RFDD or is not present",
1163                    "In sequence A field 23E does not contain RFDD or field 23E is not present, in sequence A field 21R must not be present",
1164                ));
1165            }
1166
1167            // Sequence C must be present
1168            if !self.has_sequence_c() {
1169                errors.push(SwiftValidationError::content_error(
1170                    "C96",
1171                    "32B",
1172                    "",
1173                    "Sequence C is mandatory when field 23E in Sequence A does not contain RFDD or is not present",
1174                    "In sequence A field 23E does not contain RFDD or field 23E is not present, sequence C must be present",
1175                ));
1176            }
1177        }
1178
1179        errors
1180    }
1181
1182    /// Validate Field 23E instruction codes in Sequence A (Error codes: T47, D81)
1183    fn validate_field_23e_seq_a(&self) -> Vec<SwiftValidationError> {
1184        let mut errors = Vec::new();
1185
1186        if let Some(ref field_23e) = self.field_23e {
1187            let code = &field_23e.instruction_code;
1188
1189            // T47: Validate instruction code is in allowed list for Sequence A
1190            if !Self::MT104_VALID_23E_CODES_SEQ_A.contains(&code.as_str()) {
1191                errors.push(SwiftValidationError::format_error(
1192                    "T47",
1193                    "23E",
1194                    code,
1195                    &format!("One of: {}", Self::MT104_VALID_23E_CODES_SEQ_A.join(", ")),
1196                    &format!(
1197                        "Sequence A: Instruction code '{}' is not valid for MT104. Valid codes: {}",
1198                        code,
1199                        Self::MT104_VALID_23E_CODES_SEQ_A.join(", ")
1200                    ),
1201                ));
1202            }
1203
1204            // D81: Additional information only allowed for OTHR
1205            if field_23e.additional_info.is_some() && code != Self::CODE_WITH_ADDITIONAL_INFO {
1206                errors.push(SwiftValidationError::content_error(
1207                    "D81",
1208                    "23E",
1209                    code,
1210                    &format!(
1211                        "Sequence A: Additional information is only allowed for code OTHR. Code '{}' does not allow additional information",
1212                        code
1213                    ),
1214                    "The narrative second subfield can only be used in combination with OTHR",
1215                ));
1216            }
1217        }
1218
1219        errors
1220    }
1221
1222    /// Validate Field 23E instruction codes in Sequence B (Error codes: T47, D81)
1223    fn validate_field_23e_seq_b(&self) -> Vec<SwiftValidationError> {
1224        let mut errors = Vec::new();
1225
1226        for (idx, transaction) in self.transactions.iter().enumerate() {
1227            if let Some(ref field_23e) = transaction.field_23e {
1228                let code = &field_23e.instruction_code;
1229
1230                // T47: Validate instruction code is in allowed list for Sequence B
1231                if !Self::MT104_VALID_23E_CODES_SEQ_B.contains(&code.as_str()) {
1232                    errors.push(SwiftValidationError::format_error(
1233                        "T47",
1234                        "23E",
1235                        code,
1236                        &format!("One of: {}", Self::MT104_VALID_23E_CODES_SEQ_B.join(", ")),
1237                        &format!(
1238                            "Transaction {}: Instruction code '{}' is not valid for MT104 Sequence B. Valid codes: {}",
1239                            idx + 1,
1240                            code,
1241                            Self::MT104_VALID_23E_CODES_SEQ_B.join(", ")
1242                        ),
1243                    ));
1244                }
1245
1246                // D81: Additional information only allowed for OTHR
1247                if field_23e.additional_info.is_some() && code != Self::CODE_WITH_ADDITIONAL_INFO {
1248                    errors.push(SwiftValidationError::content_error(
1249                        "D81",
1250                        "23E",
1251                        code,
1252                        &format!(
1253                            "Transaction {}: Additional information is only allowed for code OTHR. Code '{}' does not allow additional information",
1254                            idx + 1,
1255                            code
1256                        ),
1257                        "The narrative second subfield can only be used in combination with OTHR",
1258                    ));
1259                }
1260            }
1261        }
1262
1263        errors
1264    }
1265
1266    /// Main validation method - validates all network rules
1267    /// Returns array of validation errors, respects stop_on_first_error flag
1268    pub fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
1269        let mut all_errors = Vec::new();
1270
1271        // C1: Field 23E Dependencies
1272        let c1_errors = self.validate_c1_field_23e_dependencies();
1273        all_errors.extend(c1_errors);
1274        if stop_on_first_error && !all_errors.is_empty() {
1275            return all_errors;
1276        }
1277
1278        // C2: Creditor Field
1279        if let Some(error) = self.validate_c2_creditor_field() {
1280            all_errors.push(error);
1281            if stop_on_first_error {
1282                return all_errors;
1283            }
1284        }
1285
1286        // C3: Mutual Exclusivity
1287        let c3_errors = self.validate_c3_mutual_exclusivity();
1288        all_errors.extend(c3_errors);
1289        if stop_on_first_error && !all_errors.is_empty() {
1290            return all_errors;
1291        }
1292
1293        // C4: Registration Reference
1294        let c4_errors = self.validate_c4_registration_reference();
1295        all_errors.extend(c4_errors);
1296        if stop_on_first_error && !all_errors.is_empty() {
1297            return all_errors;
1298        }
1299
1300        // C5: Field 72 and RTND
1301        if let Some(error) = self.validate_c5_field_72_rtnd() {
1302            all_errors.push(error);
1303            if stop_on_first_error {
1304                return all_errors;
1305            }
1306        }
1307
1308        // C6: Charges Dependencies
1309        let c6_errors = self.validate_c6_charges_dependencies();
1310        all_errors.extend(c6_errors);
1311        if stop_on_first_error && !all_errors.is_empty() {
1312            return all_errors;
1313        }
1314
1315        // C7: Currency/Amount Difference
1316        let c7_errors = self.validate_c7_currency_amount_difference();
1317        all_errors.extend(c7_errors);
1318        if stop_on_first_error && !all_errors.is_empty() {
1319            return all_errors;
1320        }
1321
1322        // C8: Exchange Rate
1323        let c8_errors = self.validate_c8_exchange_rate();
1324        all_errors.extend(c8_errors);
1325        if stop_on_first_error && !all_errors.is_empty() {
1326            return all_errors;
1327        }
1328
1329        // C9: Field 19
1330        if let Some(error) = self.validate_c9_field_19() {
1331            all_errors.push(error);
1332            if stop_on_first_error {
1333                return all_errors;
1334            }
1335        }
1336
1337        // C10: Field 19 Amount
1338        if let Some(error) = self.validate_c10_field_19_amount() {
1339            all_errors.push(error);
1340            if stop_on_first_error {
1341                return all_errors;
1342            }
1343        }
1344
1345        // C11: Currency Consistency
1346        let c11_errors = self.validate_c11_currency_consistency();
1347        all_errors.extend(c11_errors);
1348        if stop_on_first_error && !all_errors.is_empty() {
1349            return all_errors;
1350        }
1351
1352        // C12: RFDD Comprehensive Rules
1353        let c12_errors = self.validate_c12_rfdd_comprehensive();
1354        all_errors.extend(c12_errors);
1355        if stop_on_first_error && !all_errors.is_empty() {
1356            return all_errors;
1357        }
1358
1359        // Field 23E Validation - Sequence A
1360        let f23e_a_errors = self.validate_field_23e_seq_a();
1361        all_errors.extend(f23e_a_errors);
1362        if stop_on_first_error && !all_errors.is_empty() {
1363            return all_errors;
1364        }
1365
1366        // Field 23E Validation - Sequence B
1367        let f23e_b_errors = self.validate_field_23e_seq_b();
1368        all_errors.extend(f23e_b_errors);
1369
1370        all_errors
1371    }
1372}
1373
1374impl crate::traits::SwiftMessageBody for MT104 {
1375    fn message_type() -> &'static str {
1376        "104"
1377    }
1378
1379    fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
1380        // Call the existing public method implementation
1381        MT104::parse_from_block4(block4)
1382    }
1383
1384    fn to_mt_string(&self) -> String {
1385        // Call the existing public method implementation
1386        MT104::to_mt_string(self)
1387    }
1388
1389    fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
1390        // Call the existing public method implementation
1391        MT104::validate_network_rules(self, stop_on_first_error)
1392    }
1393}