radiankore 0.1.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.
- radiankore-0.1.0/.gitignore +37 -0
- radiankore-0.1.0/LICENSE +21 -0
- radiankore-0.1.0/PKG-INFO +122 -0
- radiankore-0.1.0/README.md +94 -0
- radiankore-0.1.0/THIRD_PARTY_NOTICES +7 -0
- radiankore-0.1.0/pyproject.toml +74 -0
- radiankore-0.1.0/src/radiankore/__init__.py +24 -0
- radiankore-0.1.0/src/radiankore/_soap/__init__.py +1 -0
- radiankore-0.1.0/src/radiankore/_soap/acquirer_response.py +18 -0
- radiankore-0.1.0/src/radiankore/_soap/dian_response.py +40 -0
- radiankore-0.1.0/src/radiankore/_soap/document_info_response.py +326 -0
- radiankore-0.1.0/src/radiankore/_soap/document_status_response.py +24 -0
- radiankore-0.1.0/src/radiankore/_soap/envelope.py +76 -0
- radiankore-0.1.0/src/radiankore/_soap/namespaces.py +25 -0
- radiankore-0.1.0/src/radiankore/_soap/parsing.py +82 -0
- radiankore-0.1.0/src/radiankore/_soap/reference_notes_response.py +122 -0
- radiankore-0.1.0/src/radiankore/_soap/response.py +98 -0
- radiankore-0.1.0/src/radiankore/_soap/signing.py +84 -0
- radiankore-0.1.0/src/radiankore/_soap/transport.py +54 -0
- radiankore-0.1.0/src/radiankore/_soap/ubl.py +72 -0
- radiankore-0.1.0/src/radiankore/_soap/xml_document_response.py +49 -0
- radiankore-0.1.0/src/radiankore/catalogs/__init__.py +6 -0
- radiankore-0.1.0/src/radiankore/catalogs/events.py +99 -0
- radiankore-0.1.0/src/radiankore/catalogs/identification.py +60 -0
- radiankore-0.1.0/src/radiankore/certificate.py +90 -0
- radiankore-0.1.0/src/radiankore/client.py +185 -0
- radiankore-0.1.0/src/radiankore/environment.py +21 -0
- radiankore-0.1.0/src/radiankore/exceptions.py +30 -0
- radiankore-0.1.0/src/radiankore/models/__init__.py +1 -0
- radiankore-0.1.0/src/radiankore/models/responses/__init__.py +57 -0
- radiankore-0.1.0/src/radiankore/models/responses/acquirer.py +17 -0
- radiankore-0.1.0/src/radiankore/models/responses/document_info.py +166 -0
- radiankore-0.1.0/src/radiankore/models/responses/document_status.py +22 -0
- radiankore-0.1.0/src/radiankore/models/responses/invoice_events.py +84 -0
- radiankore-0.1.0/src/radiankore/models/responses/reference_notes.py +68 -0
- radiankore-0.1.0/src/radiankore/models/responses/xml_document.py +18 -0
- radiankore-0.1.0/src/radiankore/py.typed +0 -0
- radiankore-0.1.0/tests/conftest.py +202 -0
- radiankore-0.1.0/tests/fixtures/document_info.xml +90 -0
- radiankore-0.1.0/tests/fixtures/document_status.xml +22 -0
- radiankore-0.1.0/tests/fixtures/invoice_events.xml +286 -0
- radiankore-0.1.0/tests/fixtures/reference_notes.xml +23 -0
- radiankore-0.1.0/tests/fixtures/reference_notes_application.xml +38 -0
- radiankore-0.1.0/tests/fixtures/xml_document.xml +10 -0
- radiankore-0.1.0/tests/test_acquirer.py +246 -0
- radiankore-0.1.0/tests/test_catalogs.py +138 -0
- radiankore-0.1.0/tests/test_certificate.py +147 -0
- radiankore-0.1.0/tests/test_client.py +72 -0
- radiankore-0.1.0/tests/test_document_info.py +654 -0
- radiankore-0.1.0/tests/test_document_status.py +330 -0
- radiankore-0.1.0/tests/test_reference_notes.py +331 -0
- radiankore-0.1.0/tests/test_reference_notes_payload.py +337 -0
- radiankore-0.1.0/tests/test_response.py +556 -0
- radiankore-0.1.0/tests/test_signing.py +183 -0
- radiankore-0.1.0/tests/test_soap_client.py +495 -0
- radiankore-0.1.0/tests/test_xml_document.py +260 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Python and local environments
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.venv/
|
|
5
|
+
.uv-cache/
|
|
6
|
+
*.egg-info/
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
|
|
10
|
+
# Quality reports and caches
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
.mypy_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
.coverage
|
|
15
|
+
.coverage.*
|
|
16
|
+
coverage.xml
|
|
17
|
+
htmlcov/
|
|
18
|
+
|
|
19
|
+
# Local configuration and signing material
|
|
20
|
+
.env
|
|
21
|
+
.env.*
|
|
22
|
+
!.env.example
|
|
23
|
+
*.p12
|
|
24
|
+
*.pfx
|
|
25
|
+
*.pem
|
|
26
|
+
*.key
|
|
27
|
+
|
|
28
|
+
# Editor and environment metadata
|
|
29
|
+
.idea/
|
|
30
|
+
.vscode/
|
|
31
|
+
.DS_Store
|
|
32
|
+
.agents/
|
|
33
|
+
.codex/
|
|
34
|
+
.aws/
|
|
35
|
+
|
|
36
|
+
# Generated documentation
|
|
37
|
+
docs/_build/
|
radiankore-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Deikore SAS
|
|
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,122 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: radiankore
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python library for RADIAN events and invoice queries through DIAN.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Deikore/radiankore
|
|
6
|
+
Project-URL: Documentation, https://radiankore.readthedocs.io/
|
|
7
|
+
Project-URL: Repository, https://github.com/Deikore/radiankore
|
|
8
|
+
Project-URL: Issues, https://github.com/Deikore/radiankore/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/Deikore/radiankore/blob/HEAD/CHANGELOG.md
|
|
10
|
+
Author: Deikore SAS
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
License-File: THIRD_PARTY_NOTICES
|
|
14
|
+
Keywords: colombia,dian,electronic-invoicing,radian,soap
|
|
15
|
+
Classifier: Development Status :: 3 - Alpha
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.13
|
|
23
|
+
Requires-Dist: cryptography>=46
|
|
24
|
+
Requires-Dist: httpx>=0.28
|
|
25
|
+
Requires-Dist: lxml>=5
|
|
26
|
+
Requires-Dist: xmlsec>=1.3.17
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# radiankore
|
|
30
|
+
|
|
31
|
+
Librería Python de Deikore SAS para consultar servicios de DIAN y eventos RADIAN
|
|
32
|
+
con certificado digital PFX/P12. Python >=3.13, API síncrona y licencia MIT.
|
|
33
|
+
|
|
34
|
+
| | Estado |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| Documentación | [](https://radiankore.readthedocs.io/) |
|
|
37
|
+
| Calidad y pruebas | [](https://github.com/Deikore/radiankore/actions/workflows/ci.yml) |
|
|
38
|
+
| Paquete | [](https://pypi.org/project/radiankore/) [](https://pypi.org/project/radiankore/) [](https://github.com/Deikore/radiankore/blob/HEAD/LICENSE) |
|
|
39
|
+
|
|
40
|
+
## Instalación
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install radiankore
|
|
44
|
+
# Con uv:
|
|
45
|
+
uv add radiankore
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Disponible tras la primera publicación en PyPI. Si necesitas compilar las
|
|
49
|
+
dependencias de firma, consulta la
|
|
50
|
+
[guía de instalación](https://radiankore.readthedocs.io/).
|
|
51
|
+
|
|
52
|
+
## Métodos disponibles
|
|
53
|
+
|
|
54
|
+
| Método | Operación DIAN | Resultado |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| `get_status(track_id)` | `GetStatus` | Estado de validación y payloads decodificados |
|
|
57
|
+
| `get_invoice_events(cufe)` | `GetStatusEvent` | Eventos asociados a una factura |
|
|
58
|
+
| `get_acquirer(identification_type=..., identification_number=...)` | `GetAcquirer` | Nombre y correo del adquiriente |
|
|
59
|
+
| `get_document_info(uuid)` | `GetDocumentInfo` | Información, estados, eventos y notas del documento |
|
|
60
|
+
| `get_reference_notes(track_id)` | `GetReferenceNotes` | Notas referenciadas y motivos por línea |
|
|
61
|
+
| `get_xml_by_document_key(document_key)` | `GetXmlByDocumentKey` | XML original del documento como `bytes` |
|
|
62
|
+
|
|
63
|
+
No genera facturas ni envía eventos. Los resultados utilizan dataclasses
|
|
64
|
+
inmutables y campos opcionales cuando corresponda.
|
|
65
|
+
|
|
66
|
+
## Consultar eventos
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from radiankore import DianClient, Environment
|
|
70
|
+
|
|
71
|
+
client = DianClient(
|
|
72
|
+
certificate_path="/ruta/segura/certificado.pfx",
|
|
73
|
+
password="password-de-ejemplo",
|
|
74
|
+
environment=Environment.PRODUCTION,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
result = client.get_invoice_events("CUFE-DE-LA-FACTURA")
|
|
78
|
+
for event in result.events:
|
|
79
|
+
print(event.id, event.code, event.description, event.date, event.time)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Producción es el ambiente predeterminado; usa `Environment.HABILITATION` para
|
|
83
|
+
habilitación. Cada consulta cierra sus conexiones automáticamente y puedes
|
|
84
|
+
reutilizar el cliente sin volver a cargar el certificado.
|
|
85
|
+
|
|
86
|
+
## Otras consultas
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from pathlib import Path
|
|
90
|
+
|
|
91
|
+
from radiankore.catalogs import IdentificationType
|
|
92
|
+
|
|
93
|
+
status = client.get_status("CUFE-O-TRACK-ID")
|
|
94
|
+
if status is not None:
|
|
95
|
+
print(status.is_valid, status.status_code, status.messages)
|
|
96
|
+
|
|
97
|
+
acquirer = client.get_acquirer(
|
|
98
|
+
identification_type=IdentificationType.NIT,
|
|
99
|
+
identification_number="000123456",
|
|
100
|
+
)
|
|
101
|
+
if acquirer is not None:
|
|
102
|
+
print(acquirer.name, acquirer.email)
|
|
103
|
+
|
|
104
|
+
document = client.get_document_info("UUID-DEL-DOCUMENTO")
|
|
105
|
+
if document is not None:
|
|
106
|
+
for item in document.documents:
|
|
107
|
+
if item is not None:
|
|
108
|
+
print(item.uuid, item.totals)
|
|
109
|
+
|
|
110
|
+
notes = client.get_reference_notes("CUFE-O-TRACK-ID")
|
|
111
|
+
if notes is not None:
|
|
112
|
+
for note in notes.notes:
|
|
113
|
+
print(note.code, note.description)
|
|
114
|
+
|
|
115
|
+
xml = client.get_xml_by_document_key("CLAVE-DEL-DOCUMENTO")
|
|
116
|
+
if xml is not None and xml.document_xml is not None:
|
|
117
|
+
Path("documento.xml").write_bytes(xml.document_xml)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
La [documentación completa](https://radiankore.readthedocs.io/) incluye contratos,
|
|
121
|
+
modelos, errores y ejemplos por operación. Para contribuir, consulta
|
|
122
|
+
[CONTRIBUTING](https://github.com/Deikore/radiankore/blob/HEAD/CONTRIBUTING.md).
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# radiankore
|
|
2
|
+
|
|
3
|
+
Librería Python de Deikore SAS para consultar servicios de DIAN y eventos RADIAN
|
|
4
|
+
con certificado digital PFX/P12. Python >=3.13, API síncrona y licencia MIT.
|
|
5
|
+
|
|
6
|
+
| | Estado |
|
|
7
|
+
| --- | --- |
|
|
8
|
+
| Documentación | [](https://radiankore.readthedocs.io/) |
|
|
9
|
+
| Calidad y pruebas | [](https://github.com/Deikore/radiankore/actions/workflows/ci.yml) |
|
|
10
|
+
| Paquete | [](https://pypi.org/project/radiankore/) [](https://pypi.org/project/radiankore/) [](https://github.com/Deikore/radiankore/blob/HEAD/LICENSE) |
|
|
11
|
+
|
|
12
|
+
## Instalación
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install radiankore
|
|
16
|
+
# Con uv:
|
|
17
|
+
uv add radiankore
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Disponible tras la primera publicación en PyPI. Si necesitas compilar las
|
|
21
|
+
dependencias de firma, consulta la
|
|
22
|
+
[guía de instalación](https://radiankore.readthedocs.io/).
|
|
23
|
+
|
|
24
|
+
## Métodos disponibles
|
|
25
|
+
|
|
26
|
+
| Método | Operación DIAN | Resultado |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| `get_status(track_id)` | `GetStatus` | Estado de validación y payloads decodificados |
|
|
29
|
+
| `get_invoice_events(cufe)` | `GetStatusEvent` | Eventos asociados a una factura |
|
|
30
|
+
| `get_acquirer(identification_type=..., identification_number=...)` | `GetAcquirer` | Nombre y correo del adquiriente |
|
|
31
|
+
| `get_document_info(uuid)` | `GetDocumentInfo` | Información, estados, eventos y notas del documento |
|
|
32
|
+
| `get_reference_notes(track_id)` | `GetReferenceNotes` | Notas referenciadas y motivos por línea |
|
|
33
|
+
| `get_xml_by_document_key(document_key)` | `GetXmlByDocumentKey` | XML original del documento como `bytes` |
|
|
34
|
+
|
|
35
|
+
No genera facturas ni envía eventos. Los resultados utilizan dataclasses
|
|
36
|
+
inmutables y campos opcionales cuando corresponda.
|
|
37
|
+
|
|
38
|
+
## Consultar eventos
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from radiankore import DianClient, Environment
|
|
42
|
+
|
|
43
|
+
client = DianClient(
|
|
44
|
+
certificate_path="/ruta/segura/certificado.pfx",
|
|
45
|
+
password="password-de-ejemplo",
|
|
46
|
+
environment=Environment.PRODUCTION,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
result = client.get_invoice_events("CUFE-DE-LA-FACTURA")
|
|
50
|
+
for event in result.events:
|
|
51
|
+
print(event.id, event.code, event.description, event.date, event.time)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Producción es el ambiente predeterminado; usa `Environment.HABILITATION` para
|
|
55
|
+
habilitación. Cada consulta cierra sus conexiones automáticamente y puedes
|
|
56
|
+
reutilizar el cliente sin volver a cargar el certificado.
|
|
57
|
+
|
|
58
|
+
## Otras consultas
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from pathlib import Path
|
|
62
|
+
|
|
63
|
+
from radiankore.catalogs import IdentificationType
|
|
64
|
+
|
|
65
|
+
status = client.get_status("CUFE-O-TRACK-ID")
|
|
66
|
+
if status is not None:
|
|
67
|
+
print(status.is_valid, status.status_code, status.messages)
|
|
68
|
+
|
|
69
|
+
acquirer = client.get_acquirer(
|
|
70
|
+
identification_type=IdentificationType.NIT,
|
|
71
|
+
identification_number="000123456",
|
|
72
|
+
)
|
|
73
|
+
if acquirer is not None:
|
|
74
|
+
print(acquirer.name, acquirer.email)
|
|
75
|
+
|
|
76
|
+
document = client.get_document_info("UUID-DEL-DOCUMENTO")
|
|
77
|
+
if document is not None:
|
|
78
|
+
for item in document.documents:
|
|
79
|
+
if item is not None:
|
|
80
|
+
print(item.uuid, item.totals)
|
|
81
|
+
|
|
82
|
+
notes = client.get_reference_notes("CUFE-O-TRACK-ID")
|
|
83
|
+
if notes is not None:
|
|
84
|
+
for note in notes.notes:
|
|
85
|
+
print(note.code, note.description)
|
|
86
|
+
|
|
87
|
+
xml = client.get_xml_by_document_key("CLAVE-DEL-DOCUMENTO")
|
|
88
|
+
if xml is not None and xml.document_xml is not None:
|
|
89
|
+
Path("documento.xml").write_bytes(xml.document_xml)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
La [documentación completa](https://radiankore.readthedocs.io/) incluye contratos,
|
|
93
|
+
modelos, errores y ejemplos por operación. Para contribuir, consulta
|
|
94
|
+
[CONTRIBUTING](https://github.com/Deikore/radiankore/blob/HEAD/CONTRIBUTING.md).
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Technical reference material
|
|
2
|
+
|
|
3
|
+
The files under docs/references/dian are third-party reference material from
|
|
4
|
+
DIAN and the organizations credited in the original documents and schemas.
|
|
5
|
+
The project's MIT license does not relicense those files. Their original
|
|
6
|
+
notices and applicable terms remain in effect. They are excluded from both
|
|
7
|
+
the wheel and source distributions published to PyPI.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27,<2"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "radiankore"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python library for RADIAN events and invoice queries through DIAN."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE", "THIRD_PARTY_NOTICES"]
|
|
13
|
+
authors = [{ name = "Deikore SAS" }]
|
|
14
|
+
keywords = ["dian", "radian", "colombia", "soap", "electronic-invoicing"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
21
|
+
"Programming Language :: Python :: 3.15",
|
|
22
|
+
"Typing :: Typed",
|
|
23
|
+
]
|
|
24
|
+
dependencies = ["cryptography>=46", "httpx>=0.28", "lxml>=5", "xmlsec>=1.3.17"]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/Deikore/radiankore"
|
|
28
|
+
Documentation = "https://radiankore.readthedocs.io/"
|
|
29
|
+
Repository = "https://github.com/Deikore/radiankore"
|
|
30
|
+
Issues = "https://github.com/Deikore/radiankore/issues"
|
|
31
|
+
Changelog = "https://github.com/Deikore/radiankore/blob/HEAD/CHANGELOG.md"
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"mypy>=1.15,<2",
|
|
36
|
+
"pytest>=8.3,<10",
|
|
37
|
+
"pytest-cov>=6,<8",
|
|
38
|
+
"ruff>=0.11,<1",
|
|
39
|
+
"lxml-stubs>=0.5.1",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.wheel]
|
|
43
|
+
packages = ["src/radiankore"]
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build.targets.sdist]
|
|
46
|
+
include = ["/src", "/tests", "/README.md", "/LICENSE", "/THIRD_PARTY_NOTICES", "/pyproject.toml"]
|
|
47
|
+
|
|
48
|
+
[tool.ruff]
|
|
49
|
+
target-version = "py313"
|
|
50
|
+
line-length = 88
|
|
51
|
+
|
|
52
|
+
[tool.ruff.lint]
|
|
53
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
|
|
54
|
+
|
|
55
|
+
[tool.mypy]
|
|
56
|
+
python_version = "3.13"
|
|
57
|
+
strict = true
|
|
58
|
+
files = ["src", "tests"]
|
|
59
|
+
mypy_path = "src"
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
minversion = "8.3"
|
|
63
|
+
testpaths = ["tests"]
|
|
64
|
+
addopts = "--strict-config --strict-markers --import-mode=importlib"
|
|
65
|
+
filterwarnings = ["error"]
|
|
66
|
+
|
|
67
|
+
[tool.coverage.run]
|
|
68
|
+
branch = true
|
|
69
|
+
source = ["radiankore"]
|
|
70
|
+
relative_files = true
|
|
71
|
+
|
|
72
|
+
[tool.coverage.report]
|
|
73
|
+
show_missing = true
|
|
74
|
+
skip_covered = true
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Public interface for the radiankore library."""
|
|
2
|
+
|
|
3
|
+
from radiankore.certificate import DigitalCertificate, DigitalCertificateError
|
|
4
|
+
from radiankore.client import DianClient
|
|
5
|
+
from radiankore.environment import Environment
|
|
6
|
+
from radiankore.exceptions import (
|
|
7
|
+
DianError,
|
|
8
|
+
DianResponseError,
|
|
9
|
+
DianSignatureError,
|
|
10
|
+
DianSoapFaultError,
|
|
11
|
+
DianTransportError,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"DianClient",
|
|
16
|
+
"DianError",
|
|
17
|
+
"DianResponseError",
|
|
18
|
+
"DianSignatureError",
|
|
19
|
+
"DianSoapFaultError",
|
|
20
|
+
"DianTransportError",
|
|
21
|
+
"DigitalCertificate",
|
|
22
|
+
"DigitalCertificateError",
|
|
23
|
+
"Environment",
|
|
24
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Internal SOAP implementation; consumers should use DianClient."""
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Map GetAcquirer results independently of invoice event response processing."""
|
|
2
|
+
|
|
3
|
+
from radiankore._soap.namespaces import ACQUIRER
|
|
4
|
+
from radiankore._soap.parsing import element_text, soap_result
|
|
5
|
+
from radiankore.models.responses import AcquirerResult
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def parse_acquirer_response(data: bytes) -> AcquirerResult | None:
|
|
9
|
+
"""Return optional acquirer data, preserving any returned business status."""
|
|
10
|
+
result = soap_result(data, "GetAcquirer")
|
|
11
|
+
if result is None:
|
|
12
|
+
return None
|
|
13
|
+
return AcquirerResult(
|
|
14
|
+
name=element_text(result, f"{{{ACQUIRER}}}ReceiverName"),
|
|
15
|
+
email=element_text(result, f"{{{ACQUIRER}}}ReceiverEmail"),
|
|
16
|
+
status_code=element_text(result, f"{{{ACQUIRER}}}StatusCode"),
|
|
17
|
+
message=element_text(result, f"{{{ACQUIRER}}}Message"),
|
|
18
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Scalar and binary readers shared by DianResponse operation parsers."""
|
|
2
|
+
|
|
3
|
+
from lxml import etree
|
|
4
|
+
|
|
5
|
+
from radiankore._soap.namespaces import ARRAY, RESPONSE
|
|
6
|
+
from radiankore._soap.parsing import decode_base64_text, is_nil
|
|
7
|
+
from radiankore.exceptions import DianResponseError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def read_is_valid(result: etree._Element, *, operation: str) -> bool | None:
|
|
11
|
+
"""Read an optional, non-nillable DianResponse validity flag."""
|
|
12
|
+
node = result.find(f"{{{RESPONSE}}}IsValid")
|
|
13
|
+
if node is None:
|
|
14
|
+
return None
|
|
15
|
+
if is_nil(node):
|
|
16
|
+
raise DianResponseError(f"IsValid must not be nil in {operation}.")
|
|
17
|
+
value = (node.text or "").strip()
|
|
18
|
+
if value not in {"true", "false", "1", "0"}:
|
|
19
|
+
cause = ValueError("Invalid xs:boolean lexical value.")
|
|
20
|
+
raise DianResponseError(f"Invalid IsValid in {operation}.") from cause
|
|
21
|
+
return value in {"true", "1"}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def read_messages(result: etree._Element) -> tuple[str | None, ...]:
|
|
25
|
+
"""Preserve message order, repetitions, empty strings and nullable items."""
|
|
26
|
+
container = result.find(f"{{{RESPONSE}}}ErrorMessage")
|
|
27
|
+
if container is None or is_nil(container):
|
|
28
|
+
return ()
|
|
29
|
+
return tuple(
|
|
30
|
+
None if is_nil(node) else (node.text or "").strip()
|
|
31
|
+
for node in container.findall(f"{{{ARRAY}}}string")
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def read_binary(result: etree._Element, name: str) -> bytes | None:
|
|
36
|
+
"""Decode one binary field independently, distinguishing absent from empty."""
|
|
37
|
+
node = result.find(f"{{{RESPONSE}}}{name}")
|
|
38
|
+
if node is None or is_nil(node):
|
|
39
|
+
return None
|
|
40
|
+
return decode_base64_text(node.text or "", field_name=name)
|