swift_mt_message/messages/
mt900.rs

1use crate::errors::SwiftValidationError;
2use crate::fields::*;
3use crate::parser::utils::*;
4use serde::{Deserialize, Serialize};
5
6/// **MT900: Confirmation of Debit**
7///
8/// Confirms debit to account servicing institution's account.
9///
10/// **Usage:** Debit confirmations, account reconciliation
11/// **Category:** Category 9 (Cash Management & Customer Status)
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
13pub struct MT900 {
14    /// Transaction Reference Number (Field 20)
15    #[serde(rename = "20")]
16    pub field_20: Field20,
17
18    /// Related Reference (Field 21)
19    #[serde(rename = "21")]
20    pub field_21: Field21NoOption,
21
22    /// Account Identification (Field 25)
23    #[serde(rename = "25")]
24    pub field_25: Field25AccountIdentification,
25
26    /// Date/Time Indication (Field 13D)
27    #[serde(rename = "13D")]
28    pub field_13d: Option<Field13D>,
29
30    /// Value Date, Currency Code, Amount (Field 32A)
31    #[serde(rename = "32A")]
32    pub field_32a: Field32A,
33
34    /// Ordering Institution (Field 52)
35    #[serde(flatten)]
36    pub field_52: Option<Field52OrderingInstitution>,
37
38    /// Sender to Receiver Information (Field 72)
39    #[serde(rename = "72")]
40    pub field_72: Option<Field72>,
41}
42
43impl MT900 {
44    /// Parse message from Block 4 content
45    pub fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
46        let mut parser = crate::parser::MessageParser::new(block4, "900");
47
48        // Parse mandatory fields in order matching to_mt_string
49        let field_20 = parser.parse_field::<Field20>("20")?;
50        let field_21 = parser.parse_field::<Field21NoOption>("21")?;
51        let field_25 = parser.parse_field::<Field25AccountIdentification>("25")?;
52
53        // Parse optional Field 13D before Field 32A
54        let field_13d = parser.parse_optional_field::<Field13D>("13D")?;
55
56        // Parse mandatory Field 32A
57        let field_32a = parser.parse_field::<Field32A>("32A")?;
58
59        // Parse optional fields
60        let field_52 = parser.parse_optional_variant_field::<Field52OrderingInstitution>("52")?;
61        let field_72 = parser.parse_optional_field::<Field72>("72")?;
62
63        // Verify all content is consumed
64        verify_parser_complete(&parser)?;
65
66        Ok(Self {
67            field_20,
68            field_21,
69            field_25,
70            field_13d,
71            field_32a,
72            field_52,
73            field_72,
74        })
75    }
76
77    /// Parse from SWIFT MT text format
78    pub fn parse(input: &str) -> Result<Self, crate::errors::ParseError> {
79        let block4 = extract_block4(input)?;
80        Self::parse_from_block4(&block4)
81    }
82
83    /// Convert to SWIFT MT text format
84    pub fn to_mt_string(&self) -> String {
85        let mut result = String::new();
86
87        append_field(&mut result, &self.field_20);
88        append_field(&mut result, &self.field_21);
89        append_field(&mut result, &self.field_25);
90        append_optional_field(&mut result, &self.field_13d);
91        append_field(&mut result, &self.field_32a);
92        append_optional_field(&mut result, &self.field_52);
93        append_optional_field(&mut result, &self.field_72);
94
95        finalize_mt_string(result, false)
96    }
97
98    // ========================================================================
99    // NETWORK VALIDATION RULES (SR 2025 MT900)
100    // ========================================================================
101
102    /// Main validation method - validates all network rules
103    ///
104    /// **Note**: According to SR 2025 specifications, MT900 has no network validated rules.
105    /// This method always returns an empty vector.
106    ///
107    /// Returns array of validation errors, respects stop_on_first_error flag
108    pub fn validate_network_rules(&self, _stop_on_first_error: bool) -> Vec<SwiftValidationError> {
109        // MT900 has no network validated rules per SR 2025 specifications
110        Vec::new()
111    }
112}
113
114impl crate::traits::SwiftMessageBody for MT900 {
115    fn message_type() -> &'static str {
116        "900"
117    }
118
119    fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
120        // Call the existing public method implementation
121        MT900::parse_from_block4(block4)
122    }
123
124    fn to_mt_string(&self) -> String {
125        // Call the existing public method implementation
126        MT900::to_mt_string(self)
127    }
128
129    fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
130        // Call the existing public method implementation
131        MT900::validate_network_rules(self, stop_on_first_error)
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn test_mt900_parse() {
141        let mt900_text = r#":20:20240719001
142:21:REF20240719001
143:25:12345678901234567890
144:32A:240719USD1000,00
145-"#;
146        let result = MT900::parse_from_block4(mt900_text);
147        assert!(result.is_ok());
148        let mt900 = result.unwrap();
149        assert_eq!(mt900.field_20.reference, "20240719001");
150        assert_eq!(mt900.field_21.reference, "REF20240719001");
151    }
152
153    #[test]
154    fn test_mt900_network_validation() {
155        let mt900_text = r#":20:20240719001
156:21:REF20240719001
157:25:12345678901234567890
158:32A:240719USD1000,00
159-"#;
160        let mt900 = MT900::parse_from_block4(mt900_text).unwrap();
161
162        // MT900 has no network validation rules, should always return empty vector
163        let errors = mt900.validate_network_rules(false);
164        assert!(errors.is_empty(), "MT900 should have no validation errors");
165
166        // Test with stop_on_first_error=true as well
167        let errors = mt900.validate_network_rules(true);
168        assert!(
169            errors.is_empty(),
170            "MT900 should have no validation errors with stop_on_first_error"
171        );
172    }
173
174    #[test]
175    fn test_mt900_trait_validate_network_rules() {
176        use crate::traits::SwiftMessageBody;
177
178        let mt900_text = r#":20:20240719001
179:21:REF20240719001
180:25:12345678901234567890
181:32A:240719USD1000,00
182-"#;
183        let mt900 = MT900::parse_from_block4(mt900_text).unwrap();
184
185        // Test trait method implementation
186        let errors = <MT900 as SwiftMessageBody>::validate_network_rules(&mt900, false);
187        assert!(
188            errors.is_empty(),
189            "Trait implementation should return empty vector"
190        );
191    }
192}