swift_mt_message/messages/
mt103.rs

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