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