swiftlib 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- swiftlib/__init__.py +23 -0
- swiftlib/converters/__init__.py +18 -0
- swiftlib/converters/iso_to_mt.py +129 -0
- swiftlib/converters/mt_to_iso.py +337 -0
- swiftlib/core/__init__.py +28 -0
- swiftlib/core/block.py +390 -0
- swiftlib/core/field.py +143 -0
- swiftlib/core/message.py +149 -0
- swiftlib/exceptions.py +36 -0
- swiftlib/iso20022/__init__.py +21 -0
- swiftlib/iso20022/generator.py +430 -0
- swiftlib/iso20022/messages/__init__.py +80 -0
- swiftlib/iso20022/messages/camt053.py +153 -0
- swiftlib/iso20022/messages/pacs008.py +113 -0
- swiftlib/iso20022/messages/pain001.py +316 -0
- swiftlib/iso20022/namespaces.py +34 -0
- swiftlib/iso20022/parser.py +666 -0
- swiftlib/mt/__init__.py +46 -0
- swiftlib/mt/fields.py +239 -0
- swiftlib/mt/messages/__init__.py +59 -0
- swiftlib/mt/messages/base.py +151 -0
- swiftlib/mt/messages/mt103.py +298 -0
- swiftlib/mt/messages/mt202.py +210 -0
- swiftlib/mt/messages/mt515.py +152 -0
- swiftlib/mt/messages/mt700.py +291 -0
- swiftlib/mt/messages/mt9xx.py +588 -0
- swiftlib/mt/parser.py +136 -0
- swiftlib/mt/validator.py +89 -0
- swiftlib/mt/writer.py +27 -0
- swiftlib/py.typed +0 -0
- swiftlib-1.0.0.dist-info/METADATA +472 -0
- swiftlib-1.0.0.dist-info/RECORD +34 -0
- swiftlib-1.0.0.dist-info/WHEEL +4 -0
- swiftlib-1.0.0.dist-info/licenses/LICENSE +21 -0
swiftlib/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
swiftlib — Python library for SWIFT MT and ISO 20022 message parsing and generation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from swiftlib.exceptions import (
|
|
6
|
+
FieldFormatError,
|
|
7
|
+
GenerationError,
|
|
8
|
+
ParseError,
|
|
9
|
+
SwiftLibError,
|
|
10
|
+
UnsupportedMessageType,
|
|
11
|
+
ValidationError,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__version__ = "1.0.0"
|
|
15
|
+
__all__ = [
|
|
16
|
+
"__version__",
|
|
17
|
+
"SwiftLibError",
|
|
18
|
+
"ParseError",
|
|
19
|
+
"ValidationError",
|
|
20
|
+
"GenerationError",
|
|
21
|
+
"UnsupportedMessageType",
|
|
22
|
+
"FieldFormatError",
|
|
23
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
swiftlib.converters — MT ↔ ISO 20022 message converters.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from swiftlib.converters.iso_to_mt import Pacs008ToMT103Converter, Pain001ToMT103Converter
|
|
6
|
+
from swiftlib.converters.mt_to_iso import (
|
|
7
|
+
MT103ToPacs008Converter,
|
|
8
|
+
MT103ToPain001Converter,
|
|
9
|
+
MT940ToCamt053Converter,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"MT103ToPain001Converter",
|
|
14
|
+
"MT103ToPacs008Converter",
|
|
15
|
+
"MT940ToCamt053Converter",
|
|
16
|
+
"Pain001ToMT103Converter",
|
|
17
|
+
"Pacs008ToMT103Converter",
|
|
18
|
+
]
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""
|
|
2
|
+
swiftlib.converters.iso_to_mt — Convert ISO 20022 messages to SWIFT MT.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"Pain001ToMT103Converter",
|
|
11
|
+
"Pacs008ToMT103Converter",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Pain001ToMT103Converter:
|
|
16
|
+
"""Convert a pain.001 :class:`~swiftlib.iso20022.messages.pain001.Pain001` to a list of MT103 messages."""
|
|
17
|
+
|
|
18
|
+
def convert(self, pain001: "Pain001") -> "list[MT103]": # noqa: F821
|
|
19
|
+
from swiftlib.mt.messages.mt103 import MT103Builder
|
|
20
|
+
|
|
21
|
+
result = []
|
|
22
|
+
for pi in pain001.payment_information:
|
|
23
|
+
for tx in pi.credit_transfer_transactions:
|
|
24
|
+
builder = MT103Builder()
|
|
25
|
+
|
|
26
|
+
# Transaction reference
|
|
27
|
+
ref = tx.payment_id.end_to_end_id or tx.payment_id.instruction_id or "NOTPROVIDED"
|
|
28
|
+
builder.transaction_reference(ref[:16])
|
|
29
|
+
|
|
30
|
+
# Bank operation code
|
|
31
|
+
builder.bank_operation_code("CRED")
|
|
32
|
+
|
|
33
|
+
# Value date + currency + amount
|
|
34
|
+
from swiftlib.iso20022.messages.pain001 import InstructedAmount
|
|
35
|
+
if isinstance(tx.amount, InstructedAmount):
|
|
36
|
+
ccy = tx.amount.currency
|
|
37
|
+
amount = tx.amount.amount
|
|
38
|
+
else:
|
|
39
|
+
ccy = tx.amount.currency_of_transfer
|
|
40
|
+
amount = tx.amount.amount
|
|
41
|
+
|
|
42
|
+
builder.value_date_currency_amount(pi.requested_execution_date, ccy, amount)
|
|
43
|
+
|
|
44
|
+
# Ordering customer (debtor)
|
|
45
|
+
dbtr = pi.debtor
|
|
46
|
+
dbtr_acct = pi.debtor_account
|
|
47
|
+
acct_id = dbtr_acct.iban or dbtr_acct.other_id or "NOTPROVIDED"
|
|
48
|
+
builder.ordering_customer(
|
|
49
|
+
account=acct_id,
|
|
50
|
+
name=dbtr.name[:35],
|
|
51
|
+
address=[],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Account with institution (creditor agent)
|
|
55
|
+
if tx.creditor_agent is not None:
|
|
56
|
+
bic = tx.creditor_agent.financial_institution.bic
|
|
57
|
+
if bic:
|
|
58
|
+
builder.account_with_institution("A", bic)
|
|
59
|
+
|
|
60
|
+
# Beneficiary (creditor)
|
|
61
|
+
cdtr = tx.creditor
|
|
62
|
+
cdtr_acct = tx.creditor_account
|
|
63
|
+
cdtr_acct_id = cdtr_acct.iban or cdtr_acct.other_id or "NOTPROVIDED"
|
|
64
|
+
builder.beneficiary(
|
|
65
|
+
account=cdtr_acct_id,
|
|
66
|
+
name=cdtr.name[:35],
|
|
67
|
+
address=[],
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Remittance information
|
|
71
|
+
if tx.remittance_info is not None:
|
|
72
|
+
for ustrd in tx.remittance_info.unstructured:
|
|
73
|
+
builder.remittance_info(ustrd[:140])
|
|
74
|
+
break # only one :70: allowed
|
|
75
|
+
|
|
76
|
+
# Charges
|
|
77
|
+
charge_map = {"DEBT": "OUR", "CRED": "BEN", "SHAR": "SHA", "SLEV": "SHA"}
|
|
78
|
+
builder.charges(charge_map.get(tx.charge_bearer, "SHA"))
|
|
79
|
+
|
|
80
|
+
result.append(builder.build())
|
|
81
|
+
|
|
82
|
+
return result
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class Pacs008ToMT103Converter:
|
|
86
|
+
"""Convert a pacs.008 :class:`~swiftlib.iso20022.messages.pacs008.FIToFICreditTransfer` to a list of MT103 messages."""
|
|
87
|
+
|
|
88
|
+
def convert(self, pacs008: "FIToFICreditTransfer") -> "list[MT103]": # noqa: F821
|
|
89
|
+
from swiftlib.mt.messages.mt103 import MT103Builder
|
|
90
|
+
|
|
91
|
+
result = []
|
|
92
|
+
for tx in pacs008.credit_transfer_transaction_information:
|
|
93
|
+
builder = MT103Builder()
|
|
94
|
+
|
|
95
|
+
ref = tx.payment_id.end_to_end_id or tx.payment_id.transaction_id or "NOTPROVIDED"
|
|
96
|
+
builder.transaction_reference(ref[:16])
|
|
97
|
+
builder.bank_operation_code("CRED")
|
|
98
|
+
|
|
99
|
+
ccy = tx.interbank_settlement_amount.currency
|
|
100
|
+
amount = tx.interbank_settlement_amount.amount
|
|
101
|
+
builder.value_date_currency_amount(tx.interbank_settlement_date, ccy, amount)
|
|
102
|
+
|
|
103
|
+
# Debtor
|
|
104
|
+
dbtr_acct_id = tx.debtor_account.iban or tx.debtor_account.other_id or "NOTPROVIDED"
|
|
105
|
+
builder.ordering_customer(account=dbtr_acct_id, name=tx.debtor.name[:35])
|
|
106
|
+
|
|
107
|
+
# Debtor agent
|
|
108
|
+
if tx.debtor_agent.financial_institution.bic:
|
|
109
|
+
builder.ordering_institution("A", tx.debtor_agent.financial_institution.bic)
|
|
110
|
+
|
|
111
|
+
# Creditor agent
|
|
112
|
+
if tx.creditor_agent.financial_institution.bic:
|
|
113
|
+
builder.account_with_institution("A", tx.creditor_agent.financial_institution.bic)
|
|
114
|
+
|
|
115
|
+
# Creditor
|
|
116
|
+
cdtr_acct_id = tx.creditor_account.iban or tx.creditor_account.other_id or "NOTPROVIDED"
|
|
117
|
+
builder.beneficiary(account=cdtr_acct_id, name=tx.creditor.name[:35])
|
|
118
|
+
|
|
119
|
+
if tx.remittance_info:
|
|
120
|
+
for ustrd in tx.remittance_info.unstructured:
|
|
121
|
+
builder.remittance_info(ustrd[:140])
|
|
122
|
+
break
|
|
123
|
+
|
|
124
|
+
charge_map = {"DEBT": "OUR", "CRED": "BEN", "SHAR": "SHA", "SLEV": "SHA"}
|
|
125
|
+
builder.charges(charge_map.get(tx.charge_bearer, "SHA"))
|
|
126
|
+
|
|
127
|
+
result.append(builder.build())
|
|
128
|
+
|
|
129
|
+
return result
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""
|
|
2
|
+
swiftlib.converters.mt_to_iso — Convert SWIFT MT messages to ISO 20022.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from datetime import date, datetime
|
|
9
|
+
from decimal import Decimal
|
|
10
|
+
|
|
11
|
+
from swiftlib.exceptions import GenerationError
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"MT103ToPain001Converter",
|
|
15
|
+
"MT103ToPacs008Converter",
|
|
16
|
+
"MT940ToCamt053Converter",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _parse_party_field(swift_party: str) -> tuple[str | None, str, list[str]]:
|
|
21
|
+
"""Parse a SWIFT party field (e.g., :50K: or :59:) into (account, name, address)."""
|
|
22
|
+
lines = swift_party.strip().split("\n")
|
|
23
|
+
account: str | None = None
|
|
24
|
+
name: str = ""
|
|
25
|
+
address: list[str] = []
|
|
26
|
+
|
|
27
|
+
if not lines:
|
|
28
|
+
return account, name, address
|
|
29
|
+
|
|
30
|
+
first = lines[0]
|
|
31
|
+
if first.startswith("/"):
|
|
32
|
+
account = first[1:].strip()
|
|
33
|
+
if len(lines) > 1:
|
|
34
|
+
name = lines[1]
|
|
35
|
+
address = lines[2:]
|
|
36
|
+
else:
|
|
37
|
+
name = first
|
|
38
|
+
address = lines[1:]
|
|
39
|
+
|
|
40
|
+
return account, name, address
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class MT103ToPain001Converter:
|
|
44
|
+
"""Convert an :class:`~swiftlib.mt.messages.mt103.MT103` to a :class:`~swiftlib.iso20022.messages.pain001.Pain001`."""
|
|
45
|
+
|
|
46
|
+
def convert(self, mt103: "MT103") -> "Pain001": # noqa: F821
|
|
47
|
+
from swiftlib.iso20022.messages.pain001 import (
|
|
48
|
+
AccountIdentification,
|
|
49
|
+
BranchAndFinancialInstitutionIdentification,
|
|
50
|
+
CreditTransferTransaction,
|
|
51
|
+
FinancialInstitutionIdentification,
|
|
52
|
+
GroupHeader,
|
|
53
|
+
InstructedAmount,
|
|
54
|
+
Pain001,
|
|
55
|
+
Party,
|
|
56
|
+
PaymentIdentification,
|
|
57
|
+
PaymentInformation,
|
|
58
|
+
RemittanceInformation,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
msg_id = mt103.transaction_reference or f"MT103-{id(mt103)}"
|
|
62
|
+
now = datetime.now()
|
|
63
|
+
|
|
64
|
+
init_pty = Party(name=mt103.sender[:35] if mt103.sender else "UNKNOWN")
|
|
65
|
+
gh = GroupHeader(
|
|
66
|
+
message_id=msg_id,
|
|
67
|
+
creation_datetime=now,
|
|
68
|
+
number_of_transactions=1,
|
|
69
|
+
control_sum=mt103.amount or Decimal("0"),
|
|
70
|
+
initiating_party=init_pty,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Ordering customer (debtor)
|
|
74
|
+
ord_cust_raw = mt103.ordering_customer
|
|
75
|
+
oc_account, oc_name, oc_address = _parse_party_field(ord_cust_raw)
|
|
76
|
+
debtor = self._map_party(ord_cust_raw)
|
|
77
|
+
debtor_account = AccountIdentification(iban=oc_account)
|
|
78
|
+
|
|
79
|
+
# Ordering institution (debtor agent)
|
|
80
|
+
dbtr_agt_bic: str | None = None
|
|
81
|
+
for tag in ("52A", "52D"):
|
|
82
|
+
v = mt103.get_field_value(tag)
|
|
83
|
+
if v:
|
|
84
|
+
# BIC is typically the last line or the whole value
|
|
85
|
+
lines = v.strip().split("\n")
|
|
86
|
+
dbtr_agt_bic = lines[-1].strip()
|
|
87
|
+
break
|
|
88
|
+
debtor_agent = BranchAndFinancialInstitutionIdentification(
|
|
89
|
+
financial_institution=FinancialInstitutionIdentification(bic=dbtr_agt_bic)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Account with institution (creditor agent)
|
|
93
|
+
cdtr_agt_bic: str | None = None
|
|
94
|
+
for tag in ("57A", "57D"):
|
|
95
|
+
v = mt103.get_field_value(tag)
|
|
96
|
+
if v:
|
|
97
|
+
lines = v.strip().split("\n")
|
|
98
|
+
cdtr_agt_bic = lines[-1].strip()
|
|
99
|
+
break
|
|
100
|
+
creditor_agent = BranchAndFinancialInstitutionIdentification(
|
|
101
|
+
financial_institution=FinancialInstitutionIdentification(bic=cdtr_agt_bic)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Beneficiary (creditor)
|
|
105
|
+
bene_raw = mt103.beneficiary
|
|
106
|
+
bene_account, bene_name, bene_address = _parse_party_field(bene_raw)
|
|
107
|
+
creditor = self._map_party(bene_raw)
|
|
108
|
+
creditor_account = AccountIdentification(iban=bene_account)
|
|
109
|
+
|
|
110
|
+
# Amount
|
|
111
|
+
amount = self._map_amount(mt103)
|
|
112
|
+
|
|
113
|
+
# Remittance
|
|
114
|
+
rmt_info: RemittanceInformation | None = None
|
|
115
|
+
rmt_raw = mt103.remittance_info
|
|
116
|
+
if rmt_raw:
|
|
117
|
+
rmt_info = RemittanceInformation(unstructured=[rmt_raw])
|
|
118
|
+
|
|
119
|
+
pmt_id = PaymentIdentification(
|
|
120
|
+
instruction_id=msg_id,
|
|
121
|
+
end_to_end_id=msg_id,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
charge_bearer_map = {"OUR": "DEBT", "BEN": "CRED", "SHA": "SHAR"}
|
|
125
|
+
charge_bearer = charge_bearer_map.get(mt103.charges, "SLEV")
|
|
126
|
+
|
|
127
|
+
tx = CreditTransferTransaction(
|
|
128
|
+
payment_id=pmt_id,
|
|
129
|
+
amount=amount,
|
|
130
|
+
charge_bearer=charge_bearer,
|
|
131
|
+
creditor_agent=creditor_agent,
|
|
132
|
+
creditor=creditor,
|
|
133
|
+
creditor_account=creditor_account,
|
|
134
|
+
remittance_info=rmt_info,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
exec_date = mt103.value_date or date.today()
|
|
138
|
+
|
|
139
|
+
pi = PaymentInformation(
|
|
140
|
+
payment_info_id=f"{msg_id}-001",
|
|
141
|
+
payment_method="TRF",
|
|
142
|
+
batch_booking=False,
|
|
143
|
+
number_of_transactions=1,
|
|
144
|
+
control_sum=mt103.amount or Decimal("0"),
|
|
145
|
+
payment_type_info=None,
|
|
146
|
+
requested_execution_date=exec_date,
|
|
147
|
+
debtor=debtor,
|
|
148
|
+
debtor_account=debtor_account,
|
|
149
|
+
debtor_agent=debtor_agent,
|
|
150
|
+
credit_transfer_transactions=[tx],
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return Pain001(group_header=gh, payment_information=[pi])
|
|
154
|
+
|
|
155
|
+
def _map_party(self, swift_party: str) -> "Party": # noqa: F821
|
|
156
|
+
from swiftlib.iso20022.messages.pain001 import Party, PostalAddress
|
|
157
|
+
account, name, address = _parse_party_field(swift_party)
|
|
158
|
+
postal: PostalAddress | None = None
|
|
159
|
+
if address:
|
|
160
|
+
postal = PostalAddress(address_lines=address)
|
|
161
|
+
return Party(name=name or "", postal_address=postal)
|
|
162
|
+
|
|
163
|
+
def _map_amount(self, mt103: "MT103") -> "InstructedAmount": # noqa: F821
|
|
164
|
+
from swiftlib.iso20022.messages.pain001 import InstructedAmount
|
|
165
|
+
return InstructedAmount(
|
|
166
|
+
currency=mt103.currency or "",
|
|
167
|
+
amount=mt103.amount or Decimal("0"),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class MT103ToPacs008Converter:
|
|
172
|
+
"""Convert an MT103 to a pacs.008 :class:`~swiftlib.iso20022.messages.pacs008.FIToFICreditTransfer`."""
|
|
173
|
+
|
|
174
|
+
def convert(self, mt103: "MT103") -> "FIToFICreditTransfer": # noqa: F821
|
|
175
|
+
from swiftlib.iso20022.messages.pacs008 import (
|
|
176
|
+
CreditTransferTransactionInfo,
|
|
177
|
+
FIToFICreditTransfer,
|
|
178
|
+
GroupHeader,
|
|
179
|
+
InterBankSettlementAmount,
|
|
180
|
+
SettlementInstruction,
|
|
181
|
+
)
|
|
182
|
+
from swiftlib.iso20022.messages.pain001 import (
|
|
183
|
+
AccountIdentification,
|
|
184
|
+
BranchAndFinancialInstitutionIdentification,
|
|
185
|
+
FinancialInstitutionIdentification,
|
|
186
|
+
Party,
|
|
187
|
+
PaymentIdentification,
|
|
188
|
+
RemittanceInformation,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
now = datetime.now()
|
|
192
|
+
msg_id = mt103.transaction_reference or f"PACS008-{id(mt103)}"
|
|
193
|
+
|
|
194
|
+
gh = GroupHeader(
|
|
195
|
+
message_id=msg_id,
|
|
196
|
+
creation_datetime=now,
|
|
197
|
+
number_of_transactions=1,
|
|
198
|
+
settlement_information=SettlementInstruction(settlement_method="INDA"),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
iba = InterBankSettlementAmount(
|
|
202
|
+
currency=mt103.currency or "",
|
|
203
|
+
amount=mt103.amount or Decimal("0"),
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Parties
|
|
207
|
+
ord_raw = mt103.ordering_customer
|
|
208
|
+
oc_account, oc_name, _ = _parse_party_field(ord_raw)
|
|
209
|
+
debtor = Party(name=oc_name or "")
|
|
210
|
+
debtor_account = AccountIdentification(iban=oc_account)
|
|
211
|
+
|
|
212
|
+
bene_raw = mt103.beneficiary
|
|
213
|
+
bene_account, bene_name, _ = _parse_party_field(bene_raw)
|
|
214
|
+
creditor = Party(name=bene_name or "")
|
|
215
|
+
creditor_account = AccountIdentification(iban=bene_account)
|
|
216
|
+
|
|
217
|
+
def _get_bic(tags: list[str]) -> str | None:
|
|
218
|
+
for tag in tags:
|
|
219
|
+
v = mt103.get_field_value(tag)
|
|
220
|
+
if v:
|
|
221
|
+
return v.strip().split("\n")[-1].strip()
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
dbtr_agt = BranchAndFinancialInstitutionIdentification(
|
|
225
|
+
financial_institution=FinancialInstitutionIdentification(
|
|
226
|
+
bic=_get_bic(["52A", "52D"])
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
cdtr_agt = BranchAndFinancialInstitutionIdentification(
|
|
230
|
+
financial_institution=FinancialInstitutionIdentification(
|
|
231
|
+
bic=_get_bic(["57A", "57D"])
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
rmt_info: RemittanceInformation | None = None
|
|
236
|
+
if mt103.remittance_info:
|
|
237
|
+
rmt_info = RemittanceInformation(unstructured=[mt103.remittance_info])
|
|
238
|
+
|
|
239
|
+
charge_bearer_map = {"OUR": "DEBT", "BEN": "CRED", "SHA": "SHAR"}
|
|
240
|
+
|
|
241
|
+
tx = CreditTransferTransactionInfo(
|
|
242
|
+
payment_id=PaymentIdentification(
|
|
243
|
+
instruction_id=msg_id,
|
|
244
|
+
end_to_end_id=msg_id,
|
|
245
|
+
transaction_id=msg_id,
|
|
246
|
+
),
|
|
247
|
+
interbank_settlement_amount=iba,
|
|
248
|
+
interbank_settlement_date=mt103.value_date or date.today(),
|
|
249
|
+
charge_bearer=charge_bearer_map.get(mt103.charges, "SLEV"),
|
|
250
|
+
instructing_agent=None,
|
|
251
|
+
instructed_agent=None,
|
|
252
|
+
debtor=debtor,
|
|
253
|
+
debtor_account=debtor_account,
|
|
254
|
+
debtor_agent=dbtr_agt,
|
|
255
|
+
creditor_agent=cdtr_agt,
|
|
256
|
+
creditor=creditor,
|
|
257
|
+
creditor_account=creditor_account,
|
|
258
|
+
remittance_info=rmt_info,
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
return FIToFICreditTransfer(
|
|
262
|
+
group_header=gh, credit_transfer_transaction_information=[tx]
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class MT940ToCamt053Converter:
|
|
267
|
+
"""Convert an MT940 to a camt.053 :class:`~swiftlib.iso20022.messages.camt053.Camt053`."""
|
|
268
|
+
|
|
269
|
+
def convert(self, mt940: "MT940") -> "Camt053": # noqa: F821
|
|
270
|
+
from swiftlib.iso20022.messages.camt053 import (
|
|
271
|
+
Balance,
|
|
272
|
+
CashAccount,
|
|
273
|
+
Camt053,
|
|
274
|
+
GroupHeader053,
|
|
275
|
+
ReportEntry,
|
|
276
|
+
Statement,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
now = datetime.now()
|
|
280
|
+
msg_id = mt940.transaction_reference or f"CAMT053-{id(mt940)}"
|
|
281
|
+
|
|
282
|
+
gh = GroupHeader053(message_id=msg_id, creation_datetime=now)
|
|
283
|
+
|
|
284
|
+
acct_raw = mt940.account
|
|
285
|
+
acct = CashAccount(
|
|
286
|
+
iban=acct_raw if acct_raw.startswith("LT") or re.match(r"[A-Z]{2}\d{2}", acct_raw) else None,
|
|
287
|
+
other_id=acct_raw if not (acct_raw.startswith("LT") or re.match(r"[A-Z]{2}\d{2}", acct_raw)) else None,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
balances: list[Balance] = []
|
|
291
|
+
opening = mt940.opening_balance
|
|
292
|
+
if opening is not None:
|
|
293
|
+
dc = "DBIT" if opening.debit_credit == "D" else "CRDT"
|
|
294
|
+
balances.append(Balance(
|
|
295
|
+
type_code="OPBD",
|
|
296
|
+
amount=opening.amount,
|
|
297
|
+
currency=opening.currency,
|
|
298
|
+
credit_debit_indicator=dc,
|
|
299
|
+
date=opening.date,
|
|
300
|
+
))
|
|
301
|
+
|
|
302
|
+
closing = mt940.closing_balance
|
|
303
|
+
if closing is not None:
|
|
304
|
+
dc = "DBIT" if closing.debit_credit == "D" else "CRDT"
|
|
305
|
+
balances.append(Balance(
|
|
306
|
+
type_code="CLBD",
|
|
307
|
+
amount=closing.amount,
|
|
308
|
+
currency=closing.currency,
|
|
309
|
+
credit_debit_indicator=dc,
|
|
310
|
+
date=closing.date,
|
|
311
|
+
))
|
|
312
|
+
|
|
313
|
+
entries = [self._map_statement_line(line) for line in mt940.statement_lines]
|
|
314
|
+
|
|
315
|
+
stmt = Statement(
|
|
316
|
+
identification=msg_id,
|
|
317
|
+
creation_datetime=now,
|
|
318
|
+
account=acct,
|
|
319
|
+
balance=balances,
|
|
320
|
+
transactions=entries,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
return Camt053(group_header=gh, statements=[stmt])
|
|
324
|
+
|
|
325
|
+
def _map_statement_line(self, line: "StatementLine") -> "ReportEntry": # noqa: F821
|
|
326
|
+
from swiftlib.iso20022.messages.camt053 import ReportEntry
|
|
327
|
+
dc_indicator = "DBIT" if "D" in line.debit_credit_mark else "CRDT"
|
|
328
|
+
return ReportEntry(
|
|
329
|
+
amount=line.amount,
|
|
330
|
+
currency="", # MT940 doesn't carry currency in field 61 directly
|
|
331
|
+
credit_debit_indicator=dc_indicator,
|
|
332
|
+
status="BOOK",
|
|
333
|
+
booking_date=line.value_date,
|
|
334
|
+
value_date=line.value_date,
|
|
335
|
+
account_servicer_ref=line.account_servicing_ref,
|
|
336
|
+
entry_reference=line.reference,
|
|
337
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
swiftlib.core — core building blocks: fields, blocks, messages.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from swiftlib.core.field import Field, FieldDefinition, FieldType, parse_format
|
|
6
|
+
from swiftlib.core.block import (
|
|
7
|
+
Block1,
|
|
8
|
+
Block2Input,
|
|
9
|
+
Block2Output,
|
|
10
|
+
Block3,
|
|
11
|
+
Block4,
|
|
12
|
+
Block5,
|
|
13
|
+
)
|
|
14
|
+
from swiftlib.core.message import SwiftMessage
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"Field",
|
|
18
|
+
"FieldDefinition",
|
|
19
|
+
"FieldType",
|
|
20
|
+
"parse_format",
|
|
21
|
+
"Block1",
|
|
22
|
+
"Block2Input",
|
|
23
|
+
"Block2Output",
|
|
24
|
+
"Block3",
|
|
25
|
+
"Block4",
|
|
26
|
+
"Block5",
|
|
27
|
+
"SwiftMessage",
|
|
28
|
+
]
|