libredte-lib-sdk 0.1.0__tar.gz → 0.3.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 (101) hide show
  1. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/.github/workflows/ci.yml +4 -1
  2. libredte_lib_sdk-0.3.0/.github/workflows/docs.yml +43 -0
  3. libredte_lib_sdk-0.3.0/.gitignore +21 -0
  4. libredte_lib_sdk-0.3.0/.readthedocs.yaml +26 -0
  5. libredte_lib_sdk-0.3.0/CLAUDE.md +37 -0
  6. libredte_lib_sdk-0.3.0/LICENSE +21 -0
  7. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/Makefile +10 -2
  8. libredte_lib_sdk-0.3.0/PKG-INFO +272 -0
  9. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/README.rst +68 -38
  10. libredte_lib_sdk-0.3.0/docs/Makefile +20 -0
  11. libredte_lib_sdk-0.3.0/docs/conf.py +31 -0
  12. libredte_lib_sdk-0.3.0/docs/index.rst +14 -0
  13. libredte_lib_sdk-0.3.0/libredte_lib_sdk/__init__.py +18 -0
  14. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/billing/__init__.py +9 -0
  15. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/book/__init__.py +31 -0
  16. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/book/builder.py +42 -0
  17. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/book/loader.py +42 -0
  18. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/book/models.py +70 -0
  19. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/book/validator.py +51 -0
  20. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/billing/common.py +5 -2
  21. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/billing/document/__init__.py +22 -8
  22. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/document/builder.py +83 -0
  23. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/document/dispatcher.py +106 -0
  24. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/document/examples.py +41 -0
  25. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/document/loader.py +31 -0
  26. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/document/models.py +226 -0
  27. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/billing/document/renderer.py +21 -9
  28. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/document/validator.py +70 -0
  29. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/enums.py +22 -0
  30. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/exchange/__init__.py +25 -0
  31. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/exchange/document_response.py +121 -0
  32. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/exchange/models.py +55 -0
  33. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/billing/identifier/__init__.py +3 -0
  34. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/billing/identifier/caf_faker.py +6 -4
  35. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/identifier/caf_loader.py +29 -0
  36. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/billing/identifier/caf_validator.py +6 -7
  37. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/billing/identifier/models.py +3 -0
  38. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/integration/__init__.py +52 -0
  39. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/integration/models.py +253 -0
  40. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/integration/sii_dte.py +192 -0
  41. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/integration/sii_rcv.py +137 -0
  42. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/integration/sii_rtc.py +56 -0
  43. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/ownership_transfer/__init__.py +24 -0
  44. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/ownership_transfer/aec.py +91 -0
  45. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/ownership_transfer/models.py +31 -0
  46. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/billing/trading_parties/__init__.py +5 -1
  47. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/trading_parties/mandatario_manager.py +54 -0
  48. libredte_lib_sdk-0.3.0/libredte_lib_sdk/billing/trading_parties/models.py +110 -0
  49. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/client.py +35 -2
  50. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/exceptions.py +17 -0
  51. libredte_lib_sdk-0.3.0/libredte_lib_sdk/human_resources/__init__.py +28 -0
  52. libredte_lib_sdk-0.3.0/libredte_lib_sdk/human_resources/integration/__init__.py +22 -0
  53. libredte_lib_sdk-0.3.0/libredte_lib_sdk/human_resources/integration/previred_provider.py +41 -0
  54. libredte_lib_sdk-0.3.0/libredte_lib_sdk/human_resources/payroll/__init__.py +25 -0
  55. libredte_lib_sdk-0.3.0/libredte_lib_sdk/human_resources/payroll/calculator.py +51 -0
  56. libredte_lib_sdk-0.3.0/libredte_lib_sdk/human_resources/payroll/renderer.py +50 -0
  57. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/sdk.py +13 -3
  58. libredte_lib_sdk-0.3.0/libredte_lib_sdk/system/__init__.py +30 -0
  59. libredte_lib_sdk-0.3.0/libredte_lib_sdk/system/certificate/__init__.py +22 -0
  60. libredte_lib_sdk-0.3.0/libredte_lib_sdk/system/certificate/loader.py +38 -0
  61. libredte_lib_sdk-0.3.0/libredte_lib_sdk/system/repository/__init__.py +22 -0
  62. libredte_lib_sdk-0.3.0/libredte_lib_sdk/system/repository/catalog.py +130 -0
  63. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/pyproject.toml +12 -5
  64. libredte_lib_sdk-0.3.0/tests/__init__.py +3 -0
  65. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/tests/conftest.py +5 -1
  66. libredte_lib_sdk-0.3.0/tests/integration/__init__.py +3 -0
  67. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/tests/integration/conftest.py +60 -4
  68. libredte_lib_sdk-0.3.0/tests/integration/test_book.py +109 -0
  69. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/tests/integration/test_caf.py +4 -1
  70. libredte_lib_sdk-0.3.0/tests/integration/test_certificate_loader.py +74 -0
  71. libredte_lib_sdk-0.3.0/tests/integration/test_document_loader.py +30 -0
  72. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/tests/integration/test_document_rendering.py +4 -1
  73. libredte_lib_sdk-0.3.0/tests/integration/test_document_validation.py +113 -0
  74. libredte_lib_sdk-0.3.0/tests/integration/test_examples.py +72 -0
  75. libredte_lib_sdk-0.3.0/tests/integration/test_exchange.py +112 -0
  76. libredte_lib_sdk-0.3.0/tests/integration/test_human_resources.py +107 -0
  77. libredte_lib_sdk-0.3.0/tests/integration/test_operation_not_found.py +34 -0
  78. libredte_lib_sdk-0.3.0/tests/integration/test_ownership_transfer.py +56 -0
  79. libredte_lib_sdk-0.3.0/tests/integration/test_repository.py +83 -0
  80. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/tests/test_client.py +51 -3
  81. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/tests/test_models.py +54 -37
  82. libredte_lib_sdk-0.3.0/tests/test_services.py +1649 -0
  83. libredte_lib_sdk-0.1.0/.gitignore +0 -15
  84. libredte_lib_sdk-0.1.0/CLAUDE.md +0 -482
  85. libredte_lib_sdk-0.1.0/COPYING +0 -661
  86. libredte_lib_sdk-0.1.0/PKG-INFO +0 -878
  87. libredte_lib_sdk-0.1.0/libredte_lib_sdk/__init__.py +0 -75
  88. libredte_lib_sdk-0.1.0/libredte_lib_sdk/billing/document/builder.py +0 -68
  89. libredte_lib_sdk-0.1.0/libredte_lib_sdk/billing/document/dispatcher.py +0 -49
  90. libredte_lib_sdk-0.1.0/libredte_lib_sdk/billing/document/models.py +0 -169
  91. libredte_lib_sdk-0.1.0/libredte_lib_sdk/billing/enums.py +0 -18
  92. libredte_lib_sdk-0.1.0/libredte_lib_sdk/billing/identifier/caf_loader.py +0 -31
  93. libredte_lib_sdk-0.1.0/libredte_lib_sdk/billing/integration/__init__.py +0 -17
  94. libredte_lib_sdk-0.1.0/libredte_lib_sdk/billing/integration/models.py +0 -46
  95. libredte_lib_sdk-0.1.0/libredte_lib_sdk/billing/integration/sii_dte.py +0 -83
  96. libredte_lib_sdk-0.1.0/libredte_lib_sdk/billing/trading_parties/mandatario_manager.py +0 -45
  97. libredte_lib_sdk-0.1.0/libredte_lib_sdk/billing/trading_parties/models.py +0 -38
  98. libredte_lib_sdk-0.1.0/tests/__init__.py +0 -0
  99. libredte_lib_sdk-0.1.0/tests/integration/__init__.py +0 -0
  100. libredte_lib_sdk-0.1.0/tests/test_services.py +0 -428
  101. {libredte_lib_sdk-0.1.0 → libredte_lib_sdk-0.3.0}/libredte_lib_sdk/py.typed +0 -0
