swift_mt_message/messages/
mt103.rs

1use crate::{SwiftMessage, fields::*, swift_serde};
2use serde::{Deserialize, Serialize};
3
4/// # MT103: Single Customer Credit Transfer
5///
6/// ## Overview
7/// MT103 is the most widely used SWIFT message type for single customer credit transfers,
8/// enabling the secure and standardized transfer of funds between financial institutions
9/// on behalf of their customers. This message type facilitates cross-border payments,
10/// domestic wire transfers, and serves as the backbone of the global payments infrastructure.
11/// The MT103 supports various processing modes including standard processing, STP
12/// (Straight Through Processing), and REMIT (structured remittance information).
13///
14/// ## Message Type Specification
15/// **Message Type**: `103`  
16/// **Category**: Customer Payments and Cheques (Category 1)  
17/// **Usage**: Single Customer Credit Transfer  
18/// **Processing**: Real-time gross settlement (RTGS) and net settlement  
19/// **Network**: SWIFT FIN (Financial network)  
20///
21/// ### Message Variants
22/// ```text
23/// MT103        - Standard single customer credit transfer
24/// MT103.STP    - Straight Through Processing variant (automated)
25/// MT103.REMIT  - Enhanced remittance information variant
26/// MT103.COV    - Cover message for correspondent banking
27/// ```
28///
29/// ## Message Structure
30/// The MT103 message consists of mandatory and optional fields organized in a specific sequence:
31///
32/// ### Mandatory Fields (Core Requirements)
33/// - **Field 20**: Transaction Reference Number (sender's unique reference)
34/// - **Field 23B**: Bank Operation Code (processing instruction)
35/// - **Field 32A**: Value Date/Currency/Amount (settlement details)
36/// - **Field 50A/F/K**: Ordering Customer (payment originator)
37/// - **Field 59A/No Option**: Beneficiary Customer (payment recipient)
38/// - **Field 71A**: Details of Charges (charge allocation)
39///
40/// ### Optional Fields (Enhanced Processing)
41/// ```text
42/// Field 13C   - Time Indication (processing timing)
43/// Field 23E   - Instruction Code (special instructions)
44/// Field 26T   - Transaction Type Code (regulatory reporting)
45/// Field 33B   - Currency/Instructed Amount (original amount)
46/// Field 36    - Exchange Rate (FX conversion rate)
47/// Field 51A   - Sending Institution (message sender)
48/// Fields 52A/D - Ordering Institution (customer's bank)
49/// Fields 53A/B/D - Sender's Correspondent (correspondent bank)
50/// Fields 54A/B/D - Receiver's Correspondent (receiving correspondent)  
51/// Fields 55A/B/D - Third Reimbursement Institution (reimbursement bank)
52/// Fields 56A/C/D - Intermediary Institution (routing bank)
53/// Fields 57A/B/C/D - Account With Institution (beneficiary's bank)
54/// Field 70    - Remittance Information (payment details)
55/// Fields 71F/G - Charges (sender/receiver charges)
56/// Field 72    - Sender to Receiver Information (processing instructions)
57/// Field 77B   - Regulatory Reporting (compliance information)
58/// Field 77T   - Envelope Contents (MT103.REMIT only)
59/// ```
60///
61/// ## Business Applications
62///
63/// ### Primary Use Cases
64/// - **Cross-border payments**: International wire transfers between countries
65/// - **Domestic high-value transfers**: Large-value same-currency transfers
66/// - **Trade finance settlements**: Payment for goods and services in international trade
67/// - **Treasury operations**: Interbank funding and liquidity management
68/// - **Corporate payments**: Business-to-business payment settlements
69/// - **Retail banking**: High-value customer-initiated transfers
70///
71/// ### Industry Sectors
72/// - **Banking**: Correspondent banking relationships and customer services
73/// - **Corporate Treasury**: Multinational corporation payment processing
74/// - **Trade Finance**: Letters of credit and trade settlement
75/// - **Investment Banking**: Securities settlement and margin calls
76/// - **Insurance**: Claims settlement and premium payments
77/// - **Real Estate**: Property purchase and mortgage settlements
78///
79/// ## Message Variants Deep Dive
80///
81/// ### MT103 Core (Standard Processing)
82/// - Full flexibility in field usage and institutional routing
83/// - Manual intervention allowed at any processing stage
84/// - Support for all field options and correspondent relationships
85/// - Traditional correspondent banking model support
86/// - Detailed remittance information in Field 70
87///
88/// ### MT103.STP (Straight Through Processing)
89/// **STP Compliance Requirements:**
90/// - All institutional fields (52, 53, 54, 55, 56, 57) must use Option A (BIC only)
91/// - Field 51A (Sending Institution) is **prohibited**
92/// - Field 23E limited to specific codes: CORT, INTC, SDVA, REPA
93/// - Field 72 cannot contain RETN/REJT codes or ERI information
94/// - Automated end-to-end processing without manual intervention
95/// - Enhanced data quality and faster settlement times
96/// - Reduced operational costs and processing errors
97///
98/// ### MT103.REMIT (Enhanced Remittance)
99/// **REMIT Specific Features:**
100/// - Field 77T (Envelope Contents) is **mandatory**
101/// - Field 70 (Remittance Information) is **prohibited**
102/// - Structured remittance data for automated reconciliation
103/// - Enhanced invoice and payment matching capabilities
104/// - Support for detailed remittance advice
105/// - Regulatory compliance for electronic invoicing
106///
107/// ## Routing and Settlement Patterns
108///
109/// ### Direct Settlement
110/// ```text
111/// Ordering Bank → Account With Institution
112/// (Fields 52A → 57A)
113/// ```
114///
115/// ### Correspondent Banking Chain
116/// ```text
117/// Ordering Bank → Sender's Correspondent → Receiver's Correspondent → Account With Institution
118/// (Fields 52A → 53A → 54A → 57A)
119/// ```
120///
121/// ### Complex Multi-Institution Routing
122/// ```text
123/// Ordering Bank → Sender's Correspondent → Third Reimbursement →
124/// Intermediary → Receiver's Correspondent → Account With Institution
125/// (Fields 52A → 53A → 55A → 56A → 54A → 57A)
126/// ```
127///
128/// ## Field Relationships and Dependencies
129///
130/// ### Cross-Currency Transactions
131/// - **Field 33B** (Instructed Amount) required if different from Field 32A currency
132/// - **Field 36** (Exchange Rate) mandatory for currency conversion
133/// - Enhanced regulatory reporting may be required in Field 77B
134///
135/// ### Charge Processing
136/// - **Field 71A** (Details of Charges): OUR/BEN/SHA allocation
137/// - **Field 71F** (Sender's Charges): Specific sender charge amounts
138/// - **Field 71G** (Receiver's Charges): Specific receiver charge amounts
139/// - Charge fields must be consistent with Field 71A allocation
140///
141/// ### Institutional Routing Validation
142/// - BIC codes must be valid and active in SWIFT directory
143/// - Correspondent relationships must exist between institutions
144/// - Account numbers must be valid for the specified institution
145/// - Routing must comply with sanctions and compliance rules
146///
147/// ## Validation Rules and Compliance
148///
149/// ### Network Validated Rules (SWIFT Standards)
150/// - **T27**: BIC codes must be registered and active
151/// - **T11**: Date formats must be valid (YYMMDD)
152/// - **T40**: Currency codes must be valid ISO 4217
153/// - **T43**: Amount formats must follow currency precision rules
154/// - **T61**: All characters must be from SWIFT character set
155/// - **C32**: Field 32A amount must be positive
156/// - **C50**: Ordering customer format must be consistent
157/// - **C51**: Field 51A not allowed in MT103.STP
158/// - **C59**: Beneficiary customer format must be consistent
159/// - **C71**: Charge codes must be valid (OUR/BEN/SHA)
160///
161/// ### Business Rule Validations
162/// - Transaction reference (Field 20) should be unique per day per sender
163/// - Value date (Field 32A) should be valid business day for currency
164/// - Exchange rate (Field 36) should be reasonable for currency pair
165/// - Institutional chain should form valid correspondent relationships
166/// - Remittance information should comply with character set restrictions
167/// - Field 72 structured codes should follow SWIFT format specifications
168///
169/// ### Regulatory Compliance
170/// - **Anti-Money Laundering (AML)**: Customer identification and screening
171/// - **Know Your Customer (KYC)**: Enhanced due diligence requirements
172/// - **Sanctions Screening**: OFAC, EU, UN, and local sanctions lists
173/// - **Regulatory Reporting**: CTR, SAR, and jurisdiction-specific reports
174/// - **Data Privacy**: GDPR, PCI-DSS, and financial privacy regulations
175/// - **Cross-Border Reporting**: Balance of payments and statistical reporting
176///
177/// ## Error Handling and Return Scenarios
178///
179/// ### Field 72 Structured Codes for Returns/Rejects
180/// ```text
181/// /RETN/AC01/    - Account identifier incorrect
182/// /RETN/AC04/    - Account closed
183/// /RETN/AC06/    - Account blocked
184/// /RETN/AG01/    - Credit transfer forbidden on this account
185/// /RETN/AM05/    - Duplication of payment
186/// /RETN/BE05/    - Party in the payment chain unknown
187/// /RETN/CURR/    - Incorrect currency
188/// /RETN/DT01/    - Invalid date
189/// /RETN/RF01/    - Not unique end-to-end reference
190/// /REJT/AC01/    - Account identifier incorrect (rejected)
191/// /REJT/AC03/    - Account identifier invalid
192/// /REJT/AG03/    - Transaction type not supported
193/// /REJT/RR01/    - Missing debtor account
194/// ```
195///
196/// ### Processing Status Indicators
197/// - **ACSC**: AcceptedSettlementCompleted
198/// - **ACSP**: AcceptedSettlementInProcess  
199/// - **PDNG**: Pending
200/// - **RJCT**: Rejected
201/// - **CANC**: Cancelled
202/// - **ACWC**: AcceptedWithChange
203///
204/// ## Code Examples
205///
206/// ### Basic MT103 Creation
207/// ```rust
208/// use swift_mt_message::{messages::MT103, fields::*};
209/// use chrono::NaiveDate;
210///
211/// // Create mandatory fields
212/// let field_20 = Field20::new("FT21234567890".to_string());
213/// let field_23b = Field23B::new("CRED".to_string());
214/// let field_32a = Field32A::new(
215///     NaiveDate::from_ymd_opt(2024, 3, 15).unwrap(),
216///     "USD".to_string(),
217///     1000000.00
218/// );
219/// let field_50 = Field50::K(Field50K::new(vec![
220///     "ACME CORPORATION".to_string(),
221///     "123 BUSINESS AVENUE".to_string(),
222///     "NEW YORK NY 10001".to_string()
223/// ]).unwrap());
224/// let field_59 = Field59::A(Field59A::new(
225///     Some("GB33BUKB20201555555555".to_string()),
226///     "DEUTDEFF"
227/// ).unwrap());
228/// let field_71a = Field71A::new("OUR".to_string());
229///
230/// // Create basic MT103
231/// let mt103 = MT103::new(
232///     field_20, field_23b, field_32a, field_50, field_59, field_71a
233/// );
234/// ```
235///
236/// ### MT103.STP Compliant Message
237/// ```rust
238/// use swift_mt_message::{messages::MT103, fields::*};
239/// use chrono::NaiveDate;
240///
241/// // Create required mandatory fields
242/// let field_20 = Field20::new("FT21034567890123".to_string());
243/// let field_23b = Field23B::new("CRED".to_string());
244/// let field_32a = Field32A::new(
245///     NaiveDate::from_ymd_opt(2024, 3, 15).unwrap(),
246///     "USD".to_string(),
247///     1000000.00
248/// );
249/// let field_50 = Field50::K(Field50K::new(vec!["ACME CORPORATION".to_string()]).unwrap());
250/// let field_59 = Field59::NoOption(Field59Basic::new(vec!["BENEFICIARY NAME".to_string()]).unwrap());
251/// let field_71a = Field71A::new("OUR".to_string());
252///
253/// // STP-compliant MT103 with institutional Option A fields only
254/// let mt103_stp = MT103::new_complete(
255///     field_20, field_23b, field_32a, field_50, field_59, field_71a,
256///     None, // field_13c
257///     Some(Field23E::new("INTC", None).unwrap()), // STP-allowed code
258///     None, // field_26t
259///     None, // field_33b
260///     None, // field_36
261///     None, // field_51a - NOT allowed in STP
262///     Some(Field52A::new(None, None, "CHASUS33XXX").unwrap()),
263///     None, // field_52d - NOT allowed in STP
264///     Some(Field53A::new(None, None, "DEUTDEFFXXX").unwrap()),
265///     None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None
266/// );
267///
268/// assert!(mt103_stp.is_stp_compliant());
269/// ```
270///
271/// ### MT103.REMIT Message
272/// ```rust
273/// use swift_mt_message::{messages::MT103, fields::*};
274/// use chrono::NaiveDate;
275///
276/// // Create required mandatory fields
277/// let field_20 = Field20::new("FT21034567890123".to_string());
278/// let field_23b = Field23B::new("CRED".to_string());
279/// let field_32a = Field32A::new(
280///     NaiveDate::from_ymd_opt(2024, 3, 15).unwrap(),
281///     "USD".to_string(),
282///     1000000.00
283/// );
284/// let field_50 = Field50::K(Field50K::new(vec!["ACME CORPORATION".to_string()]).unwrap());
285/// let field_59 = Field59::NoOption(Field59Basic::new(vec!["BENEFICIARY NAME".to_string()]).unwrap());
286/// let field_71a = Field71A::new("OUR".to_string());
287///
288/// // MT103.REMIT with structured remittance data
289/// let field_77t = Field77T::new("R", "D", "REMITTANCE-2024-001").unwrap();
290///
291/// let mt103_remit = MT103::new_complete(
292///     field_20, field_23b, field_32a, field_50, field_59, field_71a,
293///     None, None, None, None, None, None, None, None, None, None, None,
294///     None, None, None, None, None, None, None, None, None, None, None,
295///     None, None,
296///     None, // field_70 - NOT allowed in REMIT
297///     None, None, None, None,
298///     Some(field_77t) // field_77t - MANDATORY in REMIT
299/// );
300///
301/// assert!(mt103_remit.is_remit());
302/// assert!(mt103_remit.is_remit_compliant());
303/// ```
304///
305/// ### Cross-Currency Transaction
306/// ```rust
307/// use swift_mt_message::{messages::MT103, fields::*};
308/// use chrono::NaiveDate;
309///
310/// // Create required mandatory fields
311/// let field_20 = Field20::new("FT21034567890123".to_string());
312/// let field_23b = Field23B::new("CRED".to_string());
313/// let field_50 = Field50::K(Field50K::new(vec!["ACME CORPORATION".to_string()]).unwrap());
314/// let field_59 = Field59::NoOption(Field59Basic::new(vec!["BENEFICIARY NAME".to_string()]).unwrap());
315/// let field_71a = Field71A::new("OUR".to_string());
316///
317/// // EUR to USD conversion with exchange rate
318/// let field_32a = Field32A::new(
319///     NaiveDate::from_ymd_opt(2024, 3, 15).unwrap(),
320///     "USD".to_string(),
321///     1000000.00 // Settlement amount in USD
322/// );
323/// let field_33b = Field33B::new("EUR", 850000.00).unwrap(); // Original EUR amount
324/// let field_36 = Field36::new(1.1765).unwrap(); // EUR/USD rate
325///
326/// let mt103_fx = MT103::new_complete(
327///     field_20, field_23b, field_32a, field_50, field_59, field_71a,
328///     None, None, None,
329///     Some(field_33b), // Original instructed amount
330///     Some(field_36),  // Exchange rate
331///     None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None
332/// );
333///
334/// assert!(mt103_fx.is_cross_currency());
335/// assert!(mt103_fx.has_required_exchange_rate());
336/// ```
337///
338///
339/// Complete implementation with all possible MT103 fields for 100% compliance
340/// Supports STP (Straight Through Processing), RETN (Return), and REJT (Reject) scenarios
341///
342/// Uses normalized field tags (without option letters) for flexibility while supporting
343/// all possible option variations through enum types for institutional fields.
344#[swift_serde]
345#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, SwiftMessage)]
346#[swift_message(mt = "103")]
347pub struct MT103 {
348    // ================================
349    // MANDATORY FIELDS (Required for all MT103 messages)
350    // ================================
351    /// **Transaction Reference Number** - Field 20
352    ///
353    /// Unique sender's reference identifying this specific payment transaction.
354    /// Used throughout the payment lifecycle for tracking, reconciliation, and audit.
355    /// Must be unique within the sender's system per business day.
356    ///
357    /// **Format**: Up to 16 alphanumeric characters  
358    /// **Usage**: Mandatory in all MT103 variants  
359    /// **Business Rule**: Should follow sender's reference numbering scheme
360    #[field("20")]
361    pub field_20: Field20,
362
363    /// **Bank Operation Code** - Field 23B
364    ///
365    /// Specifies the type of banking operation being performed.
366    /// Determines processing rules, routing behavior, and regulatory requirements.
367    ///
368    /// **Format**: Exactly 4 alphabetic characters  
369    /// **Common Values**: CRED (Credit Transfer), CRTS (Credit Transfer Same Day)  
370    /// **STP Impact**: Affects automated processing eligibility  
371    /// **Usage**: Mandatory in all MT103 variants
372    #[field("23B")]
373    pub field_23b: Field23B,
374
375    /// **Value Date/Currency/Amount** - Field 32A
376    ///
377    /// Core settlement details specifying when, in what currency, and for what amount
378    /// the transaction should be processed. The value date determines when funds
379    /// become available to the beneficiary.
380    ///
381    /// **Components**: Date (YYMMDD) + Currency (3 chars) + Amount (decimal)  
382    /// **Business Rule**: Value date should be valid business day for currency  
383    /// **Settlement**: Determines actual settlement timing  
384    /// **Usage**: Mandatory in all MT103 variants
385    #[field("32A")]
386    pub field_32a: Field32A,
387
388    /// **Ordering Customer** - Field 50A/F/K
389    ///
390    /// Identifies the party that orders the credit transfer (payment originator).
391    /// This is typically the customer on whose behalf the payment is being made.
392    /// Different options provide varying levels of detail and identification methods.
393    ///
394    /// **Options**: A (Account+BIC), F (Party ID+Name/Address), K (Name/Address only)  
395    /// **KYC Impact**: Critical for customer identification and compliance  
396    /// **AML Requirement**: Must contain sufficient information for screening  
397    /// **Usage**: Mandatory in all MT103 variants
398    #[field("50")]
399    pub field_50: Field50,
400
401    /// **Beneficiary Customer** - Field 59A/No Option
402    ///
403    /// Identifies the ultimate recipient of the credit transfer.
404    /// This is the final beneficiary who will receive the funds being transferred.
405    /// Proper identification is crucial for successful payment delivery.
406    ///
407    /// **Options**: A (Account+BIC), No Option (Account+Name/Address)  
408    /// **Delivery**: Critical for successful payment completion  
409    /// **Compliance**: Subject to sanctions screening and validation  
410    /// **Usage**: Mandatory in all MT103 variants
411    #[field("59")]
412    pub field_59: Field59,
413
414    /// **Details of Charges** - Field 71A
415    ///
416    /// Specifies how transaction charges should be allocated between the
417    /// ordering customer, beneficiary customer, and any intermediary banks.
418    ///
419    /// **Values**: OUR (sender pays), BEN (receiver pays), SHA (shared)  
420    /// **Impact**: Affects final amount received by beneficiary  
421    /// **Regulatory**: May be restricted in certain jurisdictions  
422    /// **Usage**: Mandatory in all MT103 variants
423    #[field("71A")]
424    pub field_71a: Field71A,
425
426    // ================================
427    // OPTIONAL FIELDS (Enhanced processing and compliance support)
428    // Complete implementation for 100% MT103 compliance including STP/RETN/REJT scenarios
429    // ================================
430    /// **Time Indication** - Field 13C (Optional)
431    ///
432    /// Provides specific timing instructions for payment processing,
433    /// including target execution times and UTC offset information.
434    ///
435    /// **Usage**: Optional, used for time-sensitive payments  
436    /// **STP Impact**: May affect automated processing timing  
437    /// **Format**: Time + UTC offsets for coordination  
438    /// **Business Value**: Enables precise timing control
439    #[field("13C")]
440    pub field_13c: Option<Field13C>,
441
442    /// **Instruction Code** - Field 23E (Optional)
443    ///
444    /// Specifies special processing instructions for the transaction,
445    /// such as communication requirements or handling procedures.
446    ///
447    /// **STP Restriction**: Limited to CORT, INTC, SDVA, REPA in STP messages  
448    /// **Usage**: Optional, affects processing workflow  
449    /// **Common Codes**: INTC (intracompany), HOLD (hold payment), PHON (phone)  
450    /// **Processing Impact**: May require manual intervention
451    #[field("23E")]
452    pub field_23e: Option<Field23E>,
453
454    /// **Transaction Type Code** - Field 26T (Optional)
455    ///
456    /// Regulatory reporting code for balance of payments and statistical purposes.
457    /// Required in certain jurisdictions for cross-border payments reporting.
458    ///
459    /// **Format**: 3-character alphanumeric code  
460    /// **Regulatory**: Required for BoP reporting in many jurisdictions  
461    /// **Categories**: A-series (goods), B-series (services), F-series (financial)  
462    /// **Compliance**: Critical for regulatory compliance
463    #[field("26T")]
464    pub field_26t: Option<Field26T>,
465
466    /// **Currency/Instructed Amount** - Field 33B (Optional)
467    ///
468    /// Original amount and currency as instructed by the ordering customer,
469    /// before any currency conversion or charge deduction. Used in FX transactions.
470    ///
471    /// **FX Requirement**: Mandatory for cross-currency transactions  
472    /// **Usage**: Shows original instruction before conversion  
473    /// **Audit Trail**: Provides complete transaction history  
474    /// **Relationship**: Must differ from Field 32A for FX transactions
475    #[field("33B")]
476    pub field_33b: Option<Field33B>,
477
478    /// **Exchange Rate** - Field 36 (Optional)
479    ///
480    /// Exchange rate applied for currency conversion between the instructed
481    /// amount (Field 33B) and settlement amount (Field 32A).
482    ///
483    /// **FX Requirement**: Mandatory when Field 33B present with different currency  
484    /// **Format**: Up to 12 digits with 5 decimal places maximum  
485    /// **Business Rule**: Rate should be reasonable for currency pair  
486    /// **Compliance**: Subject to regulatory FX reporting requirements
487    #[field("36")]
488    pub field_36: Option<Field36>,
489
490    /// **Sending Institution** - Field 51A (Optional)
491    ///
492    /// Identifies the financial institution sending the message.
493    /// Used in correspondent banking to identify the actual message originator.
494    ///
495    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
496    /// **Usage**: Optional in standard MT103, forbidden in STP  
497    /// **Format**: BIC code with optional account information  
498    /// **Correspondent Banking**: Critical for routing and settlement
499    #[field("51A")]
500    pub field_51a: Option<Field51A>,
501
502    // ================================
503    // INSTITUTIONAL ROUTING FIELDS (Payment routing chain)
504    // These fields define the correspondent banking chain and routing path
505    // ================================
506    /// **Ordering Institution** - Field 52A (Optional)
507    ///
508    /// Identifies the financial institution of the ordering customer.
509    /// This is the bank where the ordering customer holds their account
510    /// and from which the payment originates.
511    ///
512    /// **STP Requirement**: Only Option A (BIC-based) allowed in STP messages  
513    /// **Format**: Optional account indicator + optional account + BIC  
514    /// **Routing Role**: First institution in the correspondent banking chain  
515    /// **Business Rule**: Must have correspondent relationship with next institution
516    #[field("52A")]
517    pub field_52a: Option<Field52A>,
518
519    /// **Ordering Institution** - Field 52D (Optional)
520    ///
521    /// Alternative format for ordering institution using name and address
522    /// instead of BIC code. Provides more detailed institutional information
523    /// but requires manual processing.
524    ///
525    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
526    /// **Format**: Up to 4 lines of name and address (35 chars each)  
527    /// **Processing**: Requires manual intervention for routing  
528    /// **Usage**: Used when BIC is not available or for domestic routing
529    #[field("52D")]
530    pub field_52d: Option<Field52D>,
531
532    /// **Sender's Correspondent** - Field 53A (Optional)
533    ///
534    /// Identifies the correspondent bank of the sending institution.
535    /// Acts as an intermediary in the correspondent banking relationship
536    /// between the sender and receiver.
537    ///
538    /// **STP Requirement**: Only Option A (BIC-based) allowed in STP messages  
539    /// **Format**: Optional account indicator + optional account + BIC  
540    /// **Routing Role**: Facilitates cross-border payment routing  
541    /// **Relationship**: Must have correspondent agreements with sender and receiver
542    #[field("53A")]
543    pub field_53a: Option<Field53A>,
544
545    /// **Sender's Correspondent** - Field 53B (Optional)
546    ///
547    /// Alternative format using party identifier instead of BIC.
548    /// Allows identification through clearing codes or other party identifiers.
549    ///
550    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
551    /// **Format**: Optional account + party identifier (up to 35 chars)  
552    /// **Processing**: May require manual routing decisions  
553    /// **Usage**: Domestic clearing systems or non-BIC routing
554    #[field("53B")]
555    pub field_53b: Option<Field53B>,
556
557    /// **Sender's Correspondent** - Field 53D (Optional)
558    ///
559    /// Name and address format for sender's correspondent when BIC
560    /// or party identifier is not available or sufficient.
561    ///
562    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
563    /// **Format**: Up to 4 lines of name and address (35 chars each)  
564    /// **Processing**: Requires manual intervention for institution identification  
565    /// **Usage**: Legacy systems or domestic correspondent relationships
566    #[field("53D")]
567    pub field_53d: Option<Field53D>,
568
569    /// **Receiver's Correspondent** - Field 54A (Optional)
570    ///
571    /// Identifies the correspondent bank of the receiving institution.
572    /// Critical for cross-border payments where direct correspondent
573    /// relationships may not exist.
574    ///
575    /// **STP Requirement**: Only Option A (BIC-based) allowed in STP messages  
576    /// **Format**: Optional account indicator + optional account + BIC  
577    /// **Routing Role**: Final correspondent before beneficiary institution  
578    /// **Settlement**: Often handles final settlement to beneficiary bank
579    #[field("54A")]
580    pub field_54a: Option<Field54A>,
581
582    /// **Receiver's Correspondent** - Field 54B (Optional)
583    ///
584    /// Alternative format using party identifier for receiver's correspondent.
585    /// Enables routing through domestic clearing systems.
586    ///
587    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
588    /// **Format**: Optional account + party identifier (up to 35 chars)  
589    /// **Processing**: May require manual routing and settlement decisions  
590    /// **Clearing**: Often used for domestic ACH or clearing system routing
591    #[field("54B")]
592    pub field_54b: Option<Field54B>,
593
594    /// **Receiver's Correspondent** - Field 54D (Optional)
595    ///
596    /// Name and address format for receiver's correspondent institution.
597    /// Used when standard institutional identification is insufficient.
598    ///
599    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
600    /// **Format**: Up to 4 lines of name and address (35 chars each)  
601    /// **Processing**: Requires manual intervention for routing  
602    /// **Usage**: Complex correspondent relationships or legacy systems
603    #[field("54D")]
604    pub field_54d: Option<Field54D>,
605
606    /// **Third Reimbursement Institution** - Field 55A (Optional)
607    ///
608    /// Identifies an additional reimbursement bank in complex correspondent
609    /// banking arrangements. Used for multi-hop correspondent relationships.
610    ///
611    /// **STP Requirement**: Only Option A (BIC-based) allowed in STP messages  
612    /// **Format**: Optional account indicator + optional account + BIC  
613    /// **Routing Role**: Intermediate reimbursement in correspondent chain  
614    /// **Usage**: Complex cross-border routing with multiple correspondents
615    #[field("55A")]
616    pub field_55a: Option<Field55A>,
617
618    /// **Third Reimbursement Institution** - Field 55B (Optional)
619    ///
620    /// Alternative format for third reimbursement institution using
621    /// party identifier instead of BIC code.
622    ///
623    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
624    /// **Format**: Optional account + party identifier (up to 35 chars)  
625    /// **Processing**: Requires manual reimbursement processing  
626    /// **Complexity**: Adds additional correspondent relationship complexity
627    #[field("55B")]
628    pub field_55b: Option<Field55B>,
629
630    /// **Third Reimbursement Institution** - Field 55D (Optional)
631    ///
632    /// Name and address format for third reimbursement institution.
633    /// Provides detailed institutional information for complex routing.
634    ///
635    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
636    /// **Format**: Up to 4 lines of name and address (35 chars each)  
637    /// **Processing**: Requires manual intervention for reimbursement  
638    /// **Usage**: Highly complex correspondent banking arrangements
639    #[field("55D")]
640    pub field_55d: Option<Field55D>,
641
642    /// **Intermediary Institution** - Field 56A (Optional)
643    ///
644    /// Identifies an intermediary bank in the payment routing chain.
645    /// Acts as a pass-through institution between correspondents.
646    ///
647    /// **STP Requirement**: Only Option A (BIC-based) allowed in STP messages  
648    /// **Format**: Optional account indicator + optional account + BIC  
649    /// **Routing Role**: Facilitates payment routing between correspondents
650    #[field("56A")]
651    pub field_56a: Option<Field56A>,
652
653    /// **Intermediary Institution** - Field 56C (Optional)
654    ///
655    /// Account-based identification for intermediary institution.
656    /// Specifies the account at the intermediary for routing purposes.
657    ///
658    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
659    /// **Format**: Account number (up to 34 characters)  
660    /// **Processing**: Requires account-based routing decisions  
661    /// **Usage**: Specific account routing through intermediary banks
662    #[field("56C")]
663    pub field_56c: Option<Field56C>,
664
665    /// **Intermediary Institution** - Field 56D (Optional)
666    ///
667    /// Name and address format for intermediary institution when
668    /// other identification methods are insufficient.
669    ///
670    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
671    /// **Format**: Up to 4 lines of name and address (35 chars each)  
672    /// **Processing**: Requires manual intervention for routing  
673    /// **Complexity**: Adds manual processing overhead to payment chain
674    #[field("56D")]
675    pub field_56d: Option<Field56D>,
676
677    /// **Account With Institution** - Field 57A (Optional)
678    ///
679    /// Identifies the financial institution where the beneficiary
680    /// customer's account is held. This is the final destination bank.
681    ///
682    /// **STP Requirement**: Only Option A (BIC-based) allowed in STP messages  
683    /// **Format**: Optional account indicator + optional account + BIC  
684    /// **Routing Role**: Final destination bank for payment settlement  
685    /// **Critical**: Essential for successful payment delivery
686    #[field("57A")]
687    pub field_57a: Option<Field57A>,
688
689    /// **Account With Institution** - Field 57B (Optional)
690    ///
691    /// Alternative format using party identifier for the beneficiary's bank.
692    /// Enables routing through domestic clearing systems to final destination.
693    ///
694    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
695    /// **Format**: Optional account + party identifier (up to 35 chars)  
696    /// **Processing**: May require manual intervention for final delivery  
697    /// **Usage**: Domestic clearing systems or non-SWIFT final settlement
698    #[field("57B")]
699    pub field_57b: Option<Field57B>,
700
701    /// **Account With Institution** - Field 57C (Optional)
702    ///
703    /// Account-based identification for the beneficiary's bank.
704    /// Specifies the specific account for final settlement.
705    ///
706    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
707    /// **Format**: Account number (up to 34 characters)  
708    /// **Processing**: Requires account-based final settlement  
709    /// **Usage**: Specific account routing for final payment delivery
710    #[field("57C")]
711    pub field_57c: Option<Field57C>,
712
713    /// **Account With Institution** - Field 57D (Optional)
714    ///
715    /// Name and address format for the beneficiary's bank when
716    /// standard identification methods are not available or sufficient.
717    ///
718    /// **STP Restriction**: **PROHIBITED** in MT103.STP messages  
719    /// **Format**: Up to 4 lines of name and address (35 chars each)  
720    /// **Processing**: Requires manual intervention for final delivery  
721    /// **Risk**: Higher risk of payment delays or delivery failures
722    #[field("57D")]
723    pub field_57d: Option<Field57D>,
724
725    // ================================
726    // PAYMENT DETAILS AND INFORMATION FIELDS
727    // Additional payment information, charges, and processing instructions
728    // ================================
729    /// **Remittance Information** - Field 70 (Optional)
730    ///
731    /// Free-format text providing details about the purpose of the payment,
732    /// invoice references, and other remittance information for the beneficiary.
733    ///
734    /// **REMIT Restriction**: **PROHIBITED** in MT103.REMIT messages (use Field 77T)  
735    /// **Format**: Up to 4 lines of 35 characters each  
736    /// **Purpose**: Payment description, invoice numbers, contract references  
737    /// **Reconciliation**: Critical for beneficiary payment matching and reconciliation
738    #[field("70")]
739    pub field_70: Option<Field70>,
740
741    // ================================
742    // CHARGE AND FEE FIELDS
743    // Detailed charge information beyond the basic charge allocation
744    // ================================
745    /// **Sender's Charges** - Field 71F (Optional)
746    ///
747    /// Specifies the exact amount of charges to be borne by the ordering customer.
748    /// Provides transparency in charge allocation and enables precise cost calculation.
749    ///
750    /// **Format**: Currency code (3 chars) + Amount (decimal)  
751    /// **Relationship**: Must be consistent with Field 71A charge allocation  
752    /// **Transparency**: Enables clear communication of sender charge amounts  
753    /// **Accounting**: Critical for accurate charge accounting and reconciliation
754    #[field("71F")]
755    pub field_71f: Option<Field71F>,
756
757    /// **Receiver's Charges** - Field 71G (Optional)
758    ///
759    /// Specifies the exact amount of charges to be borne by the beneficiary customer.
760    /// Enables transparent communication of costs that will be deducted from payment.
761    ///
762    /// **Format**: Currency code (3 chars) + Amount (decimal)  
763    /// **Relationship**: Must be consistent with Field 71A charge allocation  
764    /// **Impact**: Amount will be deducted from final payment to beneficiary  
765    /// **Disclosure**: May be required for regulatory compliance and transparency
766    #[field("71G")]
767    pub field_71g: Option<Field71G>,
768
769    // ================================
770    // PROCESSING AND COMPLIANCE FIELDS
771    // Special processing instructions and regulatory compliance information
772    // ================================
773    /// **Sender to Receiver Information** - Field 72 (Optional)
774    ///
775    /// Critical field for processing instructions, return/reject codes, and
776    /// regulatory information. Essential for STP processing and exception handling.
777    ///
778    /// **STP Critical**: Contains structured codes for automated processing  
779    /// **Return/Reject**: Used for RETN/REJT codes and error communication  
780    /// **Format**: Up to 6 lines of 35 characters each  
781    /// **Structured Codes**: /CODE/subcod/narrative format for automation  
782    /// **Compliance**: May contain regulatory reporting codes and instructions
783    #[field("72")]
784    pub field_72: Option<Field72>,
785
786    /// **Regulatory Reporting** - Field 77B (Optional)
787    ///
788    /// Contains regulatory information required for compliance with local
789    /// and international reporting requirements, particularly for cross-border payments.
790    ///
791    /// **BoP Reporting**: Balance of payments statistical reporting  
792    /// **Format**: Up to 3 lines of 35 characters each  
793    /// **Compliance**: Required for certain jurisdictions and payment types  
794    /// **Content**: May include country codes, purpose codes, and regulatory references
795    #[field("77B")]
796    pub field_77b: Option<Field77B>,
797
798    /// **Envelope Contents** - Field 77T (Optional)
799    ///
800    /// **MT103.REMIT ONLY**: Contains structured remittance data envelope information.
801    /// Mandatory for MT103.REMIT messages, prohibited in standard MT103.
802    ///
803    /// **REMIT Requirement**: **MANDATORY** in MT103.REMIT messages  
804    /// **Standard Restriction**: **PROHIBITED** in standard MT103 messages  
805    /// **Format**: Envelope type + format + identifier  
806    /// **Structured Data**: References external structured remittance information  
807    /// **Automation**: Enables automated invoice matching and reconciliation
808    #[field("77T")]
809    pub field_77t: Option<Field77T>,
810}
811
812impl MT103 {
813    /// Create a new MT103 with required fields only
814    pub fn new(
815        field_20: Field20,
816        field_23b: Field23B,
817        field_32a: Field32A,
818        field_50: Field50,
819        field_59: Field59,
820        field_71a: Field71A,
821    ) -> Self {
822        Self {
823            field_20,
824            field_23b,
825            field_32a,
826            field_50,
827            field_59,
828            field_71a,
829            field_13c: None,
830            field_23e: None,
831            field_26t: None,
832            field_33b: None,
833            field_36: None,
834            field_51a: None,
835            field_52a: None,
836            field_52d: None,
837            field_53a: None,
838            field_53b: None,
839            field_53d: None,
840            field_54a: None,
841            field_54b: None,
842            field_54d: None,
843            field_55a: None,
844            field_55b: None,
845            field_55d: None,
846            field_56a: None,
847            field_56c: None,
848            field_56d: None,
849            field_57a: None,
850            field_57b: None,
851            field_57c: None,
852            field_57d: None,
853            field_70: None,
854            field_71f: None,
855            field_71g: None,
856            field_72: None,
857            field_77b: None,
858            field_77t: None,
859        }
860    }
861
862    /// Create a new MT103 with all fields for complete STP/RETN/REJT support
863    #[allow(clippy::too_many_arguments)]
864    pub fn new_complete(
865        field_20: Field20,
866        field_23b: Field23B,
867        field_32a: Field32A,
868        field_50: Field50,
869        field_59: Field59,
870        field_71a: Field71A,
871        field_13c: Option<Field13C>,
872        field_23e: Option<Field23E>,
873        field_26t: Option<Field26T>,
874        field_33b: Option<Field33B>,
875        field_36: Option<Field36>,
876        field_51a: Option<Field51A>,
877        field_52a: Option<Field52A>,
878        field_52d: Option<Field52D>,
879        field_53a: Option<Field53A>,
880        field_53b: Option<Field53B>,
881        field_53d: Option<Field53D>,
882        field_54a: Option<Field54A>,
883        field_54b: Option<Field54B>,
884        field_54d: Option<Field54D>,
885        field_55a: Option<Field55A>,
886        field_55b: Option<Field55B>,
887        field_55d: Option<Field55D>,
888        field_56a: Option<Field56A>,
889        field_56c: Option<Field56C>,
890        field_56d: Option<Field56D>,
891        field_57a: Option<Field57A>,
892        field_57b: Option<Field57B>,
893        field_57c: Option<Field57C>,
894        field_57d: Option<Field57D>,
895        field_70: Option<Field70>,
896        field_71f: Option<Field71F>,
897        field_71g: Option<Field71G>,
898        field_72: Option<Field72>,
899        field_77b: Option<Field77B>,
900        field_77t: Option<Field77T>,
901    ) -> Self {
902        Self {
903            field_20,
904            field_23b,
905            field_32a,
906            field_50,
907            field_59,
908            field_71a,
909            field_13c,
910            field_23e,
911            field_26t,
912            field_33b,
913            field_36,
914            field_51a,
915            field_52a,
916            field_52d,
917            field_53a,
918            field_53b,
919            field_53d,
920            field_54a,
921            field_54b,
922            field_54d,
923            field_55a,
924            field_55b,
925            field_55d,
926            field_56a,
927            field_56c,
928            field_56d,
929            field_57a,
930            field_57b,
931            field_57c,
932            field_57d,
933            field_70,
934            field_71f,
935            field_71g,
936            field_72,
937            field_77b,
938            field_77t,
939        }
940    }
941
942    /// Get the transaction reference
943    pub fn transaction_reference(&self) -> &str {
944        self.field_20.transaction_reference()
945    }
946
947    /// Get the operation code
948    pub fn operation_code(&self) -> &str {
949        self.field_23b.operation_code()
950    }
951
952    /// Get the currency code
953    pub fn currency_code(&self) -> &str {
954        self.field_32a.currency_code()
955    }
956
957    /// Get the transaction amount as decimal
958    pub fn amount_decimal(&self) -> f64 {
959        self.field_32a.amount_decimal()
960    }
961
962    /// Get the charge code
963    pub fn charge_code(&self) -> &str {
964        self.field_71a.charge_code()
965    }
966
967    /// Get the instructed amount if present
968    pub fn instructed_amount(&self) -> Option<&Field33B> {
969        self.field_33b.as_ref()
970    }
971
972    /// Get the exchange rate if present
973    pub fn exchange_rate(&self) -> Option<&Field36> {
974        self.field_36.as_ref()
975    }
976
977    /// Get sender's charges if present
978    pub fn senders_charges(&self) -> Option<&Field71F> {
979        self.field_71f.as_ref()
980    }
981
982    /// Get receiver's charges if present
983    pub fn receivers_charges(&self) -> Option<&Field71G> {
984        self.field_71g.as_ref()
985    }
986
987    /// Get sender to receiver information if present (critical for STP/RETN/REJT)
988    pub fn sender_to_receiver_info(&self) -> Option<&Field72> {
989        self.field_72.as_ref()
990    }
991
992    /// Get regulatory reporting if present
993    pub fn regulatory_reporting(&self) -> Option<&Field77B> {
994        self.field_77b.as_ref()
995    }
996
997    /// Get sending institution if present
998    pub fn sending_institution(&self) -> Option<&Field51A> {
999        self.field_51a.as_ref()
1000    }
1001
1002    /// Get envelope contents if present (MT103.REMIT only)
1003    pub fn envelope_contents(&self) -> Option<&Field77T> {
1004        self.field_77t.as_ref()
1005    }
1006
1007    /// Get ordering institution (any option) if present
1008    pub fn ordering_institution(&self) -> Option<String> {
1009        if let Some(field_52a) = &self.field_52a {
1010            Some(field_52a.bic().to_string())
1011        } else {
1012            self.field_52d
1013                .as_ref()
1014                .map(|field_52d| field_52d.name_and_address().join(" "))
1015        }
1016    }
1017
1018    /// Get sender's correspondent (any option) if present
1019    pub fn senders_correspondent(&self) -> Option<String> {
1020        if let Some(field_53a) = &self.field_53a {
1021            Some(field_53a.bic().to_string())
1022        } else {
1023            self.field_53b
1024                .as_ref()
1025                .map(|field_53b| field_53b.party_identifier().to_string())
1026                .or_else(|| {
1027                    self.field_53d
1028                        .as_ref()
1029                        .map(|field_53d| field_53d.name_and_address().join(" "))
1030                })
1031        }
1032    }
1033
1034    /// Get receiver's correspondent (any option) if present
1035    pub fn receivers_correspondent(&self) -> Option<String> {
1036        if let Some(field_54a) = &self.field_54a {
1037            Some(field_54a.bic().to_string())
1038        } else {
1039            self.field_54b
1040                .as_ref()
1041                .map(|field_54b| field_54b.party_identifier().to_string())
1042                .or_else(|| {
1043                    self.field_54d
1044                        .as_ref()
1045                        .map(|field_54d| field_54d.name_and_address().join(" "))
1046                })
1047        }
1048    }
1049
1050    /// Get third reimbursement institution (any option) if present
1051    pub fn third_reimbursement_institution(&self) -> Option<String> {
1052        if let Some(field_55a) = &self.field_55a {
1053            Some(field_55a.bic().to_string())
1054        } else {
1055            self.field_55b
1056                .as_ref()
1057                .map(|field_55b| field_55b.party_identifier().to_string())
1058                .or_else(|| {
1059                    self.field_55d
1060                        .as_ref()
1061                        .map(|field_55d| field_55d.name_and_address().join(" "))
1062                })
1063        }
1064    }
1065
1066    /// Get intermediary institution (any option) if present
1067    pub fn intermediary_institution(&self) -> Option<String> {
1068        if let Some(field_56a) = &self.field_56a {
1069            Some(field_56a.bic().to_string())
1070        } else {
1071            self.field_56c
1072                .as_ref()
1073                .map(|field_56c| field_56c.account_number().to_string())
1074                .or_else(|| {
1075                    self.field_56d
1076                        .as_ref()
1077                        .map(|field_56d| field_56d.name_and_address().join(" "))
1078                })
1079        }
1080    }
1081
1082    /// Get account with institution (any option) if present
1083    pub fn account_with_institution(&self) -> Option<String> {
1084        if let Some(field_57a) = &self.field_57a {
1085            Some(field_57a.bic().to_string())
1086        } else {
1087            self.field_57b
1088                .as_ref()
1089                .map(|field_57b| field_57b.party_identifier().to_string())
1090                .or_else(|| {
1091                    self.field_57c
1092                        .as_ref()
1093                        .map(|field_57c| field_57c.account_number().to_string())
1094                })
1095                .or_else(|| {
1096                    self.field_57d
1097                        .as_ref()
1098                        .map(|field_57d| field_57d.lines.join(" "))
1099                })
1100        }
1101    }
1102
1103    /// Get remittance information if present
1104    pub fn remittance_information(&self) -> Option<&Field70> {
1105        self.field_70.as_ref()
1106    }
1107
1108    /// Get time indication if present
1109    pub fn time_indication(&self) -> Option<&Field13C> {
1110        self.field_13c.as_ref()
1111    }
1112
1113    /// Get instruction code if present
1114    pub fn instruction_code(&self) -> Option<&Field23E> {
1115        self.field_23e.as_ref()
1116    }
1117
1118    /// Get transaction type code if present
1119    pub fn transaction_type_code(&self) -> Option<&Field26T> {
1120        self.field_26t.as_ref()
1121    }
1122
1123    /// Check if all required fields are present and valid
1124    pub fn validate_structure(&self) -> bool {
1125        // All required fields are enforced by the struct, so if we can construct it,
1126        // the structure is valid. Individual field validation is handled
1127        // by the SwiftField trait implementations.
1128        true
1129    }
1130
1131    /// Check if this is a cross-currency transaction
1132    pub fn is_cross_currency(&self) -> bool {
1133        if let Some(field_33b) = &self.field_33b {
1134            field_33b.currency() != self.field_32a.currency_code()
1135        } else {
1136            false
1137        }
1138    }
1139
1140    /// Check if exchange rate is provided for cross-currency transactions
1141    pub fn has_required_exchange_rate(&self) -> bool {
1142        if self.is_cross_currency() {
1143            self.field_36.is_some()
1144        } else {
1145            true // Not required for same-currency transactions
1146        }
1147    }
1148
1149    /// Check if this message has RETN (return) indicators in field 72
1150    pub fn has_return_codes(&self) -> bool {
1151        if let Some(field_72) = &self.field_72 {
1152            field_72
1153                .information
1154                .iter()
1155                .any(|line| line.contains("/RETN/") || line.to_uppercase().contains("RETURN"))
1156        } else {
1157            false
1158        }
1159    }
1160
1161    /// Check if this message has REJT (reject) indicators in field 72
1162    pub fn has_reject_codes(&self) -> bool {
1163        if let Some(field_72) = &self.field_72 {
1164            field_72
1165                .information
1166                .iter()
1167                .any(|line| line.contains("/REJT/") || line.to_uppercase().contains("REJECT"))
1168        } else {
1169            false
1170        }
1171    }
1172
1173    /// Check if this is an STP (Straight Through Processing) compliant message
1174    pub fn is_stp_compliant(&self) -> bool {
1175        // STP compliance requirements:
1176        // 1. All institutional fields (52, 53, 54, 55, 56, 57) must use option A if present
1177        // 2. Field 23E may only contain specific codes (CORT, INTC, SDVA, REPA)
1178        // 3. Field 72 must not contain REJT/RETN codes
1179        // 4. No ERI information in field 72
1180        // 5. Field 51A is not allowed in STP
1181
1182        // Field 51A is not allowed in STP
1183        if self.field_51a.is_some() {
1184            return false;
1185        }
1186
1187        // Check institutional fields - only option A is allowed for STP
1188        let non_a_institutional_fields = self.field_52d.is_some()
1189            || self.field_53b.is_some()
1190            || self.field_53d.is_some()
1191            || self.field_54b.is_some()
1192            || self.field_54d.is_some()
1193            || self.field_55b.is_some()
1194            || self.field_55d.is_some()
1195            || self.field_56c.is_some()
1196            || self.field_56d.is_some()
1197            || self.field_57b.is_some()
1198            || self.field_57c.is_some()
1199            || self.field_57d.is_some();
1200
1201        if non_a_institutional_fields {
1202            return false;
1203        }
1204
1205        // Check field 23E for STP-allowed codes
1206        if let Some(field_23e) = &self.field_23e {
1207            let allowed_codes = ["CORT", "INTC", "SDVA", "REPA"];
1208            if !allowed_codes.contains(&field_23e.instruction_code.as_str()) {
1209                return false;
1210            }
1211        }
1212
1213        // Check field 72 for REJT/RETN codes (not allowed in STP)
1214        if self.has_return_codes() || self.has_reject_codes() {
1215            return false;
1216        }
1217
1218        true
1219    }
1220
1221    /// Check if this is an MT103.REMIT message
1222    pub fn is_remit(&self) -> bool {
1223        // MT103.REMIT is identified by the presence of Field 77T
1224        // and absence of Field 70 (remittance information)
1225        self.field_77t.is_some() && self.field_70.is_none()
1226    }
1227
1228    /// Check if message is REMIT compliant
1229    pub fn is_remit_compliant(&self) -> bool {
1230        if !self.is_remit() {
1231            return false;
1232        }
1233
1234        // Field 77T is mandatory for MT103.REMIT
1235        if self.field_77t.is_none() {
1236            return false;
1237        }
1238
1239        // Field 70 is not allowed in MT103.REMIT
1240        if self.field_70.is_some() {
1241            return false;
1242        }
1243
1244        // Additional REMIT validations can be added here
1245        true
1246    }
1247
1248    /// Get the message variant type
1249    pub fn get_variant(&self) -> &'static str {
1250        if self.is_remit() {
1251            "MT103.REMIT"
1252        } else if self.is_stp_compliant() {
1253            "MT103.STP"
1254        } else {
1255            "MT103"
1256        }
1257    }
1258
1259    /// Get all institution fields in routing order for payment processing
1260    pub fn get_routing_chain(&self) -> Vec<(&str, String)> {
1261        let mut chain = Vec::new();
1262
1263        // Ordering Institution
1264        if let Some(bic) = self.ordering_institution() {
1265            chain.push(("Ordering Institution", bic));
1266        }
1267
1268        // Sender's Correspondent
1269        if let Some(correspondent) = self.senders_correspondent() {
1270            chain.push(("Sender's Correspondent", correspondent));
1271        }
1272
1273        // Receiver's Correspondent
1274        if let Some(correspondent) = self.receivers_correspondent() {
1275            chain.push(("Receiver's Correspondent", correspondent));
1276        }
1277
1278        // Third Reimbursement Institution
1279        if let Some(institution) = self.third_reimbursement_institution() {
1280            chain.push(("Third Reimbursement", institution));
1281        }
1282
1283        // Intermediary Institution
1284        if let Some(institution) = self.intermediary_institution() {
1285            chain.push(("Intermediary", institution));
1286        }
1287
1288        // Account With Institution
1289        if let Some(institution) = self.account_with_institution() {
1290            chain.push(("Account With Institution", institution));
1291        }
1292
1293        chain
1294    }
1295
1296    /// Extract structured information from field 72 for STP/RETN/REJT processing
1297    pub fn get_field_72_structured_info(&self) -> Option<Vec<(String, String)>> {
1298        if let Some(field_72) = &self.field_72 {
1299            let mut structured_info = Vec::new();
1300
1301            for line in &field_72.information {
1302                if line.starts_with('/') && line.len() > 3 {
1303                    // Extract code and narrative
1304                    if let Some(end_pos) = line[1..].find('/') {
1305                        let code = &line[1..end_pos + 1];
1306                        let narrative = &line[end_pos + 2..];
1307                        structured_info.push((code.to_string(), narrative.to_string()));
1308                    }
1309                }
1310            }
1311
1312            if structured_info.is_empty() {
1313                None
1314            } else {
1315                Some(structured_info)
1316            }
1317        } else {
1318            None
1319        }
1320    }
1321
1322    /// Get return/reject reason codes from field 72
1323    pub fn get_return_reject_codes(&self) -> Vec<String> {
1324        let mut codes = Vec::new();
1325
1326        if let Some(structured_info) = self.get_field_72_structured_info() {
1327            for (code, narrative) in structured_info {
1328                if code == "RETN" || code == "REJT" {
1329                    codes.push(format!("{}: {}", code, narrative));
1330                }
1331            }
1332        }
1333
1334        codes
1335    }
1336}
1337
1338#[cfg(test)]
1339mod tests {
1340    use super::*;
1341
1342    #[test]
1343    fn test_mt103_creation() {
1344        use chrono::NaiveDate;
1345
1346        let field_20 = Field20::new("FT21234567890".to_string());
1347        let field_23b = Field23B::new("CRED".to_string());
1348        let field_32a = Field32A::new(
1349            NaiveDate::from_ymd_opt(2021, 3, 15).unwrap(),
1350            "EUR".to_string(),
1351            1234567.89,
1352        );
1353        let field_50 = Field50::K(Field50K::new(vec!["JOHN DOE".to_string()]).unwrap());
1354        let field_59 =
1355            Field59::NoOption(Field59Basic::new(vec!["JANE SMITH".to_string()]).unwrap());
1356        let field_71a = Field71A::new("OUR".to_string());
1357
1358        let mt103 = MT103::new(
1359            field_20, field_23b, field_32a, field_50, field_59, field_71a,
1360        );
1361
1362        assert_eq!(mt103.transaction_reference(), "FT21234567890");
1363        assert_eq!(mt103.operation_code(), "CRED");
1364        assert_eq!(mt103.currency_code(), "EUR");
1365        assert_eq!(mt103.charge_code(), "OUR");
1366    }
1367
1368    #[test]
1369    fn test_mt103_message_type() {
1370        use crate::SwiftMessageBody;
1371        assert_eq!(MT103::message_type(), "103");
1372    }
1373
1374    #[test]
1375    fn test_mt103_json_field_names() {
1376        use chrono::NaiveDate;
1377
1378        let field_20 = Field20::new("FT21234567890".to_string());
1379        let field_23b = Field23B::new("CRED".to_string());
1380        let field_32a = Field32A::new(
1381            NaiveDate::from_ymd_opt(2021, 3, 15).unwrap(),
1382            "EUR".to_string(),
1383            1234567.89,
1384        );
1385        let field_50 = Field50::K(Field50K::new(vec!["JOHN DOE".to_string()]).unwrap());
1386        let field_59 =
1387            Field59::NoOption(Field59Basic::new(vec!["JANE SMITH".to_string()]).unwrap());
1388        let field_71a = Field71A::new("OUR".to_string());
1389
1390        let mt103 = MT103::new(
1391            field_20, field_23b, field_32a, field_50, field_59, field_71a,
1392        );
1393
1394        // Serialize to JSON
1395        let json = serde_json::to_string(&mt103).unwrap();
1396
1397        // Verify that JSON uses SWIFT field tags, not struct field names
1398        assert!(json.contains("\"20\":"));
1399        assert!(json.contains("\"23B\":"));
1400        assert!(json.contains("\"32A\":"));
1401        assert!(json.contains("\"50\":"));
1402        assert!(json.contains("\"59\":"));
1403        assert!(json.contains("\"71A\":"));
1404
1405        // Verify that JSON does NOT contain struct field names
1406        assert!(!json.contains("\"field_20\":"));
1407        assert!(!json.contains("\"field_23b\":"));
1408        assert!(!json.contains("\"field_32a\":"));
1409        assert!(!json.contains("\"field_50\":"));
1410        assert!(!json.contains("\"field_59\":"));
1411        assert!(!json.contains("\"field_71a\":"));
1412    }
1413
1414    #[test]
1415    fn test_mt103_remit_functionality() {
1416        use chrono::NaiveDate;
1417
1418        let field_20 = Field20::new("FT21234567890".to_string());
1419        let field_23b = Field23B::new("CRED".to_string());
1420        let field_32a = Field32A::new(
1421            NaiveDate::from_ymd_opt(2021, 3, 15).unwrap(),
1422            "EUR".to_string(),
1423            1234567.89,
1424        );
1425        let field_50 = Field50::K(Field50K::new(vec!["JOHN DOE".to_string()]).unwrap());
1426        let field_59 =
1427            Field59::NoOption(Field59Basic::new(vec!["JANE SMITH".to_string()]).unwrap());
1428        let field_71a = Field71A::new("OUR".to_string());
1429        let field_77t = Field77T::new("R", "D", "REMITTANCE-2024-001234567890").unwrap();
1430
1431        let mt103_remit = MT103::new_complete(
1432            field_20,
1433            field_23b,
1434            field_32a,
1435            field_50,
1436            field_59,
1437            field_71a,
1438            None,
1439            None,
1440            None,
1441            None,
1442            None,
1443            None,
1444            None,
1445            None,
1446            None,
1447            None,
1448            None,
1449            None,
1450            None,
1451            None,
1452            None,
1453            None,
1454            None,
1455            None,
1456            None,
1457            None,
1458            None,
1459            None,
1460            None,
1461            None,
1462            None,
1463            None,
1464            None,
1465            None,
1466            None,
1467            Some(field_77t),
1468        );
1469
1470        assert!(mt103_remit.is_remit());
1471        assert!(mt103_remit.is_remit_compliant());
1472        assert_eq!(mt103_remit.get_variant(), "MT103.REMIT");
1473        assert!(mt103_remit.envelope_contents().is_some());
1474    }
1475
1476    #[test]
1477    fn test_mt103_field51a_support() {
1478        use chrono::NaiveDate;
1479
1480        let field_20 = Field20::new("FT21234567890".to_string());
1481        let field_23b = Field23B::new("CRED".to_string());
1482        let field_32a = Field32A::new(
1483            NaiveDate::from_ymd_opt(2021, 3, 15).unwrap(),
1484            "EUR".to_string(),
1485            1234567.89,
1486        );
1487        let field_50 = Field50::K(Field50K::new(vec!["JOHN DOE".to_string()]).unwrap());
1488        let field_59 =
1489            Field59::NoOption(Field59Basic::new(vec!["JANE SMITH".to_string()]).unwrap());
1490        let field_71a = Field71A::new("OUR".to_string());
1491        let field_51a = Field51A::new(None, None, "CHASUS33XXX").unwrap();
1492
1493        let mt103_with_51a = MT103::new_complete(
1494            field_20,
1495            field_23b,
1496            field_32a,
1497            field_50,
1498            field_59,
1499            field_71a,
1500            None,
1501            None,
1502            None,
1503            None,
1504            None,
1505            Some(field_51a),
1506            None,
1507            None,
1508            None,
1509            None,
1510            None,
1511            None,
1512            None,
1513            None,
1514            None,
1515            None,
1516            None,
1517            None,
1518            None,
1519            None,
1520            None,
1521            None,
1522            None,
1523            None,
1524            None,
1525            None,
1526            None,
1527            None,
1528            None,
1529            None,
1530        );
1531
1532        assert!(mt103_with_51a.sending_institution().is_some());
1533        assert!(!mt103_with_51a.is_stp_compliant()); // Field 51A not allowed in STP
1534        assert_eq!(mt103_with_51a.get_variant(), "MT103");
1535    }
1536
1537    #[test]
1538    fn test_mt103_variant_detection() {
1539        use chrono::NaiveDate;
1540
1541        let field_20 = Field20::new("FT21234567890".to_string());
1542        let field_23b = Field23B::new("CRED".to_string());
1543        let field_32a = Field32A::new(
1544            NaiveDate::from_ymd_opt(2021, 3, 15).unwrap(),
1545            "EUR".to_string(),
1546            1234567.89,
1547        );
1548        let field_50 = Field50::K(Field50K::new(vec!["JOHN DOE".to_string()]).unwrap());
1549        let field_59 =
1550            Field59::NoOption(Field59Basic::new(vec!["JANE SMITH".to_string()]).unwrap());
1551        let field_71a = Field71A::new("OUR".to_string());
1552
1553        // Standard MT103
1554        let mt103_standard = MT103::new(
1555            field_20.clone(),
1556            field_23b.clone(),
1557            field_32a.clone(),
1558            field_50.clone(),
1559            field_59.clone(),
1560            field_71a.clone(),
1561        );
1562        assert_eq!(mt103_standard.get_variant(), "MT103.STP"); // Basic message is STP compliant
1563
1564        // MT103.REMIT
1565        let field_77t = Field77T::new("R", "D", "REMITTANCE-2024-001234567890").unwrap();
1566        let mt103_remit = MT103::new_complete(
1567            field_20.clone(),
1568            field_23b.clone(),
1569            field_32a.clone(),
1570            field_50.clone(),
1571            field_59.clone(),
1572            field_71a.clone(),
1573            None,
1574            None,
1575            None,
1576            None,
1577            None,
1578            None,
1579            None,
1580            None,
1581            None,
1582            None,
1583            None,
1584            None,
1585            None,
1586            None,
1587            None,
1588            None,
1589            None,
1590            None,
1591            None,
1592            None,
1593            None,
1594            None,
1595            None,
1596            None,
1597            None,
1598            None,
1599            None,
1600            None,
1601            None,
1602            Some(field_77t),
1603        );
1604        assert_eq!(mt103_remit.get_variant(), "MT103.REMIT");
1605    }
1606}