swift_mt_message/messages/
mt950.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 MT950 {
14 #[serde(rename = "20")]
16 pub field_20: Field20,
17
18 #[serde(rename = "25")]
20 pub field_25: Field25NoOption,
21
22 #[serde(rename = "28C")]
24 pub field_28c: Field28C,
25
26 #[serde(rename = "60")]
28 pub field_60: Field60,
29
30 #[serde(rename = "61", skip_serializing_if = "Option::is_none")]
32 pub field_61: Option<Vec<Field61>>,
33
34 #[serde(rename = "62")]
36 pub field_62: Field62,
37
38 #[serde(rename = "64", skip_serializing_if = "Option::is_none")]
40 pub field_64: Option<Field64>,
41}
42
43impl MT950 {
44 pub fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
46 let mut parser = crate::parser::MessageParser::new(block4, "950");
47
48 let field_20 = parser.parse_field::<Field20>("20")?;
50 let field_25 = parser.parse_field::<Field25NoOption>("25")?;
51 let field_28c = parser.parse_field::<Field28C>("28C")?;
52
53 let field_60 = if parser.detect_field("60F") {
55 Field60::F(parser.parse_field::<Field60F>("60F")?)
56 } else if parser.detect_field("60M") {
57 Field60::M(parser.parse_field::<Field60M>("60M")?)
58 } else if parser.detect_field("60") {
59 parser.parse_field::<Field60>("60")?
61 } else {
62 return Err(crate::errors::ParseError::InvalidFormat {
63 message: "MT950: Missing required field 60 (opening balance)".to_string(),
64 });
65 };
66
67 parser = parser.with_duplicates(true);
69
70 let mut field_61_vec = Vec::new();
72 while parser.detect_field("61") {
73 if let Ok(field) = parser.parse_field::<Field61>("61") {
74 field_61_vec.push(field);
75 } else {
76 break;
77 }
78 }
79 let field_61 = if field_61_vec.is_empty() {
80 None
81 } else {
82 Some(field_61_vec)
83 };
84
85 let field_62 = if parser.detect_field("62F") {
87 Field62::F(parser.parse_field::<Field62F>("62F")?)
88 } else if parser.detect_field("62M") {
89 Field62::M(parser.parse_field::<Field62M>("62M")?)
90 } else if parser.detect_field("62") {
91 parser.parse_field::<Field62>("62")?
93 } else {
94 return Err(crate::errors::ParseError::InvalidFormat {
95 message: "MT950: Missing required field 62 (closing balance)".to_string(),
96 });
97 };
98
99 let field_64 = parser.parse_optional_field::<Field64>("64")?;
101
102 Ok(MT950 {
103 field_20,
104 field_25,
105 field_28c,
106 field_60,
107 field_61,
108 field_62,
109 field_64,
110 })
111 }
112
113 fn get_field_60_currency_prefix(&self) -> &str {
123 match &self.field_60 {
124 Field60::F(field) => &field.currency[0..2],
125 Field60::M(field) => &field.currency[0..2],
126 }
127 }
128
129 fn get_field_62_currency_prefix(&self) -> &str {
131 match &self.field_62 {
132 Field62::F(field) => &field.currency[0..2],
133 Field62::M(field) => &field.currency[0..2],
134 }
135 }
136
137 fn get_field_60_currency(&self) -> &str {
139 match &self.field_60 {
140 Field60::F(field) => &field.currency,
141 Field60::M(field) => &field.currency,
142 }
143 }
144
145 fn get_field_62_currency(&self) -> &str {
147 match &self.field_62 {
148 Field62::F(field) => &field.currency,
149 Field62::M(field) => &field.currency,
150 }
151 }
152
153 fn validate_c1_currency_consistency(&self) -> Vec<SwiftValidationError> {
160 let mut errors = Vec::new();
161
162 let base_currency_prefix = self.get_field_60_currency_prefix();
164 let base_currency = self.get_field_60_currency();
165
166 let field_62_prefix = self.get_field_62_currency_prefix();
168 let field_62_currency = self.get_field_62_currency();
169
170 if field_62_prefix != base_currency_prefix {
171 errors.push(SwiftValidationError::business_error(
172 "C27",
173 "62a",
174 vec!["60a".to_string()],
175 &format!(
176 "Currency code mismatch: field 62a currency '{}' (prefix '{}') must have the same first two characters as field 60a currency '{}' (prefix '{}')",
177 field_62_currency, field_62_prefix, base_currency, base_currency_prefix
178 ),
179 "The first two characters of the three character currency code in fields 60a, 62a and 64 must be the same",
180 ));
181 }
182
183 if let Some(ref field_64) = self.field_64 {
185 let field_64_prefix = &field_64.currency[0..2];
186 if field_64_prefix != base_currency_prefix {
187 errors.push(SwiftValidationError::business_error(
188 "C27",
189 "64",
190 vec!["60a".to_string(), "62a".to_string()],
191 &format!(
192 "Currency code mismatch: field 64 currency '{}' (prefix '{}') must have the same first two characters as field 60a currency '{}' (prefix '{}')",
193 field_64.currency, field_64_prefix, base_currency, base_currency_prefix
194 ),
195 "The first two characters of the three character currency code in fields 60a, 62a and 64 must be the same",
196 ));
197 }
198 }
199
200 errors
201 }
202
203 pub fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
206 let mut all_errors = Vec::new();
207
208 let c1_errors = self.validate_c1_currency_consistency();
210 all_errors.extend(c1_errors);
211 if stop_on_first_error && !all_errors.is_empty() {
212 return all_errors;
213 }
214
215 all_errors
216 }
217}
218
219impl crate::traits::SwiftMessageBody for MT950 {
221 fn message_type() -> &'static str {
222 "950"
223 }
224
225 fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
226 Self::parse_from_block4(block4)
227 }
228
229 fn to_mt_string(&self) -> String {
230 let mut result = String::new();
231
232 append_field(&mut result, &self.field_20);
233 append_field(&mut result, &self.field_25);
234 append_field(&mut result, &self.field_28c);
235 append_field(&mut result, &self.field_60);
236 append_vec_field(&mut result, &self.field_61);
237 append_field(&mut result, &self.field_62);
238 append_optional_field(&mut result, &self.field_64);
239
240 finalize_mt_string(result, false)
241 }
242
243 fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
244 MT950::validate_network_rules(self, stop_on_first_error)
246 }
247}