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}