swift_mt_message/messages/
mt205.rs1use crate::fields::*;
2use serde::{Deserialize, Serialize};
3use swift_mt_message_macros::{SwiftMessage, serde_swift_fields};
4
5#[serde_swift_fields]
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, SwiftMessage)]
28#[validation_rules(MT205_VALIDATION_RULES)]
29pub struct MT205 {
30 #[field("20", mandatory)]
32 pub field_20: GenericReferenceField, #[field("21", mandatory)]
35 pub field_21: GenericReferenceField, #[field("32A", mandatory)]
38 pub field_32a: Field32A, #[field("52A", mandatory)]
41 pub field_52a: GenericBicField, #[field("58A", mandatory)]
44 pub field_58a: GenericBicField, #[field("13C", optional)]
48 pub field_13c: Option<Vec<Field13C>>, #[field("53A", optional)]
51 pub field_53a: Option<GenericBicField>, #[field("56A", optional)]
54 pub field_56a: Option<GenericBicField>, #[field("57A", optional)]
57 pub field_57a: Option<GenericBicField>, #[field("72", optional)]
60 pub field_72: Option<GenericMultiLine6x35>, #[field("50A", optional)]
64 pub field_50a: Option<Field50>, #[field("52A_SEQ_B", optional)]
67 pub field_52a_seq_b: Option<GenericBicField>, #[field("56A_SEQ_B", optional)]
70 pub field_56a_seq_b: Option<GenericBicField>, #[field("57A_SEQ_B", optional)]
73 pub field_57a_seq_b: Option<GenericBicField>, #[field("59A", optional)]
76 pub field_59a: Option<GenericBicField>, #[field("70", optional)]
79 pub field_70: Option<GenericMultiLine4x35>, #[field("72_SEQ_B", optional)]
82 pub field_72_seq_b: Option<GenericMultiLine6x35>, #[field("33B", optional)]
85 pub field_33b: Option<GenericCurrencyAmountField>, }
87
88impl MT205 {
89 pub fn has_reject_codes(&self) -> bool {
96 if self.field_20.value.to_uppercase().contains("REJT") {
98 return true;
99 }
100
101 if let Some(field_72) = &self.field_72 {
103 let content = field_72.lines.join(" ").to_uppercase();
104 if content.contains("/REJT/") || content.contains("REJT") {
105 return true;
106 }
107 }
108
109 false
110 }
111
112 pub fn has_return_codes(&self) -> bool {
119 if self.field_20.value.to_uppercase().contains("RETN") {
121 return true;
122 }
123
124 if let Some(field_72) = &self.field_72 {
126 let content = field_72.lines.join(" ").to_uppercase();
127 if content.contains("/RETN/") || content.contains("RETN") {
128 return true;
129 }
130 }
131
132 false
133 }
134
135 pub fn is_cover_message(&self) -> bool {
143 self.field_50a.is_some() || self.field_59a.is_some()
146 }
147}
148
149const MT205_VALIDATION_RULES: &str = r#"{
151 "rules": [
152 {
153 "id": "C1",
154 "description": "Transaction Reference (20) must not start or end with '/' and must not contain '//'",
155 "condition": {
156 "and": [
157 {"!": {"matches": [{"var": "field_20.value"}, "^/"]}},
158 {"!": {"matches": [{"var": "field_20.value"}, "/$"]}},
159 {"!": {"matches": [{"var": "field_20.value"}, "//"]}}
160 ]
161 }
162 },
163 {
164 "id": "C2",
165 "description": "Related Reference (21) must not start or end with '/' and must not contain '//'",
166 "condition": {
167 "and": [
168 {"!": {"matches": [{"var": "field_21.value"}, "^/"]}},
169 {"!": {"matches": [{"var": "field_21.value"}, "/$"]}},
170 {"!": {"matches": [{"var": "field_21.value"}, "//"]}}
171 ]
172 }
173 },
174 {
175 "id": "C3",
176 "description": "Field 52a is mandatory in MT205 (no fallback to sender BIC)",
177 "condition": {
178 "!=": [{"var": "field_52a.bic"}, ""]
179 }
180 },
181 {
182 "id": "C4",
183 "description": "Field 54a is not present in MT205 (structural difference from MT202)",
184 "condition": true
185 },
186 {
187 "id": "C5",
188 "description": "Cover message detection based on Sequence B customer fields presence",
189 "condition": {
190 "if": [
191 {"or": [
192 {"var": "field_50a.is_some"},
193 {"var": "field_59a.is_some"},
194 {"var": "field_70.is_some"}
195 ]},
196 {"var": "field_52a_seq_b.is_some"},
197 true
198 ]
199 }
200 },
201 {
202 "id": "C6",
203 "description": "Cross-currency validation: if 33B present, currency should differ from 32A",
204 "condition": {
205 "if": [
206 {"var": "field_33b.is_some"},
207 {"!=": [{"var": "field_33b.currency"}, {"var": "field_32a.currency"}]},
208 true
209 ]
210 }
211 },
212 {
213 "id": "C7",
214 "description": "REJT/RETN indicator validation in field 72",
215 "condition": {
216 "if": [
217 {"var": "field_72.is_some"},
218 {"or": [
219 {"!": {"matches": [{"var": "field_72.lines"}, "/REJT/"]}},
220 {"!": {"matches": [{"var": "field_72.lines"}, "/RETN/"]}},
221 true
222 ]},
223 true
224 ]
225 }
226 },
227 {
228 "id": "C8",
229 "description": "Time indication validation for CLS/TARGET timing",
230 "condition": {
231 "if": [
232 {"var": "field_13c.is_some"},
233 {"allValid": [
234 {"var": "field_13c"},
235 {"matches": [{"var": "time_code"}, "^(SNDTIME|RNCTIME|CLSTIME|TILTIME|FROTIME|REJTIME)$"]}
236 ]},
237 true
238 ]
239 }
240 },
241 {
242 "id": "C9",
243 "description": "Settlement method determination (METAFCT003 - simplified scenarios)",
244 "condition": {
245 "if": [
246 {"var": "field_53a.is_some"},
247 {"!=": [{"var": "field_53a.bic"}, {"var": "field_52a.bic"}]},
248 true
249 ]
250 }
251 }
252 ]
253}"#;