swift_mt_message/messages/
mt202.rs

1use crate::fields::*;
2use serde::{Deserialize, Serialize};
3use swift_mt_message_macros::{SwiftMessage, serde_swift_fields};
4
5/// # MT202 Sequence B: Underlying Customer Credit Transfer Details (COV variant)
6///
7/// This sequence contains the underlying customer credit transfer details
8/// and is present only in MT202 COV messages.
9#[serde_swift_fields]
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, SwiftMessage)]
11#[validation_rules(MT202_SEQUENCE_B_VALIDATION_RULES)]
12pub struct MT202SequenceB {
13    // Mandatory Fields for COV Sequence B
14    #[field("50A", mandatory)]
15    pub field_50a: Field50,
16
17    #[field("59A", mandatory)]
18    pub field_59a: Field59,
19
20    // Optional Fields for COV Sequence B
21    #[field("52A", optional)]
22    pub field_52a: Option<GenericBicField>,
23
24    #[field("52D", optional)]
25    pub field_52d: Option<GenericNameAddressField>,
26
27    #[field("56A", optional)]
28    pub field_56a: Option<GenericBicField>,
29
30    #[field("56C", optional)]
31    pub field_56c: Option<GenericAccountField>,
32
33    #[field("56D", optional)]
34    pub field_56d: Option<GenericNameAddressField>,
35
36    #[field("57A", optional)]
37    pub field_57a: Option<GenericBicField>,
38
39    #[field("57B", optional)]
40    pub field_57b: Option<GenericPartyField>,
41
42    #[field("57C", optional)]
43    pub field_57c: Option<GenericAccountField>,
44
45    #[field("57D", optional)]
46    pub field_57d: Option<GenericNameAddressField>,
47
48    #[field("70", optional)]
49    pub field_70: Option<GenericMultiLine4x35>,
50
51    #[field("72", optional)]
52    pub field_72: Option<GenericMultiLine6x35>,
53
54    #[field("33B", optional)]
55    pub field_33b: Option<GenericCurrencyAmountField>,
56}
57
58const MT202_SEQUENCE_B_VALIDATION_RULES: &str = r#"{
59  "rules": [
60    {
61      "id": "C1",
62      "description": "If 56a is present, 57a becomes mandatory",
63      "condition": true
64    }
65  ]
66}"#;
67
68/// # MT202: General Financial Institution Transfer (Standard and COV variants)
69///
70/// Unified structure supporting both standard MT202 and MT202 COV variants.
71/// Use `is_cover_message()` to check if the message is a COV variant.
72#[serde_swift_fields]
73#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, SwiftMessage)]
74#[validation_rules(MT202_VALIDATION_RULES)]
75pub struct MT202 {
76    // Mandatory Fields
77    #[field("20", mandatory)]
78    pub field_20: GenericReferenceField,
79
80    #[field("21", mandatory)]
81    pub field_21: GenericReferenceField,
82
83    #[field("32A", mandatory)]
84    pub field_32a: Field32A,
85
86    #[field("58A", mandatory)]
87    pub field_58a: GenericBicField,
88
89    // Optional Fields
90    #[field("13C", optional)]
91    pub field_13c: Option<Vec<Field13C>>,
92
93    #[field("52A", optional)]
94    pub field_52a: Option<GenericBicField>,
95
96    #[field("52D", optional)]
97    pub field_52d: Option<GenericNameAddressField>,
98
99    #[field("53A", optional)]
100    pub field_53a: Option<GenericBicField>,
101
102    #[field("53B", optional)]
103    pub field_53b: Option<GenericPartyField>,
104
105    #[field("53D", optional)]
106    pub field_53d: Option<GenericNameAddressField>,
107
108    #[field("54A", optional)]
109    pub field_54a: Option<GenericBicField>,
110
111    #[field("54B", optional)]
112    pub field_54b: Option<GenericPartyField>,
113
114    #[field("54D", optional)]
115    pub field_54d: Option<GenericNameAddressField>,
116
117    #[field("56A", optional)]
118    pub field_56a: Option<GenericBicField>,
119
120    #[field("56D", optional)]
121    pub field_56d: Option<GenericNameAddressField>,
122
123    #[field("57A", optional)]
124    pub field_57a: Option<GenericBicField>,
125
126    #[field("57B", optional)]
127    pub field_57b: Option<GenericPartyField>,
128
129    #[field("57D", optional)]
130    pub field_57d: Option<GenericNameAddressField>,
131
132    #[field("72", optional)]
133    pub field_72: Option<GenericMultiLine6x35>,
134
135    // Sequence B: Underlying Customer Credit Transfer Details (COV variant)
136    // This entire section is optional and present only in MT202 COV messages
137    #[field("SEQUENCE_B", optional)]
138    pub sequence_b: Option<MT202SequenceB>,
139}
140
141impl MT202 {
142    /// Check if this MT202 message contains reject codes
143    ///
144    /// Reject messages are identified by checking:
145    /// 1. Field 20 (Transaction Reference) for "REJT" prefix or content
146    /// 2. Field 72 (Sender to Receiver Information) containing `/REJT/` codes
147    /// 3. Additional structured reject information in field 72
148    pub fn has_reject_codes(&self) -> bool {
149        // Check field 20 (transaction reference)
150        if self.field_20.value.to_uppercase().contains("REJT") {
151            return true;
152        }
153
154        // Check field 72 for structured reject codes
155        if let Some(field_72) = &self.field_72 {
156            let content = field_72.lines.join(" ").to_uppercase();
157            if content.contains("/REJT/") || content.contains("REJT") {
158                return true;
159            }
160        }
161
162        false
163    }
164
165    /// Check if this MT202 message contains return codes
166    ///
167    /// Return messages are identified by checking:
168    /// 1. Field 20 (Transaction Reference) for "RETN" prefix or content
169    /// 2. Field 72 (Sender to Receiver Information) containing `/RETN/` codes
170    /// 3. Additional structured return information in field 72
171    pub fn has_return_codes(&self) -> bool {
172        // Check field 20 (transaction reference)
173        if self.field_20.value.to_uppercase().contains("RETN") {
174            return true;
175        }
176
177        // Check field 72 for structured return codes
178        if let Some(field_72) = &self.field_72 {
179            let content = field_72.lines.join(" ").to_uppercase();
180            if content.contains("/RETN/") || content.contains("RETN") {
181                return true;
182            }
183        }
184
185        false
186    }
187
188    /// Check if this MT202 message is a Cover (COV) message
189    ///
190    /// COV messages are distinguished by:
191    /// - Presence of Sequence B section for underlying customer credit transfer details
192    /// - Field 121 (UETR) in Block 3 is typically mandatory for COV messages
193    pub fn is_cover_message(&self) -> bool {
194        // The key distinguishing feature of COV is the presence of Sequence B
195        self.sequence_b.is_some()
196    }
197}
198
199const MT202_VALIDATION_RULES: &str = r#"{
200  "rules": [
201    {
202      "id": "C1",
203      "description": "If 56a is present, 57a becomes mandatory",
204      "condition": {
205        "if": [
206          {"or": [
207            {"var": "field_56a.is_some"},
208            {"var": "field_56d.is_some"}
209          ]},
210          {"or": [
211            {"var": "field_57a.is_some"},
212            {"var": "field_57b.is_some"},
213            {"var": "field_57d.is_some"}
214          ]},
215          true
216        ]
217      }
218    }
219  ],
220  "constants": {
221    "VALID_TIME_CODES": ["CLS", "RNC", "SND"],
222    "VALID_INSTRUCTION_CODES": ["/INT/", "/COV/", "/REIMBURSEMENT/", "/SETTLEMENT/", "/SDVA/", "/RETN/", "/REJT/"]
223  }
224}"#;