@@ -17,7 +17,7 @@ jobs:
17
17
  strategy:
18
18
  matrix:
19
19
  os: [ubuntu-latest]
20
- python-version: ['3.14']
20
+ python-version: ['3.11', '3.12', '3.13', '3.14']
21
21
 
22
22
  steps:
23
23
  - name: Check out repository
@@ -42,6 +42,9 @@ jobs:
42
42
  - name: Run Ruff (format)
43
43
  run: ruff format --check .
44
44
 
45
+ - name: Run mypy
46
+ run: mypy
47
+
45
48
  # No corre los tests `live` (marker `live`, excluidos por defecto vía
46
49
  # `addopts` en pyproject.toml): golpean la API real de LibreDTE y/o
47
50
  # un ambiente de desarrollo local, no algo apto para CI.
@@ -0,0 +1,43 @@
1
+ name: Docs
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches:
7
+ - main
8
+
9
+ jobs:
10
+ docs:
11
+ name: Docs
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - name: Check out repository
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: '3.13'
22
+
23
+ - name: Install dependencies
24
+ run: |
25
+ python -m pip install --upgrade pip
26
+ pip install -e '.[docs]'
27
+
28
+ - name: Generate API reference
29
+ run: sphinx-apidoc -o docs/ libredte_lib_sdk --force --separate
30
+
31
+ - name: Build documentation
32
+ run: sphinx-build -b html docs/ docs/_build/html
33
+
34
+ - name: Create CNAME file
35
+ run: echo "lib-sdk-python.docs.libredte.cl" > docs/_build/html/CNAME
36
+
37
+ - name: Deploy to GitHub Pages
38
+ if: success()
39
+ uses: peaceiris/actions-gh-pages@v4
40
+ with:
41
+ github_token: ${{ secrets.GITHUB_TOKEN }}
42
+ publish_dir: ./docs/_build/html
43
+ publish_branch: gh-pages
@@ -0,0 +1,21 @@
1
+ # Python.
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .venv/
6
+ venv/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ dist/
10
+ build/
11
+ .coverage
12
+ var/
13
+ # Referencia de API generada por `sphinx-apidoc` (ver `make docs` /
14
+ # `build.jobs.pre_build` en `.readthedocs.yaml`) — no se commitea, se
15
+ # regenera en cada build a partir de los docstrings.
16
+ docs/_build/
17
+ docs/libredte_lib_sdk*.rst
18
+ docs/modules.rst
19
+
20
+ # Archivos específicos del sistema operativo.
21
+ .DS_Store
@@ -0,0 +1,26 @@
1
+ # .readthedocs.yaml
2
+ # Read the Docs configuration file
3
+ # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4
+
5
+ version: 2
6
+
7
+ build:
8
+ os: ubuntu-24.04
9
+ tools:
10
+ python: "3.13"
11
+ jobs:
12
+ # La referencia de API (docs/libredte_lib_sdk*.rst, docs/modules.rst)
13
+ # no se commitea: se regenera acá a partir de los docstrings, igual
14
+ # que hace `make docs` en local.
15
+ pre_build:
16
+ - sphinx-apidoc -o docs/ libredte_lib_sdk --force --separate
17
+
18
+ sphinx:
19
+ configuration: docs/conf.py
20
+
21
+ python:
22
+ install:
23
+ - method: pip
24
+ path: .
25
+ extra_requirements:
26
+ - docs
@@ -0,0 +1,37 @@
1
+ # `libredte_lib_sdk` — pendientes
2
+
3
+ ## Pendiente
4
+
5
+ - **Tests en vivo de `integration.sii_dte`/`sii_rcv`/`sii_rtc`** (10
6
+ operaciones): tienen test unitario (mockeado), pero ninguno en
7
+ `live`. A diferencia del resto de la suite `live` (que usa
8
+ `fake_certificate`, autofirmado), estas operaciones hablan con el SII
9
+ real y necesitan un certificado real vigente — no hay fixture para
10
+ eso hoy, y committear uno no correspondería. Falta decidir el
11
+ mecanismo (ej. certificado real vía variable de entorno, `skip` si
12
+ no está disponible) antes de poder escribirlos.
13
+ - **DTO `Liquidacion` sin construir.** `human_resources.payroll
14
+ .calculator::calculate()` recibe `empleado`/`contrato`/`contexto`
15
+ como `dict` (correcto — son entradas) pero **devuelve** la
16
+ liquidación también como `dict`, sin tipar; al ser una respuesta,
17
+ debería tipar como un DTO real (reusable también como entrada de
18
+ `renderer::render()`, que hoy acepta el mismo `dict` tal cual, igual
19
+ que `Certificate`). Pendiente por el tamaño de la entidad real en
20
+ `libredte-lib-pro` (`Liquidacion`, con `Empleado`/`Contrato`/
21
+ `LiquidacionContexto` anidados; `Empleado` por sí solo tiene ~26
22
+ campos con varios enums y objetos anidados — `Contacto`,
23
+ `CuentaBancaria`, `CargaFamiliar`).
24
+ - **`exchange.receiver`/`exchange.sender`** (framework genérico de
25
+ intercambio de documentos comerciales, tipo Peppol/UBL, no solo DTE)
26
+ queda fuera del SDK — se agrega cuando una app consumidora lo
27
+ necesite de verdad.
28
+ - **Docs (Sphinx) armadas pero sin publicar.** `.readthedocs.yaml` +
29
+ `docs/` generan la documentación (`make docs`), y
30
+ `.github/workflows/docs.yml` ya publica a GitHub Pages
31
+ (`gh-pages`) con CNAME `lib-sdk-python.docs.libredte.cl` en cada
32
+ push a `main`. Faltan 2 pasos externos, ninguno hecho todavía: (1)
33
+ conectar el repo en readthedocs.org (sin eso no hay build ahí ni URL
34
+ en `readthedocs.io`); (2) apuntar el DNS de
35
+ `lib-sdk-python.docs.libredte.cl` a GitHub Pages y habilitar el
36
+ origen `gh-pages` en la configuración de Pages del repo (sin eso el
37
+ workflow corre pero el dominio no sirve nada).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (C) 2026 LibreDTE <https://www.libredte.cl>
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.
@@ -1,4 +1,4 @@
1
- .PHONY: install-dev lint format format-check test test-live check build upload clean
1
+ .PHONY: install-dev lint format format-check typecheck test test-live check docs build upload clean
2
2
 
