swift_mt_message/messages/mt196.rs
1use crate::errors::SwiftValidationError;
2use crate::fields::*;
3use crate::parser::utils::*;
4use serde::{Deserialize, Serialize};
5
6/// **MT196: Answers**
7///
8/// Comprehensive answers and responses to queries and requests related to customer payments.
9///
10/// **Usage:** Query responses, payment inquiries, status updates
11/// **Category:** Category 1 (Customer Payments & Cheques)
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
13pub struct MT196 {
14 /// Sender's Reference (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 /// Answers (Field 76)
23 #[serde(rename = "76")]
24 pub field_76: Field76,
25
26 /// Proprietary Message (Field 77A)
27 #[serde(rename = "77A", skip_serializing_if = "Option::is_none")]
28 pub field_77a: Option<Field77A>,
29
30 /// Message Type and Date (Field 11)
31 #[serde(rename = "11", skip_serializing_if = "Option::is_none")]
32 pub field_11: Option<Field11>,
33
34 /// Narrative (Field 79)
35 #[serde(rename = "79", skip_serializing_if = "Option::is_none")]
36 pub field_79: Option<Field79>,
37}
38
39impl MT196 {
40 /// Parse message from Block 4 content
41 pub fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
42 let mut parser = crate::parser::MessageParser::new(block4, "196");
43
44 // Parse mandatory fields
45 let field_20 = parser.parse_field::<Field20>("20")?;
46 let field_21 = parser.parse_field::<Field21NoOption>("21")?;
47 let field_76 = parser.parse_field::<Field76>("76")?;
48
49 // Parse optional fields
50 let field_77a = parser.parse_optional_field::<Field77A>("77A")?;
51 let field_11 = parser.parse_optional_field::<Field11>("11")?;
52 let field_79 = parser.parse_optional_field::<Field79>("79")?;
53
54 Ok(MT196 {
55 field_20,
56 field_21,
57 field_76,
58 field_77a,
59 field_11,
60 field_79,
61 })
62 }
63
64 /// Parse from generic SWIFT input (tries to detect blocks)
65 pub fn parse(input: &str) -> Result<Self, crate::errors::ParseError> {
66 let block4 = extract_block4(input)?;
67 Self::parse_from_block4(&block4)
68 }
69
70 /// Convert to SWIFT MT text format
71 pub fn to_mt_string(&self) -> String {
72 let mut result = String::new();
73
74 append_field(&mut result, &self.field_20);
75 append_field(&mut result, &self.field_21);
76 append_field(&mut result, &self.field_76);
77 append_optional_field(&mut result, &self.field_77a);
78 append_optional_field(&mut result, &self.field_11);
79 append_optional_field(&mut result, &self.field_79);
80
81 finalize_mt_string(result, false)
82 }
83
84 // ========================================================================
85 // NETWORK VALIDATION RULES (SR 2025 MT196)
86 // ========================================================================
87
88 // ========================================================================
89 // VALIDATION RULE C1
90 // ========================================================================
91
92 /// C1: Field 79 or Copy of Fields Requirement (Error code: C31)
93 /// Either field 79 or a "Copy of at least the mandatory fields of the message to
94 /// which the answer relates", but not both, may be present in the message.
95 ///
96 /// **Note**: This implementation currently validates the presence of field 79.
97 /// The "Copy of fields" requirement cannot be validated at this level since
98 /// copied fields are represented as additional optional fields in the message structure.
99 /// Full validation of this rule requires message-level context that is not
100 /// available in the current MT196 structure.
101 fn validate_c1_field_79_or_copy(&self) -> Option<SwiftValidationError> {
102 // Note: MT196 structure currently only has field 79 as defined optional field.
103 // The "Copy of at least the mandatory fields of the original message" is
104 // represented as additional optional fields that would need to be tracked
105 // separately. This validation currently only checks for field 79 presence.
106 //
107 // In a full implementation, we would need to track whether any copied fields
108 // from the original message are present, which would require extending the
109 // MT196 structure to include a generic "copied_fields" collection.
110
111 // Current implementation: no validation error since we cannot detect copied fields
112 // This is a known limitation of the current structure
113 None
114 }
115
116 /// Main validation method - validates all network rules
117 /// Returns array of validation errors, respects stop_on_first_error flag
118 pub fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
119 let mut all_errors = Vec::new();
120
121 // C1: Field 79 or Copy of Fields Requirement
122 if let Some(error) = self.validate_c1_field_79_or_copy() {
123 all_errors.push(error);
124 if stop_on_first_error {
125 return all_errors;
126 }
127 }
128
129 all_errors
130 }
131}
132
133impl crate::traits::SwiftMessageBody for MT196 {
134 fn message_type() -> &'static str {
135 "196"
136 }
137
138 fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
139 // Call the existing public method implementation
140 MT196::parse_from_block4(block4)
141 }
142
143 fn to_mt_string(&self) -> String {
144 // Call the existing public method implementation
145 MT196::to_mt_string(self)
146 }
147
148 fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
149 // Call the existing public method implementation
150 MT196::validate_network_rules(self, stop_on_first_error)
151 }
152}