use crate::mt::error::MtError;
use crate::mt::types::Block4;
use super::common::{parse_32a, parse_party_value, require_field, PartyInfo};
#[derive(Debug, Clone, PartialEq)]
pub struct Mt202 {
pub transaction_reference: String,
pub related_reference: String,
pub value_date: String,
pub currency: String,
pub amount: String,
pub beneficiary_institution: PartyInfo,
pub time_indication: Option<String>,
pub ordering_institution: Option<PartyInfo>,
pub senders_correspondent: Option<PartyInfo>,
pub receivers_correspondent: Option<PartyInfo>,
pub intermediary: Option<PartyInfo>,
pub sender_to_receiver_info: Option<String>,
}
pub fn parse_mt202(block4: &Block4) -> Result<Mt202, MtError> {
let mt = "202";
let transaction_reference = require_field(block4, "20", mt)?.value.clone();
let related_reference = require_field(block4, "21", mt)?.value.clone();
let field_32a = require_field(block4, "32A", mt)?.value.clone();
let (value_date, currency, amount) = parse_32a(&field_32a, "32A")?;
let beneficiary_institution = block4
.fields
.iter()
.find(|f| f.tag == "58A" || f.tag == "58D")
.map(|f| parse_party_value(&f.value))
.ok_or_else(|| MtError::MissingField {
tag: "58A/D".into(),
message_type: mt.into(),
})?;
let time_indication = block4.get("13C").map(|f| f.value.clone());
let ordering_institution = block4
.fields
.iter()
.find(|f| f.tag == "52A" || f.tag == "52D")
.map(|f| parse_party_value(&f.value));
let senders_correspondent = block4
.fields
.iter()
.find(|f| f.tag == "53A" || f.tag == "53B" || f.tag == "53D")
.map(|f| parse_party_value(&f.value));
let receivers_correspondent = block4
.fields
.iter()
.find(|f| f.tag == "54A" || f.tag == "54B" || f.tag == "54D")
.map(|f| parse_party_value(&f.value));
let intermediary = block4
.fields
.iter()
.find(|f| f.tag == "56A" || f.tag == "56D")
.map(|f| parse_party_value(&f.value));
let sender_to_receiver_info = block4.get("72").map(|f| f.value.clone());
Ok(Mt202 {
transaction_reference,
related_reference,
value_date,
currency,
amount,
beneficiary_institution,
time_indication,
ordering_institution,
senders_correspondent,
receivers_correspondent,
intermediary,
sender_to_receiver_info,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mt::parser::parse;
const MT202_RAW: &str = "\
{1:F01BANKBEBBAXXX0000000000}\
{2:I202BANKDEFFXXXXN}\
{3:{108:MT202REF}}\
{4:
:20:TXN-REF-202-001
:21:REL-REF-001
:32A:230615USD50000,00
:58A:CHASUS33XXX
-}\
{5:{CHK:DEF12345678}}";
#[test]
fn test_parse_mt202_required_fields() {
let msg = parse(MT202_RAW).unwrap();
let mt = parse_mt202(&msg.block4).unwrap();
assert_eq!(mt.transaction_reference, "TXN-REF-202-001");
assert_eq!(mt.related_reference, "REL-REF-001");
assert_eq!(mt.value_date, "2023-06-15");
assert_eq!(mt.currency, "USD");
assert_eq!(mt.amount, "50000.00");
}
#[test]
fn test_parse_mt202_beneficiary() {
let msg = parse(MT202_RAW).unwrap();
let mt = parse_mt202(&msg.block4).unwrap();
assert_eq!(
mt.beneficiary_institution.name.as_deref(),
Some("CHASUS33XXX")
);
}
#[test]
fn test_parse_mt202_missing_21_fails() {
let raw = "\
{1:F01BANKBEBBAXXX0000000000}\
{2:I202BANKDEFFXXXXN}\
{3:}\
{4:
:20:REF
:32A:230615USD1000,00
:58A:CHASUS33
-}";
let msg = parse(raw).unwrap();
let err = parse_mt202(&msg.block4).unwrap_err();
assert!(matches!(err, MtError::MissingField { tag, .. } if tag == "21"));
}
#[test]
fn test_parse_mt202_with_optional_fields() {
let raw = "\
{1:F01BANKBEBBAXXX0000000000}\
{2:I202BANKDEFFXXXXN}\
{3:}\
{4:
:20:TXN202
:21:RELREF
:32A:230615EUR25000,00
:52A:DEUTDEDB
:58A:CHASUS33
:72:/ACC/ADDITIONAL INFO
-}";
let msg = parse(raw).unwrap();
let mt = parse_mt202(&msg.block4).unwrap();
assert!(mt.ordering_institution.is_some());
assert_eq!(
mt.sender_to_receiver_info.as_deref(),
Some("/ACC/ADDITIONAL INFO")
);
}
}