3
3
  VENV = .venv
4
4
  VENV_READY = $(VENV)/.installed
@@ -20,6 +20,9 @@ format: $(VENV_READY)
20
20
  format-check: $(VENV_READY)
21
21
  $(VENV)/bin/ruff format --check .
22
22
 
23
+ typecheck: $(VENV_READY)
24
+ $(VENV)/bin/mypy
25
+
23
26
  test: $(VENV_READY)
24
27
  $(VENV)/bin/pytest -v
25
28
 
@@ -29,7 +32,12 @@ test: $(VENV_READY)
29
32
  test-live: $(VENV_READY)
30
33
  $(VENV)/bin/pytest -v -m live
31
34
 
32
- check: lint format-check test
35
+ check: lint format-check typecheck test
36
+
37
+ docs: $(VENV_READY)
38
+ $(VENV)/bin/pip install -e '.[docs]'
39
+ $(VENV)/bin/sphinx-apidoc -o docs libredte_lib_sdk --force --separate
40
+ $(VENV)/bin/sphinx-build -b html docs docs/_build/html
33
41
 
34
42
  build: $(VENV_READY)
35
43
  $(VENV)/bin/python -m build
@@ -0,0 +1,272 @@
1
+ Metadata-Version: 2.5
2
+ Name: libredte-lib-sdk
3
+ Version: 0.3.0
4
+ Summary: SDK Python para la API de LibreDTE Lib (facturacion electronica de Chile).
5
+ Project-URL: Homepage, https://www.libredte.cl
6
+ Project-URL: Source, https://github.com/libredte/libredte-lib-sdk-python
7
+ Author: LibreDTE
8
+ License: MIT License
9
+
10
+ Copyright (C) 2026 LibreDTE <https://www.libredte.cl>
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+ License-File: LICENSE
30
+ Requires-Python: >=3.11
31
+ Requires-Dist: httpx>=0.27
32
+ Provides-Extra: dev
33
+ Requires-Dist: build; extra == 'dev'
34
+ Requires-Dist: mypy; extra == 'dev'
35
+ Requires-Dist: pytest; extra == 'dev'
36
+ Requires-Dist: pytest-cov; extra == 'dev'
37
+ Requires-Dist: respx; extra == 'dev'
38
+ Requires-Dist: ruff; extra == 'dev'
39
+ Requires-Dist: twine; extra == 'dev'
40
+ Provides-Extra: docs
41
+ Requires-Dist: sphinx; extra == 'docs'
42
+ Requires-Dist: sphinx-rtd-theme; extra == 'docs'
43
+ Description-Content-Type: text/x-rst
44
+
45
+ LibreDTE: SDK Python para la API de LibreDTE Lib
46
+ ================================================
47
+
48
+ SDK Python (``libredte_lib_sdk``) que orquesta la API de LibreDTE Lib
49
+ (``https://core.libredte.cl/api`` por defecto) para cubrir el ciclo de
50
+ emisión de un DTE: borrador, documento timbrado y firmado,
51
+ envío al SII, consulta de estado por Track ID, y generación de HTML/PDF
52
+ — más carga y validación de CAF real, y generación de CAF y certificado
53
+ ficticios para poder probar todo lo anterior sin credenciales reales.
54
+
55
+ Pensado como base de una app de facturación (ej. Django): este SDK
56
+ concentra la lógica de negocio/orquestación de DTE, para que la app web
57
+ sea solo vista, autenticación y persistencia.
58
+
59
+ El paquete Python espeja la organización de la propia API
60
+ (``package.component.worker::operation``): ``sdk.billing.<component>
61
+ .<worker>`` para negocio DTE, ``sdk.system.<component>.<worker>`` para
62
+ infraestructura de la biblioteca (catálogos reales, carga de certificados
63
+ — ver "Recursos de `system`" más abajo).
64
+
65
+ Instalación
66
+ -----------
67
+
68
+ .. code-block:: bash
69
+
70
+ pip install libredte-lib-sdk
71
+
72
+ Uso
73
+ ---
74
+
75
+ .. code-block:: python
76
+
77
+ from libredte_lib_sdk import LibreDTE
78
+ from libredte_lib_sdk.billing.trading_parties import Mandatario
79
+
80
+ sdk = LibreDTE() # base_url y api_token también vía env vars, ver abajo
81
+ b = sdk.billing
82
+
83
+ input_data = {
84
+ 'Encabezado': {
85
+ 'IdDoc': {
86
+ 'TipoDTE': 33,
87
+ 'Folio': 1
88
+ },
89
+ 'Emisor': {
90
+ 'RUTEmisor': '76192083-9',
91
+ 'RznSoc': 'SASCO SpA',
92
+ 'GiroEmis': 'Servicios',
93
+ 'DirOrigen': 'Santiago',
94
+ 'CmnaOrigen': 'Santiago',
95
+ },
96
+ 'Receptor': {
97
+ 'RUTRecep': '12345678-5',
98
+ 'RznSocRecep': 'Cliente de prueba',
99
+ 'GiroRecep': 'Giro',
100
+ 'DirRecep': 'Santiago',
101
+ 'CmnaRecep': 'Santiago',
102
+ },
103
+ },
104
+ 'Detalle': [
105
+ {
106
+ 'NmbItem': 'Producto A',
107
+ 'QtyItem': 1,
108
+ 'PrcItem': 1000
109
+ }
110
+ ],
111
+ }
112
+
113
+ # CAF y certificado ficticios, para pruebas/desarrollo:
114
+ caf = b.identifier.caf_faker.create(
115
+ {'rut': '76192083-9', 'razon_social': 'SASCO SpA'},
116
+ codigo_documento=33, folio_desde=1, folio_hasta=100,
117
+ )
118
+ certificate = b.trading_parties.mandatario_manager.create_fake_certificate(
119
+ Mandatario(rut='76192083-9', nombre='SASCO SpA', email='sasco@example.com'),
120
+ )
121
+
122
+ # En producción: cargar y validar el CAF real que el SII le entregó
123
+ # al emisor, en vez del CAF ficticio de arriba.
124
+ # caf = b.identifier.caf_loader.load(xml_base64_del_caf_subido)
125
+ # b.identifier.caf_validator.validate(caf.xml_base64)
126
+
127
+ # 1. Borrador.
128
+ borrador = b.document.builder.build_draft(input_data)
129
+
130
+ # 2. Documento real, timbrado y firmado.
131
+ documento = b.document.builder.build_signed(
132
+ input_data, caf_xml=caf.xml_base64, certificate=certificate,
133
+ )
134
+
135
+ # 3. Envío al SII (arma el sobre EnvioDTE y luego lo envía).
136
+ emisor = {
137
+ 'rut': '76192083-9',
138
+ 'razon_social': 'SASCO SpA',
139
+ 'autorizacion_dte': {
140
+ 'fecha_resolucion': '2014-08-22', 'numero_resolucion': 80,
141
+ },
142
+ }
143
+ sobre = b.document.dispatcher.create(
144
+ documento.xml_base64, certificate=certificate, emisor=emisor,
145
+ )
146
+ envio = b.integration.sii_dte.send(
147
+ sobre.xml_base64, certificate=certificate, company_rut='76192083-9',
148
+ )
149
+
150
+ # 4. Estado del envío, por Track ID.
151
+ estado = b.integration.sii_dte.check_status(
152
+ envio.track_id, certificate=certificate, company_rut='76192083-9',
153
+ )
154
+
155
+ # 5. HTML o PDF — misma forma de respuesta para ambos.
156
+ html = b.document.renderer.render(documento.xml_base64, format='html')
157
+ pdf = b.document.renderer.render(documento.xml_base64, format='pdf')
158
+ pdf_bytes = pdf.first.content_bytes
159
+
160
+ # Varias copias/presentaciones en una sola llamada (ej. cedible +
161
+ # tributaria):
162
+ copias = b.document.renderer.render(
163
+ documento.xml_base64,
164
+ format='pdf',
165
+ renderings={'tributaria': 1, 'cedible': 1},
166
+ )
167
+ cedible_bytes = copias.by_label('cedible')[0].content_bytes
168
+
169
+ Cada componente (``document``, ``identifier``, ``trading_parties``,
170
+ ``integration``) agrupa servicios independientes, uno por *worker* de la
171
+ API: cada método hace exactamente una llamada HTTP, sin encadenar red por
172
+ detrás. Encadenar los pasos (ej. armar el documento, el sobre, y
173
+ enviarlo) es responsabilidad de quien use el SDK.
174
+
175
+ Configuración
176
+ -------------
177
+
178
+ Base URL
179
+ ~~~~~~~~
180
+
181
+ Por defecto el SDK apunta a ``https://core.libredte.cl/api``. Para usar
182
+ otra API (ej. ``https://pro.libredte.cl/api``, o un ambiente propio),
183
+ pasar ``base_url`` al construir ``LibreDTE`` o setear la variable de
184
+ entorno ``LIBREDTE_LIB_SDK_BASE_URL``:
185
+
186
+ .. code-block:: python
187
+
188
+ sdk = LibreDTE(base_url='https://pro.libredte.cl/api')
189
+
190
+ .. code-block:: bash
191
+
192
+ export LIBREDTE_LIB_SDK_BASE_URL=https://pro.libredte.cl/api
193
+
194
+ ``core.libredte.cl`` y ``pro.libredte.cl`` exponen el mismo contrato con
195
+ límites de solicitudes distintos; ambos aceptan la variante ``LibreDTE
196
+ (base_url=..., ...)`` con el resto de los parámetros de configuración
197
+ (``api_token``, ``timeout``, ``http_client``).
198
+
199
+ Autenticación
200
+ ~~~~~~~~~~~~~
201
+
202
+ ``LibreDTE(api_token=...)`` o la variable de entorno
203
+ ``LIBREDTE_LIB_SDK_TOKEN`` — si está presente, se manda como header
204
+ ``Authorization: Bearer <token>``.
205
+
206
+ Recursos de ``system``
207
+ -----------------------
208
+
209
+ Además de ``billing`` (negocio DTE), el SDK expone ``system``:
210
+ infraestructura de la biblioteca que una app consumidora
211
+ necesita para poblar sus propios datos, no operaciones de facturación.
212
+
213
+ .. code-block:: python
214
+
215
+ s = sdk.system
216
+
217
+ # Ejemplos de documentos reales (los mismos casos de prueba,
218
+ # validados, de libredte-lib-core) — útiles para poblar datos de
219
+ # demostración con variedad real en vez de inventada a mano.
220
+ ejemplos = b.document.examples.list() # [ExampleSummary(id=..., category=..., case=...), ...]
221
+ ejemplo = b.document.examples.get(ejemplos[0].id)
222
+ documento = b.document.builder.build_signed(
223
+ ejemplo.input_data, caf_xml=caf.xml_base64, certificate=certificate,
224
+ )
225
+
226
+ # Catálogos/repositorios reales (comunas, tipos de documento, etc.)
227
+ # — el esquema de cada elemento depende del repositorio pedido, no
228
+ # hay un DTO fijo: se devuelve el dict/list tal cual la API.
229
+ repositorios = s.repository.catalog.list() # FQCN de cada repositorio disponible
230
+ tipos_dte = s.repository.catalog.find_by(
231
+ 'libredte\\lib\\Core\\Package\\Billing\\Component\\Document'
232
+ '\\Contract\\TipoDocumentoInterface',
233
+ criteria={'codigo': [33, 39]}, # coincidencia con cualquiera de estos códigos
234
+ )
235
+
236
+ # Cargar un certificado real (.p12/.pfx) sin desempaquetarlo
237
+ # localmente: la API entrega certificado (PEM) + RUT/nombre/correo
238
+ # ya extraídos en una sola llamada — mismo `Certificate` que usan
239
+ # build_signed()/dispatcher.create(), ya con todos los datos.
240
+ certificate = s.certificate.loader.load(datos_del_archivo_pfx, 'contraseña')
241
+ print(certificate.rut, certificate.nombre, certificate.email)
242
+
243
+ Errores
244
+ -------
245
+
246
+ Cualquier error de la API (validación, regla de negocio, error interno)
247
+ levanta ``libredte_lib_sdk.LibreDteApiError``, con ``.status_code``,
248
+ ``.detail`` y ``.php_class`` (la clase real de la excepción PHP detrás,
249
+ si la API la informó — ej. ``DocumentException``, o una excepción de
250
+ autenticación del SII). Un ``429`` de la API levanta
251
+ ``LibreDteRateLimitError`` (subclase de lo anterior), con
252
+ ``.retry_after``/``.limit``/``.remaining``. Un problema de red levanta
253
+ ``libredte_lib_sdk.LibreDteConnectionError``.
254
+
255
+ Desarrollo
256
+ ----------
257
+
258
+ .. code-block:: bash
259
+
260
+ make install-dev
261
+ make check # ruff + tests unitarios (offline, sin red)
262
+ make test-live # tests de integración reales contra la API
263
+
264
+ Los tests unitarios no hacen red real: mockean la API con ``respx``. Los
265
+ de ``tests/integration/`` sí (marker ``live``), y están excluidos por
266
+ defecto de ``pytest``/``make test``/``make check``.
267
+
268
+ Términos y condiciones de uso
269
+ ------------------------------
270
+
271
+ Este proyecto está licenciado bajo la Licencia MIT. Ver
272
+ `LICENSE <LICENSE>`_.
@@ -2,8 +2,8 @@ LibreDTE: SDK Python para la API de LibreDTE Lib
2
2
  ================================================
3
3
 
4
4
  SDK Python (``libredte_lib_sdk``) que orquesta la API de LibreDTE Lib
5
- (``https://core.libredte.cl/api`` por defecto) para cubrir el ciclo
6
- básico de emisión de un DTE: borrador, documento timbrado y firmado,
5
+ (``https://core.libredte.cl/api`` por defecto) para cubrir el ciclo de
6
+ emisión de un DTE: borrador, documento timbrado y firmado,
7
7
  envío al SII, consulta de estado por Track ID, y generación de HTML/PDF
8
8
  — más carga y validación de CAF real, y generación de CAF y certificado
9
9
  ficticios para poder probar todo lo anterior sin credenciales reales.
@@ -13,8 +13,10 @@ concentra la lógica de negocio/orquestación de DTE, para que la app web
13
13
  sea solo vista, autenticación y persistencia.
14
14
 
15
15
  El paquete Python espeja la organización de la propia API
16
- (``paquete.componente.worker::operacion``): ``sdk.billing.<componente>
17
- .<worker>``.
16
+ (``package.component.worker::operation``): ``sdk.billing.<component>
17
+ .<worker>`` para negocio DTE, ``sdk.system.<component>.<worker>`` para
18
+ infraestructura de la biblioteca (catálogos reales, carga de certificados
19
+ — ver "Recursos de `system`" más abajo).
18
20
 
19
21
  Instalación
20
22
  -----------
@@ -28,14 +30,18 @@ Uso
28
30
 
29
31
  .. code-block:: python
30
32
 
31
- from libredte_lib_sdk import Emisor, AutorizacionDte, LibreDTE
33
+ from libredte_lib_sdk import LibreDTE
34
+ from libredte_lib_sdk.billing.trading_parties import Mandatario
32
35
 
33
36
  sdk = LibreDTE() # base_url y api_token también vía env vars, ver abajo
34
37
  b = sdk.billing
35
38
 
36
- parsed_data = {
39
+ input_data = {
37
40
  'Encabezado': {
38
- 'IdDoc': {'TipoDTE': 33, 'Folio': 1},
41
+ 'IdDoc': {
42
+ 'TipoDTE': 33,
43
+ 'Folio': 1
44
+ },
39
45
  'Emisor': {
40
46
  'RUTEmisor': '76192083-9',
41
47
  'RznSoc': 'SASCO SpA',
@@ -51,7 +57,13 @@ Uso
51
57
  'CmnaRecep': 'Santiago',
52
58
  },
53
59
  },
54
- 'Detalle': [{'NmbItem': 'Producto A', 'QtyItem': 1, 'PrcItem': 1000}],
60
+ 'Detalle': [
61
+ {
62
+ 'NmbItem': 'Producto A',
63
+ 'QtyItem': 1,
64
+ 'PrcItem': 1000
65
+ }
66
+ ],
55
67
  }
56
68
 
57
69
  # CAF y certificado ficticios, para pruebas/desarrollo:
@@ -60,7 +72,7 @@ Uso
60
72
  codigo_documento=33, folio_desde=1, folio_hasta=100,
61
73
  )
62
74
  certificate = b.trading_parties.mandatario_manager.create_fake_certificate(
63
- {'run': '76192083-9', 'nombre': 'SASCO SpA'},
75
+ Mandatario(rut='76192083-9', nombre='SASCO SpA', email='sasco@example.com'),
64
76
  )
65
77
 
66
78
  # En producción: cargar y validar el CAF real que el SII le entregó
@@ -69,21 +81,21 @@ Uso
69
81
  # b.identifier.caf_validator.validate(caf.xml_base64)
70
82
 
71
83
  # 1. Borrador.
72
- borrador = b.document.builder.build_draft(parsed_data)
84
+ borrador = b.document.builder.build_draft(input_data)
73
85
 
74
86
  # 2. Documento real, timbrado y firmado.
75
87
  documento = b.document.builder.build_signed(
76
- parsed_data, caf_xml=caf.xml_base64, certificate=certificate,
88
+ input_data, caf_xml=caf.xml_base64, certificate=certificate,
77
89
  )
78
90
 
79
91
  # 3. Envío al SII (arma el sobre EnvioDTE y luego lo envía).
80
- emisor = Emisor(
81
- rut='76192083-9',
82
- razon_social='SASCO SpA',
83
- autorizacion_dte=AutorizacionDte(
84
- fecha_resolucion='2014-08-22', numero_resolucion=80,
85
- ),
86
- )
92
+ emisor = {
93
+ 'rut': '76192083-9',
94
+ 'razon_social': 'SASCO SpA',
95
+ 'autorizacion_dte': {
96
+ 'fecha_resolucion': '2014-08-22', 'numero_resolucion': 80,
97
+ },
98
+ }
87
99
  sobre = b.document.dispatcher.create(
88
100
  documento.xml_base64, certificate=certificate, emisor=emisor,
89
101
  )
@@ -147,22 +159,42 @@ Autenticación
147
159
  ``LIBREDTE_LIB_SDK_TOKEN`` — si está presente, se manda como header
148
160
  ``Authorization: Bearer <token>``.
149
161
 
150
- Render de documentos
151
- ---------------------
152
-
153
- ``render()`` devuelve un ``RenderResult`` con ``.renderings`` (una lista
154
- de ``Rendering``, cada uno con ``.content_bytes``/``.mime_type``/
155
- ``.filename``/``.label``/``.copies``/``.copy_number``). Sin el argumento
156
- ``renderings``, trae un único elemento (``'tributaria'``), accesible con
157
- ``.first``. Pasando ``renderings={'tributaria': 1, 'cedible': 1}`` (o
158
- más de una copia de una misma presentación, ej. ``{'tributaria': 2}``)
159
- trae uno por cada copia generada — usar ``.by_label('cedible')`` para
160
- filtrar. Una presentación no soportada por el tipo de documento (ej.
161
- ``'cedible'`` en una boleta) se omite en silencio si se pidió junto con
162
- otra que sí se pudo generar; si ninguna se pudo generar, o si se pide
163
- una presentación que no existe, la API responde con un
164
- ``LibreDteApiError`` claro. Sirve igual para ``format='html'`` y
165
- ``format='pdf'``.
162
+ Recursos de ``system``
163
+ -----------------------
164
+
165
+ Además de ``billing`` (negocio DTE), el SDK expone ``system``:
166
+ infraestructura de la biblioteca que una app consumidora
167
+ necesita para poblar sus propios datos, no operaciones de facturación.
168
+
169
+ .. code-block:: python
170
+
171
+ s = sdk.system
172
+
173
+ # Ejemplos de documentos reales (los mismos casos de prueba,
174
+ # validados, de libredte-lib-core) — útiles para poblar datos de
175
+ # demostración con variedad real en vez de inventada a mano.
176
+ ejemplos = b.document.examples.list() # [ExampleSummary(id=..., category=..., case=...), ...]
177
+ ejemplo = b.document.examples.get(ejemplos[0].id)
178
+ documento = b.document.builder.build_signed(
179
+ ejemplo.input_data, caf_xml=caf.xml_base64, certificate=certificate,
180
+ )
181
+
182
+ # Catálogos/repositorios reales (comunas, tipos de documento, etc.)
183
+ # — el esquema de cada elemento depende del repositorio pedido, no
184
+ # hay un DTO fijo: se devuelve el dict/list tal cual la API.
185
+ repositorios = s.repository.catalog.list() # FQCN de cada repositorio disponible
186
+ tipos_dte = s.repository.catalog.find_by(
187
+ 'libredte\\lib\\Core\\Package\\Billing\\Component\\Document'
188
+ '\\Contract\\TipoDocumentoInterface',
189
+ criteria={'codigo': [33, 39]}, # coincidencia con cualquiera de estos códigos
190
+ )
191
+
192
+ # Cargar un certificado real (.p12/.pfx) sin desempaquetarlo
193
+ # localmente: la API entrega certificado (PEM) + RUT/nombre/correo
194
+ # ya extraídos en una sola llamada — mismo `Certificate` que usan
195
+ # build_signed()/dispatcher.create(), ya con todos los datos.
196
+ certificate = s.certificate.loader.load(datos_del_archivo_pfx, 'contraseña')
197
+ print(certificate.rut, certificate.nombre, certificate.email)
166
198
 
167
199
  Errores
168
200
  -------
@@ -192,7 +224,5 @@ defecto de ``pytest``/``make test``/``make check``.
192
224
  Términos y condiciones de uso
193
225
  ------------------------------
194
226
 
195
- Este proyecto es software libre y puede ser redistribuido y/o modificado
196
- bajo los términos de la Licencia Pública General Affero de GNU (AGPL),
197
- versión 3 o (a elección) cualquier versión posterior. Ver
198
- `COPYING <COPYING>`_.
227
+ Este proyecto está licenciado bajo la Licencia MIT. Ver
228
+ `LICENSE <LICENSE>`_.
@@ -0,0 +1,20 @@
1
+ # Minimal makefile for Sphinx documentation
2
+ #
3
+
4
+ # You can set these variables from the command line, and also
5
+ # from the environment for the first two.
6
+ SPHINXOPTS ?=
7
+ SPHINXBUILD ?= sphinx-build
8
+ SOURCEDIR = .
9
+ BUILDDIR = _build
10
+
11
+ # Put it first so that "make" without argument is like "make help".
12
+ help:
13
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14
+
15
+ .PHONY: help Makefile
16
+
17
+ # Catch-all target: route all unknown targets to Sphinx using the new
18
+ # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19
+ %: Makefile
20
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)