swift_mt_message/messages/
mt202cov.rs1use chrono::NaiveDate;
4use serde::{Deserialize, Serialize};
5
6use crate::common::{Amount, Field, MessageBlock, SwiftDate, tags};
7use crate::error::{MTError, Result};
8use crate::messages::{
9 MTMessageType, extract_text_block, find_field, find_fields, get_optional_field_value,
10 get_required_field_value,
11};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct MT202COV {
17 fields: Vec<Field>,
19}
20
21impl MT202COV {
22 pub fn transaction_reference(&self) -> Result<String> {
24 get_required_field_value(&self.fields, tags::SENDER_REFERENCE)
25 }
26
27 pub fn related_reference(&self) -> Option<String> {
29 get_optional_field_value(&self.fields, "21")
30 }
31
32 pub fn value_date_currency_amount(&self) -> Result<String> {
34 get_required_field_value(&self.fields, tags::VALUE_DATE_CURRENCY_AMOUNT)
35 }
36
37 pub fn amount(&self) -> Result<Amount> {
39 let field_32a = get_required_field_value(&self.fields, tags::VALUE_DATE_CURRENCY_AMOUNT)?;
40
41 if field_32a.len() < 9 {
43 return Err(MTError::InvalidFieldFormat {
44 field: "32A".to_string(),
45 message: "Field 32A too short".to_string(),
46 });
47 }
48
49 let currency_amount = &field_32a[6..];
51 Amount::parse(currency_amount)
52 }
53
54 pub fn currency(&self) -> Result<String> {
56 let amount = self.amount()?;
57 Ok(amount.currency)
58 }
59
60 pub fn value_date(&self) -> Result<NaiveDate> {
62 let field_32a = get_required_field_value(&self.fields, tags::VALUE_DATE_CURRENCY_AMOUNT)?;
63
64 if field_32a.len() < 6 {
65 return Err(MTError::InvalidFieldFormat {
66 field: "32A".to_string(),
67 message: "Field 32A too short for date".to_string(),
68 });
69 }
70
71 let date_str = &field_32a[0..6];
72 let swift_date = SwiftDate::parse_yymmdd(date_str)?;
73 Ok(swift_date.date)
74 }
75
76 pub fn ordering_customer(&self) -> Result<String> {
78 if let Some(customer) = get_optional_field_value(&self.fields, tags::ORDERING_CUSTOMER) {
80 Ok(customer)
81 } else if let Some(customer) = get_optional_field_value(&self.fields, "50A") {
82 Ok(customer)
83 } else if let Some(customer) = get_optional_field_value(&self.fields, "50F") {
84 Ok(customer)
85 } else {
86 Err(MTError::missing_required_field("50K/50A/50F"))
87 }
88 }
89
90 pub fn ordering_institution(&self) -> Option<String> {
92 get_optional_field_value(&self.fields, tags::ORDERING_INSTITUTION)
93 }
94
95 pub fn ordering_institution_d(&self) -> Option<String> {
97 get_optional_field_value(&self.fields, "52D")
98 }
99
100 pub fn senders_correspondent(&self) -> Option<String> {
102 get_optional_field_value(&self.fields, tags::SENDERS_CORRESPONDENT)
103 }
104
105 pub fn senders_correspondent_b(&self) -> Option<String> {
107 get_optional_field_value(&self.fields, "53B")
108 }
109
110 pub fn senders_correspondent_d(&self) -> Option<String> {
112 get_optional_field_value(&self.fields, "53D")
113 }
114
115 pub fn receivers_correspondent(&self) -> Option<String> {
117 get_optional_field_value(&self.fields, tags::RECEIVERS_CORRESPONDENT)
118 }
119
120 pub fn receivers_correspondent_b(&self) -> Option<String> {
122 get_optional_field_value(&self.fields, "54B")
123 }
124
125 pub fn receivers_correspondent_d(&self) -> Option<String> {
127 get_optional_field_value(&self.fields, "54D")
128 }
129
130 pub fn third_reimbursement_institution(&self) -> Option<String> {
132 get_optional_field_value(&self.fields, tags::THIRD_REIMBURSEMENT_INSTITUTION)
133 }
134
135 pub fn third_reimbursement_institution_d(&self) -> Option<String> {
137 get_optional_field_value(&self.fields, "55D")
138 }
139
140 pub fn intermediary_institution(&self) -> Option<String> {
142 get_optional_field_value(&self.fields, tags::INTERMEDIARY_INSTITUTION)
143 }
144
145 pub fn intermediary_institution_c(&self) -> Option<String> {
147 get_optional_field_value(&self.fields, "56C")
148 }
149
150 pub fn intermediary_institution_d(&self) -> Option<String> {
152 get_optional_field_value(&self.fields, "56D")
153 }
154
155 pub fn account_with_institution(&self) -> Option<String> {
157 get_optional_field_value(&self.fields, tags::ACCOUNT_WITH_INSTITUTION)
158 }
159
160 pub fn account_with_institution_b(&self) -> Option<String> {
162 get_optional_field_value(&self.fields, "57B")
163 }
164
165 pub fn account_with_institution_c(&self) -> Option<String> {
167 get_optional_field_value(&self.fields, "57C")
168 }
169
170 pub fn account_with_institution_d(&self) -> Option<String> {
172 get_optional_field_value(&self.fields, "57D")
173 }
174
175 pub fn beneficiary_institution(&self) -> Result<String> {
177 if let Some(beneficiary) = get_optional_field_value(&self.fields, "58A") {
178 Ok(beneficiary)
179 } else if let Some(beneficiary) = get_optional_field_value(&self.fields, "58D") {
180 Ok(beneficiary)
181 } else {
182 Err(MTError::missing_required_field("58A or 58D"))
183 }
184 }
185
186 pub fn beneficiary_institution_d(&self) -> Option<String> {
188 get_optional_field_value(&self.fields, "58D")
189 }
190
191 pub fn beneficiary_customer(&self) -> Result<String> {
193 if let Some(customer) = get_optional_field_value(&self.fields, tags::BENEFICIARY_CUSTOMER) {
194 Ok(customer)
195 } else if let Some(customer) = get_optional_field_value(&self.fields, "59A") {
196 Ok(customer)
197 } else if let Some(customer) = get_optional_field_value(&self.fields, "59F") {
198 Ok(customer)
199 } else {
200 Err(MTError::missing_required_field("59/59A/59F"))
201 }
202 }
203
204 pub fn remittance_information(&self) -> Option<String> {
206 get_optional_field_value(&self.fields, tags::REMITTANCE_INFORMATION)
207 }
208
209 pub fn details_of_charges(&self) -> Option<String> {
211 get_optional_field_value(&self.fields, tags::DETAILS_OF_CHARGES)
212 }
213
214 pub fn senders_charges(&self) -> Option<String> {
216 get_optional_field_value(&self.fields, tags::SENDERS_CHARGES)
217 }
218
219 pub fn receivers_charges(&self) -> Option<String> {
221 get_optional_field_value(&self.fields, tags::RECEIVERS_CHARGES)
222 }
223
224 pub fn regulatory_reporting(&self) -> Option<String> {
226 get_optional_field_value(&self.fields, "77B")
227 }
228
229 pub fn instructions(&self) -> Option<String> {
231 get_optional_field_value(&self.fields, "72")
232 }
233
234 pub fn all_instructions(&self) -> Vec<String> {
236 find_fields(&self.fields, "72")
237 .into_iter()
238 .map(|field| field.value().to_string())
239 .collect()
240 }
241
242 pub fn underlying_customer_credit_transfer(&self) -> Option<String> {
244 get_optional_field_value(&self.fields, "21")
245 }
246
247 pub fn is_cover_message(&self) -> bool {
249 self.get_field("50K").is_some() || self.get_field("59").is_some()
251 }
252}
253
254impl MTMessageType for MT202COV {
255 fn from_blocks(blocks: Vec<MessageBlock>) -> Result<Self> {
256 let fields = extract_text_block(&blocks)?;
257
258 let required_fields = [
260 tags::SENDER_REFERENCE, tags::VALUE_DATE_CURRENCY_AMOUNT, ];
263
264 for &field_tag in &required_fields {
266 if !fields.iter().any(|f| f.tag.as_str() == field_tag) {
267 return Err(MTError::missing_required_field(field_tag));
268 }
269 }
270
271 if !fields
273 .iter()
274 .any(|f| f.tag.as_str() == "58A" || f.tag.as_str() == "58D")
275 {
276 return Err(MTError::missing_required_field("58A or 58D"));
277 }
278
279 if !fields
281 .iter()
282 .any(|f| f.tag.as_str() == "50K" || f.tag.as_str() == "50A" || f.tag.as_str() == "50F")
283 {
284 return Err(MTError::missing_required_field("50K/50A/50F"));
285 }
286
287 if !fields
288 .iter()
289 .any(|f| f.tag.as_str() == "59" || f.tag.as_str() == "59A" || f.tag.as_str() == "59F")
290 {
291 return Err(MTError::missing_required_field("59/59A/59F"));
292 }
293
294 Ok(MT202COV { fields })
295 }
296
297 fn get_field(&self, tag: &str) -> Option<&Field> {
298 find_field(&self.fields, tag)
299 }
300
301 fn get_fields(&self, tag: &str) -> Vec<&Field> {
302 find_fields(&self.fields, tag)
303 }
304
305 fn get_all_fields(&self) -> Vec<&Field> {
306 self.fields.iter().collect()
307 }
308
309 fn text_fields(&self) -> &[Field] {
310 &self.fields
311 }
312}
313
314#[cfg(test)]
315mod tests {
316 use super::*;
317 use crate::common::Field;
318 use chrono::Datelike;
319
320 fn create_test_mt202cov() -> MT202COV {
321 let fields = vec![
322 Field::new("20", "COV123456789"),
323 Field::new("21", "NOTPROVIDED"),
324 Field::new("32A", "210315USD10000000,00"),
325 Field::new("50K", "ORDERING CUSTOMER\nCOMPANY ABC\nNEW YORK NY"),
326 Field::new("52A", "ORDBANK33XXX"),
327 Field::new("53A", "SNDCOR33XXX"),
328 Field::new("54A", "RCVCOR33XXX"),
329 Field::new("57A", "ACWITH33XXX"),
330 Field::new("58A", "BENBANK44XXX"),
331 Field::new("59", "BENEFICIARY CUSTOMER\nCOMPANY XYZ\nLONDON GB"),
332 Field::new("70", "INVOICE PAYMENT INV-2021-001"),
333 Field::new("71A", "OUR"),
334 Field::new("72", "COVER FOR UNDERLYING MT103"),
335 ];
336 MT202COV { fields }
337 }
338
339 #[test]
340 fn test_transaction_reference() {
341 let mt202cov = create_test_mt202cov();
342 assert_eq!(mt202cov.transaction_reference().unwrap(), "COV123456789");
343 }
344
345 #[test]
346 fn test_related_reference() {
347 let mt202cov = create_test_mt202cov();
348 assert_eq!(mt202cov.related_reference().unwrap(), "NOTPROVIDED");
349 }
350
351 #[test]
352 fn test_amount_parsing() {
353 let mt202cov = create_test_mt202cov();
354 let amount = mt202cov.amount().unwrap();
355 assert_eq!(amount.value, 10000000.0);
356 assert_eq!(amount.currency, "USD");
357 }
358
359 #[test]
360 fn test_currency() {
361 let mt202cov = create_test_mt202cov();
362 assert_eq!(mt202cov.currency().unwrap(), "USD");
363 }
364
365 #[test]
366 fn test_value_date() {
367 let mt202cov = create_test_mt202cov();
368 let date = mt202cov.value_date().unwrap();
369 assert_eq!(date.year(), 2021);
370 assert_eq!(date.month(), 3);
371 assert_eq!(date.day(), 15);
372 }
373
374 #[test]
375 fn test_ordering_customer() {
376 let mt202cov = create_test_mt202cov();
377 assert_eq!(
378 mt202cov.ordering_customer().unwrap(),
379 "ORDERING CUSTOMER\nCOMPANY ABC\nNEW YORK NY"
380 );
381 }
382
383 #[test]
384 fn test_beneficiary_customer() {
385 let mt202cov = create_test_mt202cov();
386 assert_eq!(
387 mt202cov.beneficiary_customer().unwrap(),
388 "BENEFICIARY CUSTOMER\nCOMPANY XYZ\nLONDON GB"
389 );
390 }
391
392 #[test]
393 fn test_beneficiary_institution() {
394 let mt202cov = create_test_mt202cov();
395 assert_eq!(mt202cov.beneficiary_institution().unwrap(), "BENBANK44XXX");
396 }
397
398 #[test]
399 fn test_ordering_institution() {
400 let mt202cov = create_test_mt202cov();
401 assert_eq!(mt202cov.ordering_institution().unwrap(), "ORDBANK33XXX");
402 }
403
404 #[test]
405 fn test_remittance_information() {
406 let mt202cov = create_test_mt202cov();
407 assert_eq!(
408 mt202cov.remittance_information().unwrap(),
409 "INVOICE PAYMENT INV-2021-001"
410 );
411 }
412
413 #[test]
414 fn test_details_of_charges() {
415 let mt202cov = create_test_mt202cov();
416 assert_eq!(mt202cov.details_of_charges().unwrap(), "OUR");
417 }
418
419 #[test]
420 fn test_instructions() {
421 let mt202cov = create_test_mt202cov();
422 assert_eq!(
423 mt202cov.instructions().unwrap(),
424 "COVER FOR UNDERLYING MT103"
425 );
426 }
427
428 #[test]
429 fn test_is_cover_message() {
430 let mt202cov = create_test_mt202cov();
431 assert!(mt202cov.is_cover_message());
432 }
433
434 #[test]
435 fn test_get_field() {
436 let mt202cov = create_test_mt202cov();
437 let field_20 = mt202cov.get_field("20").unwrap();
438 assert_eq!(field_20.value(), "COV123456789");
439 }
440
441 #[test]
442 fn test_get_all_fields() {
443 let mt202cov = create_test_mt202cov();
444 let all_fields = mt202cov.get_all_fields();
445 assert_eq!(all_fields.len(), 13);
446 }
447}