swift_mt_message/messages/
mt101.rs

1#[cfg(test)]
2use crate::SwiftMessageBody;
3use crate::fields::*;
4use serde::{Deserialize, Serialize};
5use swift_mt_message_macros::{SwiftMessage, serde_swift_fields};
6
7/// MT101: Request for Credit Transfer
8///
9/// Message for requesting multiple credit transfers with transaction details.
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, SwiftMessage)]
11#[validation_rules(MT101_VALIDATION_RULES)]
12#[serde_swift_fields]
13pub struct MT101 {
14    // Sequence A: Message Level Fields (Single Occurrence)
15    // Mandatory Fields
16    #[field("20", mandatory)]
17    pub field_20: GenericReferenceField, // Sender's Reference
18
19    #[field("28D", mandatory)]
20    pub field_28d: Field28D, // Message Index/Total
21
22    #[field("30", mandatory)]
23    pub field_30: GenericTextField, // Requested Execution Date
24
25    // Optional Fields - Sequence A
26    #[field("21R", optional)]
27    pub field_21r: Option<GenericReferenceField>, // Customer Specified Reference
28
29    #[field("50A", optional)]
30    pub field_50a_seq_a: Option<Field50>, // Instructing Party (Seq A)
31
32    #[field("52A", optional)]
33    pub field_52a_seq_a: Option<GenericBicField>, // Account Servicing Institution (Seq A)
34
35    #[field("52C", optional)]
36    pub field_52c_seq_a: Option<GenericAccountField>, // Account Servicing Institution C (Seq A)
37
38    #[field("51A", optional)]
39    pub field_51a: Option<GenericBicField>, // Sending Institution
40
41    #[field("25", optional)]
42    pub field_25: Option<GenericTextField>, // Authorisation
43
44    // Sequence B: Transaction Level Fields (Repetitive)
45    // Vec<MT101Transaction> automatically detected as repetitive sequence by enhanced macro
46    #[field("TRANSACTIONS", repetitive)]
47    pub transactions: Vec<MT101Transaction>,
48}
49
50/// MT101 Transaction (Sequence B)
51///
52/// Single transaction within an MT101 message.
53#[serde_swift_fields]
54#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, SwiftMessage)]
55#[validation_rules(MT101_TRANSACTION_VALIDATION_RULES)]
56pub struct MT101Transaction {
57    // Mandatory Fields per Transaction
58    #[field("21", mandatory)]
59    pub field_21: GenericReferenceField, // Transaction Reference
60
61    #[field("32B", mandatory)]
62    pub field_32b: GenericCurrencyAmountField, // Currency/Amount
63
64    #[field("59A", mandatory)]
65    pub field_59a: Field59, // Beneficiary Customer
66
67    #[field("71A", mandatory)]
68    pub field_71a: GenericTextField, // Details of Charges
69
70    // Optional Fields per Transaction
71    #[field("21F", optional)]
72    pub field_21f: Option<GenericReferenceField>, // F/X Deal Reference
73
74    #[field("23E", optional)]
75    pub field_23e: Option<Field23E>, // Instruction Code
76
77    #[field("50A_SEQ_B", optional)]
78    pub field_50a_seq_b: Option<Field50>, // Ordering Customer
79
80    #[field("50F_SEQ_B", optional)]
81    pub field_50f_seq_b: Option<GenericPartyField>, // Ordering Customer F
82
83    #[field("50G_SEQ_B", optional)]
84    pub field_50g_seq_b: Option<GenericPartyField>, // Ordering Customer G
85
86    #[field("50H_SEQ_B", optional)]
87    pub field_50h_seq_b: Option<GenericPartyField>, // Ordering Customer H
88
89    #[field("52A_SEQ_B", optional)]
90    pub field_52a_seq_b: Option<GenericBicField>, // Account Servicing Institution A
91
92    #[field("52C_SEQ_B", optional)]
93    pub field_52c_seq_b: Option<GenericAccountField>, // Account Servicing Institution C
94
95    #[field("56A", optional)]
96    pub field_56a: Option<GenericBicField>, // Intermediary Institution A
97
98    #[field("56C", optional)]
99    pub field_56c: Option<GenericAccountField>, // Intermediary Institution C
100
101    #[field("56D", optional)]
102    pub field_56d: Option<GenericNameAddressField>, // Intermediary Institution D
103
104    #[field("57A", optional)]
105    pub field_57a: Option<GenericBicField>, // Account With Institution A
106
107    #[field("57C", optional)]
108    pub field_57c: Option<GenericAccountField>, // Account With Institution C
109
110    #[field("57D", optional)]
111    pub field_57d: Option<GenericNameAddressField>, // Account With Institution D
112
113    #[field("70", optional)]
114    pub field_70: Option<GenericMultiLine4x35>, // Remittance Information
115
116    #[field("77B", optional)]
117    pub field_77b: Option<GenericMultiLine3x35>, // Regulatory Reporting
118
119    #[field("33B", optional)]
120    pub field_33b: Option<GenericCurrencyAmountField>, // Currency/Original Amount
121
122    #[field("25A", optional)]
123    pub field_25a: Option<GenericAccountField>, // Charges Account
124
125    #[field("36", optional)]
126    pub field_36: Option<Field36>, // Exchange Rate
127}
128
129/// Enhanced validation rules with forEach support for repetitive sequences
130const MT101_VALIDATION_RULES: &str = r#"{
131  "rules": [
132    {
133      "id": "C1",
134      "description": "Per-transaction: If 36 present → 21F must be present",
135      "forEach": {
136        "collection": "transactions",
137        "condition": {
138          "if": [
139            {"var": "field_36.is_some"},
140            {"var": "field_21f.is_some"},
141            true
142          ]
143        }
144      }
145    },
146    {
147      "id": "C8",
148      "description": "Cross-transaction: All currencies must match if 21R present",
149      "condition": {
150        "if": [
151          {"var": "field_21r.is_some"},
152          {"allEqual": {"map": ["transactions", "field_32b.currency"]}},
153          true
154        ]
155      }
156    },
157    {
158      "id": "SEQ_B_MIN",
159      "description": "At least one transaction required",
160      "condition": {
161        ">=": [{"length": {"var": "transactions"}}, 1]
162      }
163    }
164  ]
165}"#;
166
167/// Validation rules specific to MT101 transactions
168const MT101_TRANSACTION_VALIDATION_RULES: &str = r#"{
169  "rules": [
170    {
171      "id": "T_C1",
172      "description": "If exchange rate (36) is present, F/X deal reference (21F) must be present",
173      "condition": {
174        "if": [
175          {"var": "field_36.is_some"},
176          {"var": "field_21f.is_some"},
177          true
178        ]
179      }
180    },
181    {
182      "id": "T_C7", 
183      "description": "If intermediary institution (56A/C/D) is present, account with institution (57A/C/D) must be present",
184      "condition": {
185        "if": [
186          {"or": [
187            {"var": "field_56a.is_some"},
188            {"var": "field_56c.is_some"},
189            {"var": "field_56d.is_some"}
190          ]},
191          {"or": [
192            {"var": "field_57a.is_some"},
193            {"var": "field_57c.is_some"},
194            {"var": "field_57d.is_some"}
195          ]},
196          true
197        ]
198      }
199    },
200    {
201      "id": "T_REF",
202      "description": "Transaction reference must be unique within the message",
203      "condition": {
204        "!=": [{"var": "field_21.value"}, ""]
205      }
206    }
207  ]
208}"#;
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213    use std::collections::HashMap;
214
215    #[test]
216    fn test_mt101_compilation() {
217        // Test that the MT101 structure compiles with the enhanced macro system
218        let field_map = HashMap::new();
219
220        // This should compile without errors
221        let result = MT101::from_fields(field_map);
222
223        // We expect it to fail parsing because we have no fields,
224        // but the important thing is that it compiles
225        assert!(result.is_err());
226    }
227
228    #[test]
229    fn test_mt101_transaction_compilation() {
230        // Test that the MT101Transaction structure compiles
231        let field_map = HashMap::new();
232
233        // This should compile without errors
234        let result = MT101Transaction::from_fields(field_map);
235
236        // We expect it to fail parsing because we have no fields,
237        // but the important thing is that it compiles
238        assert!(result.is_err());
239    }
240}