swift_mt_message/messages/
mt103.rs

1use crate::fields::*;
2use serde::{Deserialize, Serialize};
3use swift_mt_message_macros::{SwiftMessage, serde_swift_fields};
4
5/// MT103: Single Customer Credit Transfer
6///
7/// ## Purpose
8/// Used to convey funds transfer instructions between financial institutions where the ordering or beneficiary customer (or both) are non-financial institutions.
9/// This is the most common payment message in the SWIFT network for retail and commercial payments worldwide.
10///
11/// ## Scope
12/// This message is:
13/// - Used for clean payment instructions without additional documents
14/// - Applicable for cross-border payments between different countries/currencies
15/// - Used for high-value domestic transfers via SWIFT network
16/// - Not applicable for collection advices, documentary credits, or cover transactions
17/// - Compatible with STP (Straight Through Processing) requirements
18/// - Subject to comprehensive network validation rules for payment integrity
19///
20/// ## Key Features
21/// - **Universal Payment Format**: Most widely used SWIFT payment message globally
22/// - **STP Compliance**: Built-in support for straight-through processing requirements
23/// - **REMIT Support**: Enhanced remittance information using field 77T for regulatory compliance
24/// - **Service Level Options**: Multiple processing speeds and cost tiers available
25/// - **Charge Allocation Flexibility**: OUR/SHA/BEN charge allocation options
26/// - **Comprehensive Validation**: 18 network validation rules ensure message integrity
27///
28/// ## Common Use Cases
29/// - International wire transfers for trade settlements and remittances
30/// - Corporate payments for supplier settlements and salary transfers
31/// - High-value domestic payments requiring SWIFT network routing
32/// - Cross-border e-commerce and marketplace settlements
33/// - Foreign exchange spot and forward settlement payments
34/// - Investment and securities transaction settlements
35/// - Emergency and expedited payment transfers
36///
37/// ## Message Structure
38/// - **Field 20**: Sender's Reference (mandatory) - Unique transaction identifier
39/// - **Field 13C**: Time Indication (optional, repetitive) - Processing time constraints
40/// - **Field 23B**: Bank Operation Code (mandatory) - Service level (CRED/SPRI/SSTD/SPAY)
41/// - **Field 23E**: Instruction Code (optional, repetitive) - Special processing instructions
42/// - **Field 26T**: Transaction Type Code (optional) - Payment category classification
43/// - **Field 32A**: Value Date/Currency/Amount (mandatory) - Settlement details
44/// - **Field 33B**: Currency/Instructed Amount (optional) - Original currency before conversion
45/// - **Field 36**: Exchange Rate (optional) - Rate for currency conversion
46/// - **Field 50**: Ordering Customer (mandatory) - Customer initiating payment
47/// - **Field 51A**: Sending Institution (optional) - Institution sending the payment
48/// - **Field 52**: Ordering Institution (optional) - Institution of ordering customer
49/// - **Field 53**: Sender's Correspondent (optional) - Sender's correspondent bank
50/// - **Field 54**: Receiver's Correspondent (optional) - Receiver's correspondent bank  
51/// - **Field 56**: Intermediary Institution (optional) - Intermediary in payment chain
52/// - **Field 57**: Account With Institution (optional) - Institution crediting beneficiary
53/// - **Field 59**: Beneficiary Customer (mandatory) - Final recipient of funds
54/// - **Field 70**: Remittance Information (optional) - Payment purpose and details
55/// - **Field 71A**: Details of Charges (mandatory) - Charge allocation (OUR/SHA/BEN)
56/// - **Field 71F**: Sender's Charges (optional) - Charges claimed by sender
57/// - **Field 71G**: Receiver's Charges (optional) - Charges claimed by receiver
58/// - **Field 72**: Sender to Receiver Information (optional) - Additional instructions
59/// - **Field 77B**: Regulatory Reporting (optional) - Compliance reporting information
60/// - **Field 77T**: Envelope for Remittance Info (optional) - Structured remittance data
61///
62/// ## Network Validation Rules
63/// - **Exchange Rate Logic**: Field 36 requirements when currencies differ (C1)
64/// - **EU/EEA Requirements**: Field 33B validation for specific country combinations (C2)
65/// - **Service Level Compatibility**: Instruction code restrictions by service level (C3-C6)
66/// - **Correspondent Dependencies**: Field dependencies for correspondent institutions (C7)
67/// - **Charge Allocation**: Currency and charge consistency rules (C8-C9)
68/// - **STP Restrictions**: Service level limitations on intermediary fields (C10-C12)
69/// - **Enhanced Validation**: Detailed charge handling and instruction code rules (C14-C18)
70///
71/// ## SRG2025 Status
72/// - **Structural Changes**: None - MT103 format remains stable
73/// - **Enhanced STP**: Strengthened straight-through processing requirements
74/// - **REMIT Enhancement**: Improved structured remittance information support
75/// - **Regulatory Compliance**: Enhanced field 77B validation for reporting requirements
76///
77/// ## Integration Considerations
78/// - **Banking Systems**: Compatible with all major core banking and payment processing systems
79/// - **API Integration**: Full RESTful API support for modern payment platforms
80/// - **Processing Requirements**: Supports real-time, same-day, and next-day processing
81/// - **Compliance Integration**: Built-in AML, sanctions screening, and regulatory reporting hooks
82///
83/// ## Relationship to Other Messages
84/// - **Triggers**: Often triggered by customer payment instructions or corporate ERP systems
85/// - **Responses**: May generate MT910 (confirmation) or MT900 (debit confirmation)
86/// - **Related**: Works with MT202 (cover payment) and MT940/MT950 (account reporting)
87/// - **Alternatives**: MT101 (bulk transfers), MT200 (financial institution transfers)
88/// - **Status Updates**: May receive MT192/MT196/MT199 for status notifications
89#[serde_swift_fields]
90#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, SwiftMessage)]
91#[validation_rules(MT103_VALIDATION_RULES)]
92pub struct MT103 {
93    // Mandatory Fields
94    #[field("20")]
95    pub field_20: Field20,
96
97    #[field("13C")]
98    pub field_13c: Option<Vec<Field13C>>,
99
100    #[field("23B")]
101    pub field_23b: Field23B,
102
103    #[field("23E")]
104    pub field_23e: Option<Vec<Field23E>>,
105
106    #[field("26T")]
107    pub field_26t: Option<Field26T>,
108
109    #[field("32A")]
110    pub field_32a: Field32A,
111
112    #[field("33B")]
113    pub field_33b: Option<Field33B>,
114
115    #[field("36")]
116    pub field_36: Option<Field36>,
117
118    #[field("50")]
119    pub field_50: Field50OrderingCustomerAFK,
120
121    #[field("51A")]
122    pub field_51a: Option<Field51A>,
123
124    #[field("52")]
125    pub field_52: Option<Field52OrderingInstitution>,
126
127    #[field("53")]
128    pub field_53: Option<Field53SenderCorrespondent>,
129
130    #[field("54")]
131    pub field_54: Option<Field54ReceiverCorrespondent>,
132
133    #[field("55")]
134    pub field_55: Option<Field55ThirdReimbursementInstitution>,
135
136    #[field("56")]
137    pub field_56: Option<Field56Intermediary>,
138
139    #[field("57")]
140    pub field_57: Option<Field57AccountWithInstitution>,
141
142    #[field("59")]
143    pub field_59: Field59,
144
145    #[field("70")]
146    pub field_70: Option<Field70>,
147
148    #[field("71A")]
149    pub field_71a: Field71A,
150
151    #[field("71F")]
152    pub field_71f: Option<Vec<Field71F>>,
153
154    #[field("71G")]
155    pub field_71g: Option<Field71G>,
156
157    #[field("72")]
158    pub field_72: Option<Field72>,
159
160    #[field("77B")]
161    pub field_77b: Option<Field77B>,
162
163    #[field("77T")]
164    pub field_77t: Option<Field77T>,
165}
166
167impl MT103 {
168    /// Check if this MT103 message is compliant with STP (Straight Through Processing) requirements
169    ///
170    /// STP compliance requires specific options and restrictions when field 23B contains SPRI, SSTD, or SPAY:
171    /// - Field 23B contains SPRI/SSTD/SPAY: Enforces strict STP rules
172    /// - Field 53a: Must not be option D (C4)
173    /// - Field 53B: Party Identifier mandatory if option B used (C5)
174    /// - Field 54a: Only option A allowed (C6)
175    /// - Field 55a: Only option A allowed (C8)
176    /// - Field 56a: Not allowed if SPRI; only A or C if SSTD/SPAY (C10)
177    /// - Field 57a: Only options A, C, or D allowed; D requires Party Identifier (C11)
178    /// - Field 59a: Account mandatory (C12)
179    /// - Field 23E: Restricted codes for SPRI (C3)
180    pub fn is_stp_compliant(&self) -> bool {
181        // Check if this is an STP message (SPRI, SSTD, or SPAY)
182        let bank_op_code = &self.field_23b.instruction_code;
183        if !["SPRI", "SSTD", "SPAY"].contains(&bank_op_code.as_str()) {
184            // Not an STP message type
185            return true;
186        }
187
188        // C3: If 23B is SPRI, field 23E may contain only SDVA, TELB, PHOB, INTC
189        // If 23B is SSTD or SPAY, field 23E must not be used
190        if bank_op_code == "SPRI" {
191            if let Some(ref field_23e_vec) = self.field_23e {
192                let allowed_codes = ["SDVA", "TELB", "PHOB", "INTC"];
193                for field_23e in field_23e_vec {
194                    if !allowed_codes.contains(&field_23e.instruction_code.as_str()) {
195                        return false;
196                    }
197                }
198            }
199        } else if ["SSTD", "SPAY"].contains(&bank_op_code.as_str())
200            && self.field_23e.is_some()
201            && !self.field_23e.as_ref().unwrap().is_empty()
202        {
203            return false;
204        }
205
206        // C4: Field 53a must not be used with option D
207        if let Some(ref field_53) = self.field_53 {
208            if let Field53SenderCorrespondent::D(_) = field_53 {
209                return false;
210            }
211
212            // C5: If field 53a is option B, Party Identifier must be present
213            if let Field53SenderCorrespondent::B(field_53b) = field_53
214                && field_53b.party_identifier.is_none()
215            {
216                return false;
217            }
218        }
219
220        // C6: Field 54a may be used with option A only
221        if let Some(ref field_54) = self.field_54 {
222            match field_54 {
223                Field54ReceiverCorrespondent::A(_) => {}
224                _ => return false,
225            }
226        }
227
228        // C8: Field 55a may be used with option A only
229        if let Some(ref field_55) = self.field_55 {
230            match field_55 {
231                Field55ThirdReimbursementInstitution::A(_) => {}
232                _ => return false,
233            }
234        }
235
236        // C10: If 23B is SPRI, field 56a must not be present
237        // If 23B is SSTD or SPAY, field 56a may be used with option A or C only
238        if bank_op_code == "SPRI" {
239            if self.field_56.is_some() {
240                return false;
241            }
242        } else if ["SSTD", "SPAY"].contains(&bank_op_code.as_str())
243            && let Some(ref field_56) = self.field_56
244        {
245            match field_56 {
246                Field56Intermediary::A(_) | Field56Intermediary::C(_) => {}
247                _ => return false,
248            }
249        }
250
251        // C11: Field 57a may be used with option A, C or D
252        // In option D, Party Identifier is mandatory
253        if let Some(ref field_57) = self.field_57 {
254            match field_57 {
255                Field57AccountWithInstitution::A(_) | Field57AccountWithInstitution::C(_) => {}
256                Field57AccountWithInstitution::D(field_57d) => {
257                    if field_57d.party_identifier.is_none() {
258                        return false;
259                    }
260                }
261                _ => return false,
262            }
263        }
264
265        // C12: Account in field 59a is mandatory
266        match &self.field_59 {
267            Field59::NoOption(field_59) => {
268                if field_59.account.is_none() {
269                    return false;
270                }
271            }
272            Field59::A(field_59a) => {
273                if field_59a.account.is_none() {
274                    return false;
275                }
276            }
277            Field59::F(field_59f) => {
278                if field_59f.party_identifier.is_none() {
279                    return false;
280                }
281            }
282        }
283
284        // Additional STP-specific validation rules
285        // C7: If field 55a is present, then both fields 53a and 54a must also be present
286        if self.field_55.is_some() && (self.field_53.is_none() || self.field_54.is_none()) {
287            return false;
288        }
289
290        true
291    }
292
293    /// Check if this MT103 message is a REMIT message with enhanced remittance information
294    ///
295    /// REMIT messages are distinguished by:
296    /// - Field 77T must be present and contain structured remittance information
297    /// - Field 70 is typically not used (replaced by 77T)
298    /// - Enhanced remittance data for regulatory compliance
299    pub fn is_remit_message(&self) -> bool {
300        // The key distinguishing feature of REMIT is the presence of field 77T
301        // with structured remittance information
302        match &self.field_77t {
303            Some(field_77t) => {
304                // Check if 77T contains actual remittance data (not just empty)
305                !field_77t.envelope_content.trim().is_empty()
306            }
307            None => false,
308        }
309    }
310
311    /// Check if this MT103 message contains reject codes
312    ///
313    /// Reject messages are identified by checking:
314    /// 1. Field 20 (Sender's Reference) for "REJT" prefix
315    /// 2. Block 3 field 108 (MUR - Message User Reference) for "REJT"
316    /// 3. Field 72 (Sender to Receiver Information) containing `/REJT/` code
317    pub fn has_reject_codes(&self) -> bool {
318        // Check field 20 (sender's reference)
319        if self.field_20.reference.to_uppercase().contains("REJT") {
320            return true;
321        }
322
323        // Check field 72 for structured reject codes
324        if let Some(field_72) = &self.field_72 {
325            let content = field_72.information.join(" ").to_uppercase();
326            if content.contains("/REJT/") || content.contains("REJT") {
327                return true;
328            }
329        }
330
331        false
332    }
333
334    /// Check if this MT103 message contains return codes
335    ///
336    /// Return messages are identified by checking:
337    /// 1. Field 20 (Sender's Reference) for "RETN" prefix
338    /// 2. Block 3 field 108 (MUR - Message User Reference) for "RETN"
339    /// 3. Field 72 (Sender to Receiver Information) containing `/RETN/` code
340    pub fn has_return_codes(&self) -> bool {
341        // Check field 20 (sender's reference)
342        if self.field_20.reference.to_uppercase().contains("RETN") {
343            return true;
344        }
345
346        // Check field 72 for structured return codes
347        if let Some(field_72) = &self.field_72 {
348            let content = field_72.information.join(" ").to_uppercase();
349            if content.contains("/RETN/") || content.contains("RETN") {
350                return true;
351            }
352        }
353
354        false
355    }
356}
357
358/// Comprehensive MT103 validation rules based on SRG2025 specification
359const MT103_VALIDATION_RULES: &str = r#"{
360  "rules": [
361    {
362      "id": "C1",
363      "description": "If field 33B is present and the currency code is different from the currency code in field 32A, field 36 must be present, otherwise field 36 is not allowed",
364      "condition": {
365        "if": [
366          {"exists": ["fields", "33B"]},
367          {
368            "if": [
369              {"!=": [{"var": "fields.33B.currency"}, {"var": "fields.32A.currency"}]},
370              {"exists": ["fields", "36"]},
371              {"!": {"exists": ["fields", "36"]}}
372            ]
373          },
374          {"!": {"exists": ["fields", "36"]}}
375        ]
376      }
377    },
378    {
379      "id": "C2", 
380      "description": "If the country codes of the Sender's and the Receiver's BICs are within EU/EEA list, then field 33B is mandatory",
381      "condition": {
382        "if": [
383          {"and": [
384            {"in": [{"var": "basic_header.sender_bic.country_code"}, {"var": "EU_EEA_COUNTRIES"}]},
385            {"in": [{"var": "application_header.receiver_bic.country_code"}, {"var": "EU_EEA_COUNTRIES"}]}
386          ]},
387          {"exists": ["fields", "33B"]},
388          true
389        ]
390      }
391    },
392    {
393      "id": "C3",
394      "description": "If field 23B contains SPRI, field 23E may contain only SDVA, TELB, PHOB, INTC. If field 23B contains SSTD or SPAY, field 23E must not be used",
395      "condition": {
396        "and": [
397          {
398            "if": [
399              {"==": [{"var": "fields.23B.instruction_code"}, "SPRI"]},
400              {
401                "if": [
402                  {"exists": ["fields", "23E"]},
403                  {
404                    "all": [
405                      {"var": "fields.23E"},
406                      {
407                        "in": [{"var": "instruction_code"}, ["SDVA", "TELB", "PHOB", "INTC"]]
408                      }
409                    ]
410                  },
411                  true
412                ]
413              },
414              true
415            ]
416          },
417          {
418            "if": [
419              {"in": [{"var": "fields.23B.instruction_code"}, ["SSTD", "SPAY"]]},
420              {"!": {"exists": ["fields", "23E"]}},
421              true
422            ]
423          }
424        ]
425      }
426    },
427    {
428      "id": "C4",
429      "description": "If field 23B contains SPRI, SSTD or SPAY, field 53a must not be used with option D",
430      "condition": {
431        "if": [
432          {"in": [{"var": "fields.23B.instruction_code"}, ["SPRI", "SSTD", "SPAY"]]},
433          {
434            "if": [
435              {"exists": ["fields", "53", "D"]},
436              false,
437              true
438            ]
439          },
440          true
441        ]
442      }
443    },
444    {
445      "id": "C5",
446      "description": "If field 23B contains SPRI, SSTD or SPAY and field 53a is present with option B, Party Identifier must be present",
447      "condition": {
448        "if": [
449          {"and": [
450            {"in": [{"var": "fields.23B.instruction_code"}, ["SPRI", "SSTD", "SPAY"]]},
451            {"exists": ["fields", "53", "B"]}
452          ]},
453          {"exists": ["fields", "53", "B", "party_identifier"]},
454          true
455        ]
456      }
457    },
458    {
459      "id": "C6",
460      "description": "If field 23B contains SPRI, SSTD or SPAY, field 54a may be used with option A only",
461      "condition": {
462        "if": [
463          {"in": [{"var": "fields.23B.instruction_code"}, ["SPRI", "SSTD", "SPAY"]]},
464          {
465            "if": [
466              {"or": [
467                {"exists": ["fields", "54", "A"]},
468                {"!": {"exists": ["fields", "54"]}}
469              ]},
470              true,
471              false
472            ]
473          },
474          true
475        ]
476      }
477    },
478    {
479      "id": "C7",
480      "description": "If field 55a is present, then both fields 53a and 54a must also be present",
481      "condition": {
482        "if": [
483          {"exists": ["fields", "55"]},
484          {"and": [
485            {"exists": ["fields", "53"]},
486            {"exists": ["fields", "54"]}
487          ]},
488          true
489        ]
490      }
491    },
492    {
493      "id": "C8",
494      "description": "If field 23B contains SPRI, SSTD or SPAY, field 55a may be used with option A only",
495      "condition": {
496        "if": [
497          {"in": [{"var": "fields.23B.instruction_code"}, ["SPRI", "SSTD", "SPAY"]]},
498          {
499            "if": [
500              {"or": [
501                {"exists": ["fields", "55", "A"]},
502                {"!": {"exists": ["fields", "55"]}}
503              ]},
504              true,
505              false
506            ]
507          },
508          true
509        ]
510      }
511    },
512    {
513      "id": "C9",
514      "description": "If field 56a is present, field 57a must also be present",
515      "condition": {
516        "if": [
517          {"exists": ["fields", "56"]},
518          {"exists": ["fields", "57"]},
519          true
520        ]
521      }
522    },
523    {
524      "id": "C10",
525      "description": "If field 23B contains SPRI, field 56a must not be present. If field 23B contains SSTD or SPAY, field 56a may be used with option A or C only",
526      "condition": {
527        "and": [
528          {
529            "if": [
530              {"==": [{"var": "fields.23B.instruction_code"}, "SPRI"]},
531              {"!": {"exists": ["fields", "56"]}},
532              true
533            ]
534          },
535          {
536            "if": [
537              {"and": [
538                {"in": [{"var": "fields.23B.instruction_code"}, ["SSTD", "SPAY"]]},
539                {"or": [
540                  {"exists": ["fields", "56", "A"]},
541                  {"exists": ["fields", "56", "C"]},
542                  {"exists": ["fields", "56", "D"]}
543                ]}
544              ]},
545              {"or": [
546                {"exists": ["fields", "56", "A"]},
547                {"exists": ["fields", "56", "C"]}
548              ]},
549              true
550            ]
551          }
552        ]
553      }
554    },
555    {
556      "id": "C11",
557      "description": "If field 23B contains SPRI, SSTD or SPAY, field 57a may be used with option A, C or D. In option D, Party Identifier is mandatory",
558      "condition": {
559        "if": [
560          {"and": [
561            {"in": [{"var": "fields.23B.instruction_code"}, ["SPRI", "SSTD", "SPAY"]]},
562            {"or": [
563              {"exists": ["fields", "57", "A"]},
564              {"exists": ["fields", "57", "B"]},
565              {"exists": ["fields", "57", "C"]},
566              {"exists": ["fields", "57", "D"]}
567            ]}
568          ]},
569          {"and": [
570            {"or": [
571              {"exists": ["fields", "57", "A"]},
572              {"exists": ["fields", "57", "C"]},
573              {"exists": ["fields", "57", "D"]}
574            ]},
575            {
576              "if": [
577                {"exists": ["fields", "57", "D"]},
578                {"exists": ["fields", "57", "D", "party_identifier"]},
579                true
580              ]
581            }
582          ]},
583          true
584        ]
585      }
586    },
587    {
588      "id": "C12",
589      "description": "If field 23B contains SPRI, SSTD or SPAY, Account in field 59a is mandatory",
590      "condition": {
591        "if": [
592          {"in": [{"var": "fields.23B.instruction_code"}, ["SPRI", "SSTD", "SPAY"]]},
593          {"or": [
594            {"exists": ["fields", "59", "A", "account"]},
595            {"exists": ["fields", "59", "NoOption", "account"]},
596            {"exists": ["fields", "59", "F", "party_identifier"]}
597          ]},
598          true
599        ]
600      }
601    },
602    {
603      "id": "C13",
604      "description": "If any field 23E contains CHQB, Account in field 59a is not allowed",
605      "condition": {
606        "if": [
607          {"and": [
608            {"exists": ["fields", "23E"]},
609            {
610              "some": [
611                {"var": "fields.23E"},
612                {"==": [{"var": "instruction_code"}, "CHQB"]}
613              ]
614            }
615          ]},
616          {"and": [
617            {"!": {"exists": ["fields", "59", "A", "account"]}},
618            {"!": {"exists": ["fields", "59", "NoOption", "account"]}}
619          ]},
620          true
621        ]
622      }
623    },
624    {
625      "id": "C14",
626      "description": "If field 71A contains OUR, then field 71F is not allowed and field 71G is optional. If field 71A contains SHA, then field 71F is optional and field 71G is not allowed. If field 71A contains BEN, then at least one occurrence of field 71F is mandatory and field 71G is not allowed",
627      "condition": {
628        "and": [
629          {
630            "if": [
631              {"==": [{"var": "fields.71A.code"}, "OUR"]},
632              {"!": {"exists": ["fields", "71F"]}},
633              true
634            ]
635          },
636          {
637            "if": [
638              {"==": [{"var": "fields.71A.code"}, "SHA"]},
639              {"!": {"exists": ["fields", "71G"]}},
640              true
641            ]
642          },
643          {
644            "if": [
645              {"==": [{"var": "fields.71A.code"}, "BEN"]},
646              {"and": [
647                {"exists": ["fields", "71F"]},
648                {">": [{"var": "fields.71F.length"}, 0]},
649                {"!": {"exists": ["fields", "71G"]}}
650              ]},
651              true
652            ]
653          }
654        ]
655      }
656    },
657    {
658      "id": "C15",
659      "description": "If either field 71F (at least one occurrence) or field 71G is present, then field 33B is mandatory",
660      "condition": {
661        "if": [
662          {"or": [
663            {"exists": ["fields", "71F"]},
664            {"exists": ["fields", "71G"]}
665          ]},
666          {"exists": ["fields", "33B"]},
667          true
668        ]
669      }
670    },
671    {
672      "id": "C16",
673      "description": "If field 56a is not present, no field 23E may contain TELI or PHOI",
674      "condition": {
675        "if": [
676          {"!": {"exists": ["fields", "56"]}},
677          {
678            "if": [
679              {"exists": ["fields", "23E"]},
680              {
681                "none": [
682                  {"var": "fields.23E"},
683                  {"in": [{"var": "instruction_code"}, ["TELI", "PHOI"]]}
684                ]
685              },
686              true
687            ]
688          },
689          true
690        ]
691      }
692    },
693    {
694      "id": "C17",
695      "description": "If field 57a is not present, no field 23E may contain TELE or PHON",
696      "condition": {
697        "if": [
698          {"!": {"exists": ["fields", "57"]}},
699          {
700            "if": [
701              {"exists": ["fields", "23E"]},
702              {
703                "none": [
704                  {"var": "fields.23E"},
705                  {"in": [{"var": "instruction_code"}, ["TELE", "PHON"]]}
706                ]
707              },
708              true
709            ]
710          },
711          true
712        ]
713      }
714    },
715    {
716      "id": "C18",
717      "description": "The currency code in the fields 71G and 32A must be the same",
718      "condition": {
719        "if": [
720          {"exists": ["fields", "71G"]},
721          {"==": [{"var": "fields.71G.currency"}, {"var": "fields.32A.currency"}]},
722          true
723        ]
724      }
725    },
726    {
727      "id": "INSTRUCTION_CODE_VALIDATION",
728      "description": "23E instruction codes must be valid when present",
729      "condition": {
730        "if": [
731          {"exists": ["fields", "23E"]},
732          {
733            "all": [
734              {"var": "fields.23E"},
735              {"in": [{"var": "instruction_code"}, ["CORT", "INTC", "REPA", "SDVA", "CHQB", "PHOB", "PHOI", "PHON", "TELE", "TELI", "TELB"]]}
736            ]
737          },
738          true
739        ]
740      }
741    },
742    {
743      "id": "AMOUNT_CONSISTENCY",
744      "description": "All amounts must be positive and properly formatted",
745      "condition": {
746        "and": [
747          {">": [{"var": "fields.32A.amount"}, 0]},
748          {
749            "if": [
750              {"exists": ["fields", "33B"]},
751              {">": [{"var": "fields.33B.amount"}, 0]},
752              true
753            ]
754          },
755          {
756            "if": [
757              {"exists": ["fields", "71F"]},
758              {
759                "all": [
760                  {"var": "fields.71F"},
761                  {">": [{"var": "amount"}, 0]}
762                ]
763              },
764              true
765            ]
766          },
767          {
768            "if": [
769              {"exists": ["fields", "71G"]},
770              {">": [{"var": "fields.71G.amount"}, 0]},
771              true
772            ]
773          }
774        ]
775      }
776    },
777    {
778      "id": "CURRENCY_CODE_VALIDATION",
779      "description": "All currency codes must be valid ISO 4217 3-letter codes",
780      "condition": {
781        "and": [
782          {"!=": [{"var": "fields.32A.currency"}, ""]},
783          {
784            "if": [
785              {"exists": ["fields", "33B"]},
786              {"!=": [{"var": "fields.33B.currency"}, ""]},
787              true
788            ]
789          },
790          {
791            "if": [
792              {"exists": ["fields", "71F"]},
793              {
794                "all": [
795                  {"var": "fields.71F"},
796                  {"!=": [{"var": "currency"}, ""]}
797                ]
798              },
799              true
800            ]
801          },
802          {
803            "if": [
804              {"exists": ["fields", "71G"]},
805              {"!=": [{"var": "fields.71G.currency"}, ""]},
806              true
807            ]
808          }
809        ]
810      }
811    },
812    {
813      "id": "REFERENCE_FORMAT",
814      "description": "Reference fields must not contain invalid patterns",
815      "condition": {
816        "and": [
817          {"!=": [{"var": "fields.20.reference"}, ""]},
818          {"!": {"in": ["//", {"var": "fields.20.reference"}]}}
819        ]
820      }
821    },
822    {
823      "id": "BIC_VALIDATION",
824      "description": "All BIC codes must be properly formatted (non-empty)",
825      "condition": {
826        "and": [
827          {"!=": [{"var": "basic_header.sender_bic"}, ""]},
828          {"!=": [{"var": "application_header.receiver_bic"}, ""]},
829          {
830            "if": [
831              {"exists": ["fields", "52", "A"]},
832              {"!=": [{"var": "fields.52.A.bic"}, ""]},
833              true
834            ]
835          },
836          {
837            "if": [
838              {"exists": ["fields", "53", "A"]},
839              {"!=": [{"var": "fields.53.A.bic"}, ""]},
840              true
841            ]
842          },
843          {
844            "if": [
845              {"exists": ["fields", "54", "A"]},
846              {"!=": [{"var": "fields.54.A.bic"}, ""]},
847              true
848            ]
849          },
850          {
851            "if": [
852              {"exists": ["fields", "55", "A"]},
853              {"!=": [{"var": "fields.55.A.bic"}, ""]},
854              true
855            ]
856          },
857          {
858            "if": [
859              {"exists": ["fields", "56", "A"]},
860              {"!=": [{"var": "fields.56.A.bic"}, ""]},
861              true
862            ]
863          },
864          {
865            "if": [
866              {"exists": ["fields", "57", "A"]},
867              {"!=": [{"var": "fields.57.A.bic"}, ""]},
868              true
869            ]
870          }
871        ]
872      }
873    },
874    {
875      "id": "REMIT_77T",
876      "description": "REMIT: If 77T is present, it must contain valid structured remittance information",
877      "condition": {
878        "if": [
879          {"exists": ["fields", "77T"]},
880          {"and": [
881            {"!=": [{"var": "fields.77T.envelope_content"}, ""]}
882          ]},
883          true
884        ]
885      }
886    }
887  ],
888  "constants": {
889    "EU_EEA_COUNTRIES": ["AD", "AT", "BE", "BG", "BV", "CH", "CY", "CZ", "DE", "DK", "ES", "EE", "FI", "FR", "GB", "GF", "GI", "GP", "GR", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MC", "MQ", "MT", "NL", "NO", "PL", "PM", "PT", "RE", "RO", "SE", "SI", "SJ", "SK", "SM", "TF", "VA"],
890    "VALID_BANK_OPERATION_CODES": ["CRED", "CRTS", "SPAY", "SPRI", "SSTD"],
891    "VALID_CHARGE_CODES": ["OUR", "SHA", "BEN"],
892    "VALID_INSTRUCTION_CODES": ["CORT", "INTC", "REPA", "SDVA", "CHQB", "PHOB", "PHOI", "PHON", "TELE", "TELI", "TELB"]
893  }
894}"#;