swiftlib 1.0.0__tar.gz

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.
Files changed (50) hide show
  1. swiftlib-1.0.0/.claude/settings.local.json +10 -0
  2. swiftlib-1.0.0/.gitignore +43 -0
  3. swiftlib-1.0.0/LICENSE +21 -0
  4. swiftlib-1.0.0/PKG-INFO +472 -0
  5. swiftlib-1.0.0/README.md +441 -0
  6. swiftlib-1.0.0/examples/converter_example.py +160 -0
  7. swiftlib-1.0.0/examples/iso20022_example.py +216 -0
  8. swiftlib-1.0.0/examples/mt103_example.py +121 -0
  9. swiftlib-1.0.0/examples/mt202_example.py +86 -0
  10. swiftlib-1.0.0/pyproject.toml +69 -0
  11. swiftlib-1.0.0/src/swiftlib/__init__.py +23 -0
  12. swiftlib-1.0.0/src/swiftlib/converters/__init__.py +18 -0
  13. swiftlib-1.0.0/src/swiftlib/converters/iso_to_mt.py +129 -0
  14. swiftlib-1.0.0/src/swiftlib/converters/mt_to_iso.py +337 -0
  15. swiftlib-1.0.0/src/swiftlib/core/__init__.py +28 -0
  16. swiftlib-1.0.0/src/swiftlib/core/block.py +390 -0
  17. swiftlib-1.0.0/src/swiftlib/core/field.py +143 -0
  18. swiftlib-1.0.0/src/swiftlib/core/message.py +149 -0
  19. swiftlib-1.0.0/src/swiftlib/exceptions.py +36 -0
  20. swiftlib-1.0.0/src/swiftlib/iso20022/__init__.py +21 -0
  21. swiftlib-1.0.0/src/swiftlib/iso20022/generator.py +430 -0
  22. swiftlib-1.0.0/src/swiftlib/iso20022/messages/__init__.py +80 -0
  23. swiftlib-1.0.0/src/swiftlib/iso20022/messages/camt053.py +153 -0
  24. swiftlib-1.0.0/src/swiftlib/iso20022/messages/pacs008.py +113 -0
  25. swiftlib-1.0.0/src/swiftlib/iso20022/messages/pain001.py +316 -0
  26. swiftlib-1.0.0/src/swiftlib/iso20022/namespaces.py +34 -0
  27. swiftlib-1.0.0/src/swiftlib/iso20022/parser.py +666 -0
  28. swiftlib-1.0.0/src/swiftlib/mt/__init__.py +46 -0
  29. swiftlib-1.0.0/src/swiftlib/mt/fields.py +239 -0
  30. swiftlib-1.0.0/src/swiftlib/mt/messages/__init__.py +59 -0
  31. swiftlib-1.0.0/src/swiftlib/mt/messages/base.py +151 -0
  32. swiftlib-1.0.0/src/swiftlib/mt/messages/mt103.py +298 -0
  33. swiftlib-1.0.0/src/swiftlib/mt/messages/mt202.py +210 -0
  34. swiftlib-1.0.0/src/swiftlib/mt/messages/mt515.py +152 -0
  35. swiftlib-1.0.0/src/swiftlib/mt/messages/mt700.py +291 -0
  36. swiftlib-1.0.0/src/swiftlib/mt/messages/mt9xx.py +588 -0
  37. swiftlib-1.0.0/src/swiftlib/mt/parser.py +136 -0
  38. swiftlib-1.0.0/src/swiftlib/mt/validator.py +89 -0
  39. swiftlib-1.0.0/src/swiftlib/mt/writer.py +27 -0
  40. swiftlib-1.0.0/src/swiftlib/py.typed +0 -0
  41. swiftlib-1.0.0/tests/__init__.py +1 -0
  42. swiftlib-1.0.0/tests/conftest.py +325 -0
  43. swiftlib-1.0.0/tests/iso20022/__init__.py +1 -0
  44. swiftlib-1.0.0/tests/iso20022/test_generator.py +263 -0
  45. swiftlib-1.0.0/tests/iso20022/test_parser.py +199 -0
  46. swiftlib-1.0.0/tests/mt/__init__.py +1 -0
  47. swiftlib-1.0.0/tests/mt/test_mt103.py +254 -0
  48. swiftlib-1.0.0/tests/mt/test_mt942.py +244 -0
  49. swiftlib-1.0.0/tests/mt/test_parser.py +224 -0
  50. swiftlib-1.0.0/tests/mt/test_writer.py +177 -0
