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