swift_mt_message/messages/
mt296.rs

1use crate::errors::{ParseError, SwiftValidationError};
2use crate::fields::*;
3use crate::parser::MessageParser;
4use crate::parser::utils::*;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8/// **MT296: Answers**
9///
10/// Response to queries, cancellation requests, or messages without dedicated response type.
11///
12/// **Usage:** Responding to MT295 queries, MT292 cancellation requests
13/// **Category:** Category 2 (Financial Institution Transfers)
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
15pub struct MT296 {
16    /// Transaction Reference Number (Field 20)
17    #[serde(rename = "20")]
18    pub field_20: Field20,
19
20    /// Related Reference (Field 21)
21    #[serde(rename = "21")]
22    pub field_21: Field21NoOption,
23
24    /// Answers (Field 76)
25    #[serde(rename = "76")]
26    pub field_76: Field76,
27
28    /// Narrative (Field 77A)
29    #[serde(rename = "77A", skip_serializing_if = "Option::is_none")]
30    pub field_77a: Option<Field77A>,
31
32    /// MT and Date of the Original Message - Received (Field 11R)
33    #[serde(rename = "11R", skip_serializing_if = "Option::is_none")]
34    pub field_11r: Option<Field11R>,
35
36    /// MT and Date of the Original Message - Sent (Field 11S)
37    #[serde(rename = "11S", skip_serializing_if = "Option::is_none")]
38    pub field_11s: Option<Field11S>,
39
40    /// Narrative Description of Original Message (Field 79)
41    #[serde(rename = "79", skip_serializing_if = "Option::is_none")]
42    pub field_79: Option<Field79>,
43
44    /// Copy of mandatory fields from original message
45    #[serde(flatten, skip_serializing_if = "HashMap::is_empty")]
46    pub original_fields: HashMap<String, serde_json::Value>,
47}
48
49impl MT296 {
50    /// Parse MT296 from a raw SWIFT message string
51    pub fn parse_from_block4(block4: &str) -> Result<Self, ParseError> {
52        let mut parser = MessageParser::new(block4, "296");
53
54        // Parse mandatory fields
55        let field_20 = parser.parse_field::<Field20>("20")?;
56        let field_21 = parser.parse_field::<Field21NoOption>("21")?;
57        let field_76 = parser.parse_field::<Field76>("76")?;
58
59        // Parse optional Field 77A
60        let field_77a = parser.parse_optional_field::<Field77A>("77A")?;
61
62        // Parse optional Field 11R or 11S
63        let field_11r = parser.parse_optional_field::<Field11R>("11R")?;
64        let field_11s = parser.parse_optional_field::<Field11S>("11S")?;
65
66        // Parse optional/conditional Field 79
67        let field_79 = parser.parse_optional_field::<Field79>("79")?;
68
69        // Collect any remaining fields as original message fields
70        // This would need to be implemented in MessageParser but for now use empty HashMap
71        let original_fields = HashMap::new();
72
73        // Validation: Only one of Field 79 or original fields should be present (C1)
74        if field_79.is_some() && !original_fields.is_empty() {
75            return Err(ParseError::InvalidFormat {
76                message: "MT296: Only one of Field 79 or copy of original message fields should be present (C1)".to_string(),
77            });
78        }
79
80        Ok(MT296 {
81            field_20,
82            field_21,
83            field_76,
84            field_77a,
85            field_11r,
86            field_11s,
87            field_79,
88            original_fields,
89        })
90    }
91
92    // ========================================================================
93    // NETWORK VALIDATION RULES (SR 2025 MTn96)
94    // ========================================================================
95
96    // ========================================================================
97    // HELPER METHODS
98    // ========================================================================
99
100    /// Check if field 79 (Narrative Description of Original Message) is present
101    fn has_field_79(&self) -> bool {
102        self.field_79.is_some()
103    }
104
105    /// Check if copy of original message fields is present
106    fn has_original_fields(&self) -> bool {
107        !self.original_fields.is_empty()
108    }
109
110    // ========================================================================
111    // VALIDATION RULES (C1)
112    // ========================================================================
113
114    /// C1: Field 79 or Copy of Fields Requirement (Error code: C31)
115    /// Either field 79 or a copy of original message fields, but not both, may be present
116    fn validate_c1_field_79_or_copy(&self) -> Option<SwiftValidationError> {
117        let has_79 = self.has_field_79();
118        let has_copy = self.has_original_fields();
119
120        if has_79 && has_copy {
121            // Both present - NOT ALLOWED
122            return Some(SwiftValidationError::content_error(
123                "C31",
124                "79",
125                "",
126                "Field 79 and copy of original message fields must not both be present",
127                "Either field 79 or a copy of at least the mandatory fields of the message to which the answer relates, but not both, may be present in the message",
128            ));
129        }
130
131        None
132    }
133
134    /// Main validation method - validates all network rules
135    /// Returns array of validation errors, respects stop_on_first_error flag
136    pub fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
137        let mut all_errors = Vec::new();
138
139        // C1: Field 79 or Copy of Fields Requirement
140        if let Some(error) = self.validate_c1_field_79_or_copy() {
141            all_errors.push(error);
142            if stop_on_first_error {
143                return all_errors;
144            }
145        }
146
147        all_errors
148    }
149}
150
151impl crate::traits::SwiftMessageBody for MT296 {
152    fn message_type() -> &'static str {
153        "296"
154    }
155
156    fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
157        Self::parse_from_block4(block4)
158    }
159
160    fn to_mt_string(&self) -> String {
161        let mut result = String::new();
162
163        append_field(&mut result, &self.field_20);
164        append_field(&mut result, &self.field_21);
165        append_field(&mut result, &self.field_76);
166        append_optional_field(&mut result, &self.field_77a);
167        append_optional_field(&mut result, &self.field_11r);
168        append_optional_field(&mut result, &self.field_11s);
169        append_optional_field(&mut result, &self.field_79);
170
171        // Note: original_fields are not serialized as they are dynamic
172        // and would require special handling
173
174        finalize_mt_string(result, false)
175    }
176
177    fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
178        // Call the existing public method implementation
179        MT296::validate_network_rules(self, stop_on_first_error)
180    }
181}