@@ -0,0 +1,10 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(python -m pytest --collect-only)",
5
+ "Bash(python *)",
6
+ "Bash(python3 -m pytest tests/ -v)",
7
+ "Bash(awk '{print \"pyproject.toml description length:\", \\($1-600\\)}')"
8
+ ]
9
+ }
10
+ }
@@ -0,0 +1,43 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.egg
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ .eggs/
11
+ MANIFEST
12
+
13
+ # Virtual environments
14
+ .venv/
15
+ venv/
16
+ env/
17
+ ENV/
18
+
19
+ # Hatch / build
20
+ .hatch/
21
+ *.whl
22
+
23
+ # Testing & coverage
24
+ .pytest_cache/
25
+ .coverage
26
+ .coverage.*
27
+ coverage.xml
28
+ htmlcov/
29
+
30
+ # Type checking
31
+ .mypy_cache/
32
+ .dmypy.json
33
+ dmypy.json
34
+
35
+ # Ruff
36
+ .ruff_cache/
37
+
38
+ # IDE / OS
39
+ .DS_Store
40
+ .idea/
41
+ .vscode/
42
+ *.swp
43
+ *.swo
swiftlib-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mahima
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,472 @@
1
+ Metadata-Version: 2.4
2
+ Name: swiftlib
3
+ Version: 1.0.0
4
+ Summary: Python library for Swift MT and ISO 20022 message parsing and generation
5
+ Project-URL: Homepage, https://github.com/mahimaanu/swiftlib
6
+ Project-URL: Repository, https://github.com/mahimaanu/swiftlib
7
+ Project-URL: Bug Tracker, https://github.com/mahimaanu/swiftlib/issues
8
+ Author: Mahima
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: banking,financial,iso20022,mt,payment,swift
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Financial and Insurance Industry
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Office/Business :: Financial
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: lxml>=4.9
24
+ Requires-Dist: pydantic>=2.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: mypy; extra == 'dev'
27
+ Requires-Dist: pytest; extra == 'dev'
28
+ Requires-Dist: pytest-cov; extra == 'dev'
29
+ Requires-Dist: ruff; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # swiftlib
33
+
34
+ A production-quality Python 3 library for parsing and generating SWIFT MT messages and ISO 20022 XML messages. Designed as a modern Python alternative to the Java [prowide-core](https://github.com/prowide/prowide-core) library.
35
+
36
+ ---
37
+
38
+ ## Overview
39
+
40
+ **swiftlib** provides:
41
+
42
+ - **SWIFT MT parsing and generation** — full support for MT103, MT202, MT202COV, MT515, MT700, MT900, MT910, MT940, MT942, MT950
43
+ - **ISO 20022 XML parsing and generation** — pain.001, camt.053, pacs.008 (extensible to any message type)
44
+ - **MT ↔ ISO 20022 converters** — MT103 ↔ pain.001, MT103 ↔ pacs.008, MT940 ↔ camt.053
45
+ - **Typed Python dataclasses** — all message components are dataclasses with full type hints
46
+ - **Fluent builder pattern** — intuitive API for constructing messages step-by-step
47
+
48
+ ---
49
+
50
+ ## Why better than prowide-core?
51
+
52
+ | Feature | swiftlib (Python) | prowide-core (Java) |
53
+ |---------|-------------------|---------------------|
54
+ | Runtime | No JVM required | Requires JDK 8+ |
55
+ | Install | `pip install swiftlib` | Maven/Gradle dependency |
56
+ | Types | Python type hints + mypy | Java generics |
57
+ | Data model | Python `dataclasses` | Java POJOs/builders |
58
+ | Serialisation | Native `json.dumps`, no mapping required | Requires Jackson/Gson |
59
+ | Decimal precision | `decimal.Decimal` (exact) | `BigDecimal` (verbose) |
60
+ | Date handling | `datetime.date` objects | `java.time.LocalDate` |
61
+ | Async compatible | Yes — no shared state | JVM threading model |
62
+ | IDE support | Full autocomplete via type hints | Depends on IDE plugin |
63
+ | Integration | FastAPI, Django, Celery, asyncio | Spring, Jakarta EE |
64
+
65
+ ---
66
+
67
+ ## Installation
68
+
69
+ ```bash
70
+ pip install swiftlib
71
+ ```
72
+
73
+ **Requirements**: Python 3.10+, lxml>=4.9, pydantic>=2.0
74
+
75
+ ---
76
+
77
+ ## Quick Start
78
+
79
+ ### Parsing an MT103
80
+
81
+ ```python
82
+ from swiftlib.mt import parse
83
+
84
+ raw = """{1:F01BANKBEBBAXXX0000000000}{2:I103BANKDEFFXXXXU}{4:
85
+ :20:FT21099B59YFW001
86
+ :23B:CRED
87
+ :32A:210101USD10000,
88
+ :50K:/12345678
89
+ JOHN DOE
90
+ 123 MAIN ST
91
+ :59:/98765432
92
+ JANE SMITH
93
+ :70:PAYMENT FOR SERVICES
94
+ :71A:OUR
95
+ -}"""
96
+
97
+ msg = parse(raw) # Returns a typed MT103 instance
98
+ print(msg.message_type) # "103"
99
+ print(msg.amount) # Decimal("10000")
100
+ print(msg.currency) # "USD"
101
+ print(msg.value_date) # date(2021, 1, 1)
102
+ print(msg.charges) # "OUR"
103
+ ```
104
+
105
+ ### Building an MT103
106
+
107
+ ```python
108
+ from swiftlib.mt.messages.mt103 import MT103Builder
109
+ from decimal import Decimal
110
+ from datetime import date
111
+
112
+ mt103 = (
113
+ MT103Builder()
114
+ .transaction_reference("FT21099B59YFW001")
115
+ .bank_operation_code("CRED")
116
+ .value_date_currency_amount(date(2021, 1, 1), "USD", Decimal("10000.00"))
117
+ .ordering_customer("/12345678", "JOHN DOE", ["123 MAIN ST", "NEW YORK NY"])
118
+ .account_with_institution("A", "BANKDEFF")
119
+ .beneficiary("/98765432", "JANE SMITH", ["456 OAK AVE", "BERLIN 10115"])
120
+ .remittance_info("PAYMENT FOR SERVICES")
121
+ .charges("OUR")
122
+ .build()
123
+ )
124
+
125
+ print(mt103.to_swift())
126
+ print(mt103.to_json())
127
+ ```
128
+
129
+ ### Parsing an ISO 20022 pain.001
130
+
131
+ ```python
132
+ from swiftlib.iso20022 import parse
133
+
134
+ with open("payment.xml") as f:
135
+ msg = parse(f.read())
136
+
137
+ print(msg.group_header.message_id)
138
+ print(msg.payment_information[0].debtor.name)
139
+ print(msg.payment_information[0].credit_transfer_transactions[0].amount.amount)
140
+ ```
141
+
142
+ ### Generating a pain.001
143
+
144
+ ```python
145
+ from swiftlib.iso20022.messages.pain001 import (
146
+ Pain001Builder, Party, PaymentInformation,
147
+ CreditTransferTransaction, PaymentIdentification,
148
+ InstructedAmount, AccountIdentification,
149
+ BranchAndFinancialInstitutionIdentification,
150
+ FinancialInstitutionIdentification,
151
+ )
152
+ from datetime import date, datetime
153
+ from decimal import Decimal
154
+
155
+ fi = FinancialInstitutionIdentification(bic="BANKDEFF")
156
+ agt = BranchAndFinancialInstitutionIdentification(financial_institution=fi)
157
+
158
+ tx = CreditTransferTransaction(
159
+ payment_id=PaymentIdentification(end_to_end_id="E2E-001"),
160
+ amount=InstructedAmount(currency="EUR", amount=Decimal("5000.00")),
161
+ charge_bearer="SLEV",
162
+ creditor_agent=agt,
163
+ creditor=Party(name="BENEFICIARY"),
164
+ creditor_account=AccountIdentification(iban="DE98765432109876543210"),
165
+ )
166
+
167
+ pi = PaymentInformation(
168
+ payment_info_id="PI-001",
169
+ payment_method="TRF",
170
+ batch_booking=False,
171
+ number_of_transactions=1,
172
+ control_sum=Decimal("5000.00"),
173
+ payment_type_info=None,
174
+ requested_execution_date=date(2021, 1, 1),
175
+ debtor=Party(name="PAYER"),
176
+ debtor_account=AccountIdentification(iban="US12345678901234567890"),
177
+ debtor_agent=agt,
178
+ credit_transfer_transactions=[tx],
179
+ )
180
+
181
+ pain001 = (
182
+ Pain001Builder()
183
+ .message_id("MSG-001")
184
+ .creation_datetime(datetime.now())
185
+ .initiating_party(Party(name="MY COMPANY"))
186
+ .add_payment_information(pi)
187
+ .build()
188
+ )
189
+
190
+ xml = pain001.to_xml()
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Supported Message Types
196
+
197
+ ### SWIFT MT
198
+
199
+ | Message Type | Description | Status |
200
+ |-------------|-------------|--------|
201
+ | MT103 | Single Customer Credit Transfer | Full |
202
+ | MT202 | General Financial Institution Transfer | Full |
203
+ | MT202COV | General FI Transfer (Cover) | Full |
204
+ | MT515 | Client Confirmation of Purchase/Sale | Full |
205
+ | MT700 | Issue of a Documentary Credit | Full |
206
+ | MT900 | Confirmation of Debit | Full |
207
+ | MT910 | Confirmation of Credit | Full |
208
+ | MT940 | Customer Statement Message | Full |
209
+ | MT942 | Interim Transaction Report | Full |
210
+ | MT950 | Statement Message | Full |
211
+
212
+ ### ISO 20022
213
+
214
+ | Message Type | Description | Status |
215
+ |-------------|-------------|--------|
216
+ | pain.001.001.09 | Customer Credit Transfer Initiation | Full |
217
+ | pain.001.001.03 | Customer Credit Transfer Initiation (v3) | Supported |
218
+ | camt.053.001.08 | Bank to Customer Statement | Full |
219
+ | camt.053.001.02 | Bank to Customer Statement (v2) | Supported |
220
+ | pacs.008.001.08 | FI to FI Customer Credit Transfer | Full |
221
+ | pacs.008.001.02 | FI to FI Customer Credit Transfer (v2) | Supported |
222
+
223
+ ---
224
+
225
+ ## API Reference Highlights
226
+
227
+ ### swiftlib.mt
228
+
229
+ ```python
230
+ from swiftlib.mt import parse, MT103, MT940, MT202
231
+
232
+ # Parse any MT message (returns typed instance)
233
+ msg = parse(raw_swift_string)
234
+
235
+ # Parse specifically as MT103
236
+ mt103 = MT103.from_swift(raw)
237
+
238
+ # Typed property access
239
+ mt103.transaction_reference # str
240
+ mt103.value_date # datetime.date
241
+ mt103.currency # str
242
+ mt103.amount # Decimal
243
+ mt103.charges # "OUR" | "SHA" | "BEN"
244
+ mt103.ordering_customer # str (multiline)
245
+ mt103.beneficiary # str (multiline)
246
+
247
+ # Validation
248
+ errors = mt103.validate() # list[str]
249
+ mt103.is_valid() # bool
250
+
251
+ # Serialisation
252
+ mt103.to_swift() # SWIFT FIN string
253
+ mt103.to_dict() # dict
254
+ mt103.to_json() # JSON string
255
+ ```
256
+
257
+ ### swiftlib.iso20022
258
+
259
+ ```python
260
+ from swiftlib.iso20022 import ISO20022Parser, ISO20022Generator
261
+
262
+ parser = ISO20022Parser()
263
+ msg = parser.parse(xml_string) # auto-detect type
264
+ msg = parser.parse_file("payment.xml") # from file
265
+
266
+ generator = ISO20022Generator()
267
+ xml = generator.generate(pain001) # str
268
+ xml_bytes = generator.generate_bytes(pain001) # bytes
269
+ ```
270
+
271
+ ### swiftlib.converters
272
+
273
+ ```python
274
+ from swiftlib.converters import (
275
+ MT103ToPain001Converter,
276
+ MT103ToPacs008Converter,
277
+ MT940ToCamt053Converter,
278
+ Pain001ToMT103Converter,
279
+ Pacs008ToMT103Converter,
280
+ )
281
+
282
+ # MT103 → pain.001
283
+ pain001 = MT103ToPain001Converter().convert(mt103)
284
+
285
+ # MT103 → pacs.008
286
+ pacs008 = MT103ToPacs008Converter().convert(mt103)
287
+
288
+ # pain.001 → list[MT103]
289
+ mt103_list = Pain001ToMT103Converter().convert(pain001)
290
+
291
+ # MT940 → camt.053
292
+ camt053 = MT940ToCamt053Converter().convert(mt940)
293
+ ```
294
+
295
+ ---
296
+
297
+ ## Conversion
298
+
299
+ ```python
300
+ from swiftlib.mt import MT103
301
+ from swiftlib.converters import MT103ToPain001Converter, Pain001ToMT103Converter
302
+
303
+ # Parse MT103
304
+ mt103 = MT103.from_swift(raw_mt103)
305
+
306
+ # Convert to pain.001
307
+ pain001 = MT103ToPain001Converter().convert(mt103)
308
+ xml = pain001.to_xml()
309
+
310
+ # Convert back to MT103
311
+ mt103_list = Pain001ToMT103Converter().convert(pain001)
312
+ restored = mt103_list[0]
313
+ print(restored.to_swift())
314
+ ```
315
+
316
+ ---
317
+
318
+ ## Integration Examples
319
+
320
+ ### FastAPI
321
+
322
+ ```python
323
+ from fastapi import FastAPI, HTTPException
324
+ from swiftlib.mt import parse
325
+ from swiftlib.exceptions import ParseError
326
+
327
+ app = FastAPI()
328
+
329
+ @app.post("/parse-mt")
330
+ async def parse_mt(body: str):
331
+ try:
332
+ msg = parse(body)
333
+ return msg.to_dict()
334
+ except ParseError as e:
335
+ raise HTTPException(status_code=400, detail=str(e))
336
+
337
+ @app.post("/convert/mt103-to-xml")
338
+ async def convert_mt103_to_xml(body: str):
339
+ from swiftlib.mt.messages.mt103 import MT103
340
+ from swiftlib.converters import MT103ToPain001Converter
341
+ mt103 = MT103.from_swift(body)
342
+ pain001 = MT103ToPain001Converter().convert(mt103)
343
+ return {"xml": pain001.to_xml()}
344
+ ```
345
+
346
+ ### Django
347
+
348
+ ```python
349
+ # views.py
350
+ from django.http import JsonResponse, HttpRequest
351
+ from swiftlib.mt import parse
352
+
353
+ def parse_swift(request: HttpRequest) -> JsonResponse:
354
+ raw = request.body.decode("utf-8")
355
+ try:
356
+ msg = parse(raw)
357
+ return JsonResponse(msg.to_dict())
358
+ except Exception as e:
359
+ return JsonResponse({"error": str(e)}, status=400)
360
+ ```
361
+
362
+ ### Celery
363
+
364
+ ```python
365
+ from celery import Celery
366
+ from swiftlib.mt import parse
367
+ from swiftlib.converters import MT103ToPain001Converter
368
+
369
+ app = Celery("tasks", broker="redis://localhost:6379/0")
370
+
371
+ @app.task
372
+ def process_mt103(raw_swift: str) -> str:
373
+ """Process an MT103 and return pain.001 XML."""
374
+ from swiftlib.mt.messages.mt103 import MT103
375
+ mt103 = MT103.from_swift(raw_swift)
376
+ pain001 = MT103ToPain001Converter().convert(mt103)
377
+ return pain001.to_xml()
378
+ ```
379
+
380
+ ### asyncio
381
+
382
+ ```python
383
+ import asyncio
384
+ from swiftlib.mt import parse
385
+
386
+ # swiftlib is thread-safe and stateless — safe to use in async code
387
+ async def process_messages(messages: list[str]) -> list[dict]:
388
+ loop = asyncio.get_event_loop()
389
+ results = []
390
+ for raw in messages:
391
+ # Run CPU-bound parsing in thread pool to avoid blocking event loop
392
+ msg = await loop.run_in_executor(None, parse, raw)
393
+ results.append(msg.to_dict())
394
+ return results
395
+ ```
396
+
397
+ ---
398
+
399
+ ## Building from Source
400
+
401
+ ```bash
402
+ git clone https://github.com/your-org/swiftlib.git
403
+ cd swiftlib
404
+ pip install -e ".[dev]"
405
+ ```
406
+
407
+ ---
408
+
409
+ ## Running Tests
410
+
411
+ ```bash
412
+ # All tests
413
+ pytest
414
+
415
+ # With coverage
416
+ pytest --cov=swiftlib --cov-report=html
417
+
418
+ # Specific test file
419
+ pytest tests/mt/test_mt103.py -v
420
+
421
+ # Specific test
422
+ pytest tests/mt/test_parser.py::TestParseMT103Basic::test_parse_mt103_basic -v
423
+ ```
424
+
425
+ ---
426
+
427
+ ## Code Quality
428
+
429
+ ```bash
430
+ # Lint
431
+ ruff check src/
432
+
433
+ # Type check
434
+ mypy src/swiftlib/
435
+
436
+ # Format
437
+ ruff format src/
438
+ ```
439
+
440
+ ---
441
+
442
+ ## Contributing
443
+
444
+ 1. Fork the repository
445
+ 2. Create a feature branch (`git checkout -b feature/mt700-improvements`)
446
+ 3. Add tests for your changes
447
+ 4. Ensure all tests pass (`pytest`)
448
+ 5. Ensure type checks pass (`mypy src/swiftlib/`)
449
+ 6. Submit a pull request
450
+
451
+ ### Adding a New MT Message Type
452
+
453
+ 1. Create `src/swiftlib/mt/messages/mtXXX.py`
454
+ 2. Subclass `MTMessage`, define `_MESSAGE_TYPE`, `_MANDATORY_FIELDS`, `_FIELD_ORDER`
455
+ 3. Add typed property accessors
456
+ 4. Implement a builder class
457
+ 5. Register in `src/swiftlib/mt/messages/__init__.py` (`_MESSAGE_REGISTRY`)
458
+ 6. Add tests in `tests/mt/`
459
+
460
+ ### Adding a New ISO 20022 Message Type
461
+
462
+ 1. Create a dataclass module in `src/swiftlib/iso20022/messages/`
463
+ 2. Add namespace to `namespaces.py`
464
+ 3. Add parser method in `ISO20022Parser._dispatch()`
465
+ 4. Add generator method in `ISO20022Generator.generate()`
466
+ 5. Add tests in `tests/iso20022/`
467
+
468
+ ---
469
+
470
+ ## License
471
+
472
+ MIT License. See [LICENSE](LICENSE) for details.