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}"#;