swift_mt_message/messages/
mt291.rs

1use crate::errors::{ParseError, SwiftValidationError};
2use crate::fields::*;
3use crate::parser::MessageParser;
4use crate::parser::utils::*;
5use serde::{Deserialize, Serialize};
6
7/// **MT291: Request for Payment of Charges, Interest and Other Expenses**
8///
9/// Request payment of charges, interest, and expenses.
10///
11/// **Usage:** Interbank charge payment requests, expense reimbursement
12/// **Category:** Category 2 (Financial Institution Transfers)
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
14pub struct MT291 {
15    /// Transaction Reference Number (Field 20)
16    #[serde(rename = "20")]
17    pub field_20: Field20,
18
19    /// Related Reference (Field 21)
20    #[serde(rename = "21")]
21    pub field_21: Field21NoOption,
22
23    /// Currency Code, Amount (Field 32B)
24    #[serde(rename = "32B")]
25    pub field_32b: Field32B,
26
27    /// Ordering Institution (Field 52)
28    #[serde(flatten, skip_serializing_if = "Option::is_none")]
29    pub field_52: Option<Field52OrderingInstitution>,
30
31    /// Account With Institution (Field 57)
32    #[serde(flatten, skip_serializing_if = "Option::is_none")]
33    pub field_57: Option<Field57AccountWithABD>,
34
35    /// Details of Charges (Field 71B)
36    #[serde(rename = "71B")]
37    pub field_71b: Field71B,
38
39    /// Sender to Receiver Information (Field 72)
40    #[serde(rename = "72", skip_serializing_if = "Option::is_none")]
41    pub field_72: Option<Field72>,
42}
43
44impl MT291 {
45    /// Parse MT291 from a raw SWIFT message string
46    pub fn parse_from_block4(block4: &str) -> Result<Self, ParseError> {
47        let mut parser = MessageParser::new(block4, "291");
48
49        // Parse mandatory fields
50        let field_20 = parser.parse_field::<Field20>("20")?;
51        let field_21 = parser.parse_field::<Field21NoOption>("21")?;
52        let field_32b = parser.parse_field::<Field32B>("32B")?;
53
54        // Parse optional Field 52 - Ordering Institution
55        let field_52 = parser.parse_optional_variant_field::<Field52OrderingInstitution>("52")?;
56
57        // Parse optional Field 57 - Account With Institution (A, B, D only per spec)
58        let field_57 = parser.parse_optional_variant_field::<Field57AccountWithABD>("57")?;
59
60        // Parse mandatory Field 71B
61        let field_71b = parser.parse_field::<Field71B>("71B")?;
62
63        // Parse optional Field 72
64        let field_72 = parser.parse_optional_field::<Field72>("72")?;
65
66        Ok(MT291 {
67            field_20,
68            field_21,
69            field_32b,
70            field_52,
71            field_57,
72            field_71b,
73            field_72,
74        })
75    }
76
77    /// Parse from generic SWIFT input (tries to detect blocks)
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_32b);
90        append_optional_field(&mut result, &self.field_52);
91        append_optional_field(&mut result, &self.field_57);
92        append_field(&mut result, &self.field_71b);
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 MTn91)
100    // ========================================================================
101
102    /// Main validation method - validates all network rules
103    ///
104    /// **Note**: According to SR 2025 MTn91 specification, there are no network
105    /// validated rules for this message type. This method is provided for API
106    /// consistency and returns an empty vector.
107    ///
108    /// Returns array of validation errors, respects stop_on_first_error flag
109    pub fn validate_network_rules(&self, _stop_on_first_error: bool) -> Vec<SwiftValidationError> {
110        // Per SR 2025 MTn91 specification:
111        // "There are no network validated rules for this message type."
112        Vec::new()
113    }
114}
115
116impl crate::traits::SwiftMessageBody for MT291 {
117    fn message_type() -> &'static str {
118        "291"
119    }
120
121    fn parse_from_block4(block4: &str) -> Result<Self, crate::errors::ParseError> {
122        // Call the existing public method implementation
123        MT291::parse_from_block4(block4)
124    }
125
126    fn to_mt_string(&self) -> String {
127        // Call the existing public method implementation
128        MT291::to_mt_string(self)
129    }
130
131    fn validate_network_rules(&self, stop_on_first_error: bool) -> Vec<SwiftValidationError> {
132        // Call the existing public method implementation
133        MT291::validate_network_rules(self, stop_on_first_error)
134    }
135}