swift_mt_message/messages/mt104.rs
1// This is a demonstration of how MT104 could be redesigned to work with field attributes
2//
3// The key insight is that the #[field("XX")] attribute system expects individual SWIFT fields,
4// not complex nested structures. Here's how we can solve the MT104 problem:
5
6use crate::fields::{
7 Field20, Field21, Field23E, Field26T, Field30, Field36, Field50, Field59, Field70, Field71A,
8 Field72, Field77B, GenericBalanceField, GenericBicField, GenericCurrencyAmountField,
9};
10use serde::{Deserialize, Serialize};
11use swift_mt_message_macros::{SwiftMessage, serde_swift_fields};
12
13/// MT104: Customer Direct Debit
14///
15/// Message for customer direct debit instructions with transaction details.
16#[serde_swift_fields]
17#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, SwiftMessage)]
18#[validation_rules(MT104_VALIDATION_RULES)]
19pub struct MT104 {
20 // ================================
21 // SEQUENCE A - GENERAL INFORMATION
22 // ================================
23 /// **Sender's Reference** - Field 20 (Mandatory)
24 /// Unique reference assigned by the sender to identify this MT104 message.
25 #[field("20", mandatory)]
26 pub field_20: Field20,
27
28 /// **Customer Specified Reference** - Field 21R (Conditional)
29 /// Required if Field 23E = RFDD (example condition)
30 #[field("21R", optional)]
31 pub field_21r: Option<Field21>,
32
33 /// **Instruction Code** - Field 23E (Optional)
34 /// Values: AUTH, NAUT, OTHR, RFDD, RTND
35 #[field("23E", optional)]
36 pub field_23e: Option<Field23E>,
37
38 /// **Registration Reference** - Field 21E (Conditional)
39 /// Subject to C3/C12 conditions
40 #[field("21E", optional)]
41 pub field_21e: Option<Field21>,
42
43 /// **Requested Execution Date** - Field 30 (Mandatory)
44 /// Format: YYMMDD
45 #[field("30", mandatory)]
46 pub field_30: Field30,
47
48 /// **Sending Institution** - Field 51A (Optional)
49 /// Only for FileAct
50 #[field("51A", optional)]
51 pub field_51a: Option<GenericBicField>,
52
53 /// **Instructing Party** - Field 50a Seq A (Conditional)
54 /// Options: C, L. Conditional C3 (if not present in any Seq B)
55 #[field("50A_INSTRUCTING", optional)]
56 pub field_50a_instructing: Option<Field50>,
57
58 /// **Creditor** - Field 50a Seq A (Conditional)
59 /// Options: A, K. Subject to C2, C4, C12
60 #[field("50A_CREDITOR", optional)]
61 pub field_50a_creditor: Option<Field50>,
62
63 /// **Creditor's Bank** - Field 52a Seq A (Conditional)
64 /// Options: A, C, D. Subject to C3, C12
65 #[field("52A", optional)]
66 pub field_52a: Option<GenericBicField>,
67
68 /// **Transaction Type Code** - Field 26T Seq A (Conditional)
69 /// Subject to C3
70 #[field("26T", optional)]
71 pub field_26t: Option<Field26T>,
72
73 /// **Regulatory Reporting** - Field 77B Seq A (Conditional)
74 /// Subject to C3
75 #[field("77B", optional)]
76 pub field_77b: Option<Field77B>,
77
78 /// **Details of Charges** - Field 71A Seq A (Conditional)
79 /// Values: BEN, OUR, SHA
80 #[field("71A", optional)]
81 pub field_71a: Option<Field71A>,
82
83 /// **Sender to Receiver Information** - Field 72 (Conditional)
84 /// Subject to C5
85 #[field("72", optional)]
86 pub field_72: Option<Field72>,
87
88 // ================================
89 // SEQUENCE B - TRANSACTION DETAILS (REPEATING)
90 // ================================
91 /// **Transaction Details** - Sequence B (Mandatory, Repetitive)
92 /// Each element represents one direct debit transaction
93 #[field("TRANSACTIONS", repetitive)]
94 pub transactions: Vec<MT104Transaction>,
95
96 // ================================
97 // SEQUENCE C - SETTLEMENT DETAILS (OPTIONAL)
98 // ================================
99 /// **Settlement Amount** - Field 32B Seq C (Optional)
100 /// Currency & Settlement Amount - Sum or explicit
101 #[field("32B", optional)]
102 pub field_32b: Option<GenericCurrencyAmountField>,
103
104 /// **Sum of Amounts** - Field 19 (Optional)
105 /// Required if 32B not total of B-32Bs
106 #[field("19", optional)]
107 pub field_19: Option<GenericBalanceField>,
108
109 /// **Sum of Sender's Charges** - Field 71F Seq C (Optional)
110 /// If 71F in B
111 #[field("71F", optional)]
112 pub field_71f: Option<GenericCurrencyAmountField>,
113
114 /// **Sum of Receiver's Charges** - Field 71G Seq C (Optional)
115 /// If 71G in B
116 #[field("71G", optional)]
117 pub field_71g: Option<GenericCurrencyAmountField>,
118
119 /// **Sender's Correspondent** - Field 53a (Optional)
120 /// Reimbursement instruction
121 #[field("53A", optional)]
122 pub field_53a: Option<GenericBicField>,
123}
124
125/// # MT104 Transaction (Sequence B)
126///
127/// Single direct debit transaction within an MT104 message.
128#[serde_swift_fields]
129#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, SwiftMessage)]
130#[validation_rules(MT104_TRANSACTION_VALIDATION_RULES)]
131pub struct MT104Transaction {
132 /// **Transaction Reference** - Field 21 (Mandatory)
133 /// Unique per transaction
134 #[field("21", mandatory)]
135 pub field_21: Field21,
136
137 /// **Instruction Code** - Field 23E Seq B (Conditional)
138 /// Depends on 23E in Seq A (C1)
139 #[field("23E", optional)]
140 pub field_23e: Option<Field23E>,
141
142 /// **Mandate Reference** - Field 21C (Optional)
143 /// Optional mandate info
144 #[field("21C", optional)]
145 pub field_21c: Option<Field21>,
146
147 /// **Direct Debit Reference** - Field 21D (Optional)
148 /// Optional ref for transaction
149 #[field("21D", optional)]
150 pub field_21d: Option<Field21>,
151
152 /// **Registration Reference** - Field 21E Seq B (Conditional)
153 /// C3 / C12
154 #[field("21E", optional)]
155 pub field_21e: Option<Field21>,
156
157 /// **Currency and Amount** - Field 32B (Mandatory)
158 /// ISO 4217 currency, comma for decimals
159 #[field("32B", mandatory)]
160 pub field_32b: GenericCurrencyAmountField,
161
162 /// **Instructing Party** - Field 50a Seq B (Conditional)
163 /// Must not appear if in Seq A (C3)
164 #[field("50A_INSTRUCTING", optional)]
165 pub field_50a_instructing: Option<Field50>,
166
167 /// **Creditor** - Field 50a Seq B (Conditional)
168 /// C2, C4, C12
169 #[field("50A_CREDITOR", optional)]
170 pub field_50a_creditor: Option<Field50>,
171
172 /// **Creditor's Bank** - Field 52a Seq B (Conditional)
173 /// C3, C12
174 #[field("52A", optional)]
175 pub field_52a: Option<GenericBicField>,
176
177 /// **Debtor's Bank** - Field 57a (Optional)
178 /// Optional
179 #[field("57A", optional)]
180 pub field_57a: Option<GenericBicField>,
181
182 /// **Debtor** - Field 59a (Mandatory)
183 /// Must include account
184 #[field("59A", mandatory)]
185 pub field_59a: Field59,
186
187 /// **Remittance Information** - Field 70 (Optional)
188 /// Codes: INV, IPI, RFB, ROC
189 #[field("70", optional)]
190 pub field_70: Option<Field70>,
191
192 /// **Transaction Type Code** - Field 26T Seq B (Conditional)
193 /// Purpose info
194 #[field("26T", optional)]
195 pub field_26t: Option<Field26T>,
196
197 /// **Regulatory Reporting** - Field 77B Seq B (Conditional)
198 /// Optional unless conflict with A
199 #[field("77B", optional)]
200 pub field_77b: Option<Field77B>,
201
202 /// **Original Ordered Amount** - Field 33B (Optional)
203 /// Must differ from 32B
204 #[field("33B", optional)]
205 pub field_33b: Option<GenericCurrencyAmountField>,
206
207 /// **Details of Charges** - Field 71A Seq B (Conditional)
208 /// Cond. C3
209 #[field("71A", optional)]
210 pub field_71a: Option<Field71A>,
211
212 /// **Sender's Charges** - Field 71F (Conditional)
213 /// C6, C12
214 #[field("71F", optional)]
215 pub field_71f: Option<GenericCurrencyAmountField>,
216
217 /// **Receiver's Charges** - Field 71G (Conditional)
218 /// C6, C12
219 #[field("71G", optional)]
220 pub field_71g: Option<GenericCurrencyAmountField>,
221
222 /// **Exchange Rate** - Field 36 (Conditional)
223 /// Required if 33B present & different from 32B
224 #[field("36", optional)]
225 pub field_36: Option<Field36>,
226}
227
228/// Enhanced validation rules with forEach support for repetitive sequences
229const MT104_VALIDATION_RULES: &str = r#"{
230 "rules": [
231 {
232 "id": "C1",
233 "description": "Per-transaction: If 36 present → 21F must be present (placeholder)",
234 "forEach": {
235 "collection": "transactions",
236 "condition": {
237 "if": [
238 {"var": "field_36.is_some"},
239 true,
240 true
241 ]
242 }
243 }
244 },
245 {
246 "id": "C3",
247 "description": "Various conditional field requirements",
248 "condition": {
249 ">=": [{"length": {"var": "transactions"}}, 1]
250 }
251 },
252 {
253 "id": "TXN_MIN",
254 "description": "At least one transaction required",
255 "condition": {
256 ">=": [{"length": {"var": "transactions"}}, 1]
257 }
258 }
259 ]
260}"#;
261
262/// Validation rules specific to MT104 transactions
263const MT104_TRANSACTION_VALIDATION_RULES: &str = r#"{
264 "rules": [
265 {
266 "id": "T_C1",
267 "description": "If exchange rate (36) is present, related conditions apply",
268 "condition": {
269 "if": [
270 {"var": "field_36.is_some"},
271 true,
272 true
273 ]
274 }
275 },
276 {
277 "id": "T_REF",
278 "description": "Transaction reference must be unique within the message",
279 "condition": {
280 "!=": [{"var": "field_21.value"}, ""]
281 }
282 }
283 ]
284}"#;