swift_mt_message/messages/
mt942.rs1use crate::errors::SwiftValidationError;
2use crate::fields::*;
3use crate::parser::utils::*;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
13pub struct MT942 {
14 #[serde(rename = "20")]
16 pub field_20: Field20,
17
18 #[serde(rename = "21", skip_serializing_if = "Option::is_none")]
20 pub field_21: Option<Field21NoOption>,
21
22 #[serde(rename = "25")]
24 pub field_25: Field25AccountIdentification,
25
26 #[serde(rename = "28C")]
28 pub field_28c: Field28C,
29
30 #[serde(rename = "34F_debit")]
32 pub floor_limit_debit: Field34F,
33
34 #[serde(rename = "34F_credit", skip_serializing_if = "Option::is_none")]
36 pub floor_limit_credit: Option<Field34F>,
37
38 #[serde(rename = "13D")]
40 pub field_13d: Field13D,
41
42 #[serde(rename = "statement_lines")]
44 pub statement_lines: Vec<MT942StatementLine>,
45
46 #[serde(rename = "90D", skip_serializing_if = "Option::is_none")]
48 pub field_90d: Option<Field90D>,
49
50 #[serde(rename = "90C", skip_serializing_if = "Option::is_none")]
52 pub field_90c: Option<Field90C>,
53
54 #[serde(rename = "86", skip_serializing_if = "Option::is_none")]
56 pub field_86: Option<Field86>,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
61pub struct MT942StatementLine {
62 #[serde(rename = "61")]
64 pub field_61: Field61,
65
66 #[serde(rename = "86", skip_serializing_if = "Option::is_none")]
68 pub field_86: Option<Field86>,
69}
70
71impl MT942 {
72 pub fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
74 let mut parser = crate::parser::MessageParser::new(block4, "942");
75
76 let field_13d_early = if parser.detect_field("13D") {
81 Some(parser.parse_field::<Field13D>("13D")?)
82 } else {
83 None
84 };
85
86 let field_20 = parser.parse_field::<Field20>("20")?;
88 let field_21 = parser.parse_optional_field::<Field21NoOption>("21")?;
89 let field_25 = parser.parse_field::<Field25AccountIdentification>("25")?;
90 let field_28c = parser.parse_field::<Field28C>("28C")?;
91
92 let floor_limit_debit = parser.parse_field::<Field34F>("34F")?;
94 let floor_limit_credit = parser.parse_optional_field::<Field34F>("34F")?;
95
96 let field_13d = if let Some(early_13d) = field_13d_early {
98 early_13d
99 } else {
100 parser.parse_field::<Field13D>("13D")?
101 };
102
103 parser = parser.with_duplicates(true);
105
106 let mut statement_lines = Vec::new();
108
109 while parser.detect_field("61") {
110 let field_61 = parser.parse_field::<Field61>("61")?;
111 let field_86 = parser.parse_optional_field::<Field86>("86")?;
112
113 statement_lines.push(MT942StatementLine { field_61, field_86 });
114 }
115
116 let field_90d = parser.parse_optional_field::<Field90D>("90D")?;
118 let field_90c = parser.parse_optional_field::<Field90C>("90C")?;
119
120 let field_86 = parser.parse_optional_field::<Field86>("86")?;
122
123 Ok(MT942 {
124 field_20,
125 field_21,
126 field_25,
127 field_28c,
128 floor_limit_debit,
129 floor_limit_credit,
130 field_13d,
131 statement_lines,
132 field_90d,
133 field_90c,
134 field_86,
135 })
136 }
137
138 fn get_base_currency(&self) -> &str {
148 &self.floor_limit_debit.currency[0..2]
149 }
150
151 fn validate_c1_currency_consistency(&self) -> Vec<SwiftValidationError> {
159 let mut errors = Vec::new();
160 let base_currency = self.get_base_currency();
161
162 if let Some(ref floor_limit_credit) = self.floor_limit_credit {
164 let credit_currency = &floor_limit_credit.currency[0..2];
165 if credit_currency != base_currency {
166 errors.push(SwiftValidationError::content_error(
167 "C27",
168 "34F",
169 &floor_limit_credit.currency,
170 &format!(
171 "Currency code in second field 34F ({}) must match first field 34F ({}). First two characters must be the same for all currency fields",
172 credit_currency, base_currency
173 ),
174 "The first two characters of the three character currency code in fields 34F, 90D, and 90C must be the same for all occurrences",
175 ));
176 }
177 }
178
179 if let Some(ref field_90d) = self.field_90d {
181 let field_90d_currency = &field_90d.currency[0..2];
182 if field_90d_currency != base_currency {
183 errors.push(SwiftValidationError::content_error(
184 "C27",
185 "90D",
186 &field_90d.currency,
187 &format!(
188 "Currency code in field 90D ({}) must match field 34F ({}). First two characters must be the same for all currency fields",
189 field_90d_currency, base_currency
190 ),
191 "The first two characters of the three character currency code in fields 34F, 90D, and 90C must be the same for all occurrences",
192 ));
193 }
194 }
195
196 if let Some(ref field_90c) = self.field_90c {
198 let field_90c_currency = &field_90c.currency[0..2];
199 if field_90c_currency != base_currency {
200 errors.push(SwiftValidationError::content_error(
201 "C27",
202 "90C",
203 &field_90c.currency,
204 &format!(
205 "Currency code in field 90C ({}) must match field 34F ({}). First two characters must be the same for all currency fields",
206 field_90c_currency, base_currency
207 ),
208 "The first two characters of the three character currency code in fields 34F, 90D, and 90C must be the same for all occurrences",
209 ));
210 }
211 }
212
213 errors
214 }
215
216 fn validate_c2_floor_limit_dc_mark(&self) -> Option<SwiftValidationError> {
221 if let Some(ref floor_limit_credit) = self.floor_limit_credit {
222 if self.floor_limit_debit.indicator != Some('D') {
226 return Some(SwiftValidationError::content_error(
227 "C23",
228 "34F",
229 &format!("{:?}", self.floor_limit_debit.indicator),
230 &format!(
231 "When two field 34F are present, first occurrence must have D/C mark 'D', found '{:?}'",
232 self.floor_limit_debit.indicator
233 ),
234 "When both fields 34F are present, subfield 2 of the first 34F must contain the value 'D', and subfield 2 of the second 34F must contain the value 'C'",
235 ));
236 }
237
238 if floor_limit_credit.indicator != Some('C') {
240 return Some(SwiftValidationError::content_error(
241 "C23",
242 "34F",
243 &format!("{:?}", floor_limit_credit.indicator),
244 &format!(
245 "When two field 34F are present, second occurrence must have D/C mark 'C', found '{:?}'",
246 floor_limit_credit.indicator
247 ),
248 "When both fields 34F are present, subfield 2 of the first 34F must contain the value 'D', and subfield 2 of the second 34F must contain the value 'C'",
249 ));
250 }
251 } else {
252 if self.floor_limit_debit.indicator.is_some() {
254 return Some(SwiftValidationError::content_error(
255 "C23",
256 "34F",
257 &format!("{:?}", self.floor_limit_debit.indicator),
258 &format!(
259 "When only one field 34F is present, D/C mark must not be used, found '{:?}'",
260 self.floor_limit_debit.indicator
261 ),
262 "When only one field 34F is present, the second subfield (D/C Mark) must not be used",
263 ));
264 }
265 }
266
267 None
268 }
269
270 fn validate_c3_field_86_positioning(&self) -> Vec<SwiftValidationError> {
275 let errors = Vec::new();
276
277 for statement_line in self.statement_lines.iter() {
279 if statement_line.field_86.is_some() {
280 }
287 }
288
289 errors
296 }
297
298 pub fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
301 let mut all_errors = Vec::new();
302
303 let c1_errors = self.validate_c1_currency_consistency();
305 all_errors.extend(c1_errors);
306 if stop_on_first_error && !all_errors.is_empty() {
307 return all_errors;
308 }
309
310 if let Some(error) = self.validate_c2_floor_limit_dc_mark() {
312 all_errors.push(error);
313 if stop_on_first_error {
314 return all_errors;
315 }
316 }
317
318 let c3_errors = self.validate_c3_field_86_positioning();
320 all_errors.extend(c3_errors);
321
322 all_errors
323 }
324}
325
326impl crate::traits::SwiftMessageBody for MT942 {
328 fn message_type() -> &'static str {
329 "942"
330 }
331
332 fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
333 Self::parse_from_block4(block4)
334 }
335
336 fn to_mt_string(&self) -> String {
337 let mut result = String::new();
338
339 append_field(&mut result, &self.field_20);
340 append_optional_field(&mut result, &self.field_21);
341 append_field(&mut result, &self.field_25);
342 append_field(&mut result, &self.field_28c);
343 append_field(&mut result, &self.floor_limit_debit);
344 append_optional_field(&mut result, &self.floor_limit_credit);
345 append_field(&mut result, &self.field_13d);
346
347 for statement_line in &self.statement_lines {
349 append_field(&mut result, &statement_line.field_61);
350 append_optional_field(&mut result, &statement_line.field_86);
351 }
352
353 append_optional_field(&mut result, &self.field_90d);
354 append_optional_field(&mut result, &self.field_90c);
355 append_optional_field(&mut result, &self.field_86);
356
357 finalize_mt_string(result, false)
358 }
359
360 fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
361 MT942::validate_network_rules(self, stop_on_first_error)
363 }
364}