beliq 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.
- beliq-0.1.0/.github/workflows/ci.yml +27 -0
- beliq-0.1.0/.github/workflows/release.yml +50 -0
- beliq-0.1.0/.gitignore +10 -0
- beliq-0.1.0/LICENSE +21 -0
- beliq-0.1.0/PKG-INFO +144 -0
- beliq-0.1.0/README.md +120 -0
- beliq-0.1.0/openapi.json +5874 -0
- beliq-0.1.0/pyproject.toml +50 -0
- beliq-0.1.0/scripts/sync_spec.py +36 -0
- beliq-0.1.0/src/beliq/__init__.py +64 -0
- beliq-0.1.0/src/beliq/_build_request.py +140 -0
- beliq-0.1.0/src/beliq/_internal.py +40 -0
- beliq-0.1.0/src/beliq/client.py +350 -0
- beliq-0.1.0/src/beliq/constants.py +58 -0
- beliq-0.1.0/src/beliq/errors.py +50 -0
- beliq-0.1.0/src/beliq/py.typed +0 -0
- beliq-0.1.0/src/beliq/types.py +102 -0
- beliq-0.1.0/tests/fixtures/error-invalid-xml.json +8 -0
- beliq-0.1.0/tests/fixtures/me.json +11 -0
- beliq-0.1.0/tests/fixtures/parse.json +12 -0
- beliq-0.1.0/tests/fixtures/validate-invalid.json +24 -0
- beliq-0.1.0/tests/test_build_request.py +141 -0
- beliq-0.1.0/tests/test_client.py +223 -0
- beliq-0.1.0/tests/test_integration.py +70 -0
- beliq-0.1.0/tests/test_spec_contract.py +52 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ['3.10', '3.11', '3.12', '3.13']
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
- run: python -m pip install -e ".[dev]"
|
|
23
|
+
- run: ruff check src tests
|
|
24
|
+
- run: mypy
|
|
25
|
+
# Includes tests/test_spec_contract.py, which fails if the hand-written
|
|
26
|
+
# contracts drift from the vendored openapi.json.
|
|
27
|
+
- run: pytest
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishes beliq to PyPI via Trusted Publishing (OIDC) - no API token needed.
|
|
4
|
+
# Attestations are generated automatically. Trigger by pushing a version tag
|
|
5
|
+
# (e.g. v0.1.1) or running the workflow manually.
|
|
6
|
+
#
|
|
7
|
+
# One-time setup on pypi.org: register beliq-eu/beliq-sdk-python as a Trusted
|
|
8
|
+
# Publisher for the beliq project (workflow `release.yml`, environment `pypi`).
|
|
9
|
+
# Prefer a pending publisher so the very first tag run creates the project via
|
|
10
|
+
# OIDC with no local upload.
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
tags:
|
|
14
|
+
- 'v*.*.*'
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
test:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: '3.12'
|
|
28
|
+
- run: python -m pip install -e ".[dev]"
|
|
29
|
+
- run: ruff check src tests
|
|
30
|
+
- run: mypy
|
|
31
|
+
- run: pytest
|
|
32
|
+
|
|
33
|
+
publish:
|
|
34
|
+
needs: test
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
environment: pypi
|
|
37
|
+
permissions:
|
|
38
|
+
id-token: write # required for PyPI Trusted Publishing (OIDC)
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
- uses: actions/setup-python@v5
|
|
42
|
+
with:
|
|
43
|
+
python-version: '3.12'
|
|
44
|
+
- name: Build
|
|
45
|
+
run: |
|
|
46
|
+
python -m pip install build
|
|
47
|
+
python -m build
|
|
48
|
+
# No `password`: the action exchanges the GitHub OIDC token with PyPI.
|
|
49
|
+
- name: Publish to PyPI
|
|
50
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
beliq-0.1.0/.gitignore
ADDED
beliq-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) beliq
|
|
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.
|
beliq-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: beliq
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official beliq SDK: generate, validate, parse, and convert EU-compliant e-invoices (XRechnung, ZUGFeRD, Factur-X, Peppol BIS) against authority-pinned, drift-checked rules.
|
|
5
|
+
Project-URL: Homepage, https://beliq.eu
|
|
6
|
+
Project-URL: Repository, https://github.com/beliq-eu/beliq-sdk-python
|
|
7
|
+
Project-URL: Issues, https://github.com/beliq-eu/beliq-sdk-python/issues
|
|
8
|
+
Author-email: beliq <hello@beliq.eu>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: beliq,compliance,e-invoice,einvoice,en16931,factur-x,peppol,xrechnung,zugferd
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Office/Business :: Financial :: Accounting
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: httpx>=0.27
|
|
17
|
+
Requires-Dist: pydantic>=2
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
21
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
22
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# beliq
|
|
26
|
+
|
|
27
|
+
Official Python SDK for the [beliq](https://beliq.eu) e-invoicing compliance API. Generate, validate, parse, and convert EN 16931 invoices (XRechnung, ZUGFeRD, Factur-X, Peppol BIS) against authority-pinned, nightly-drift-checked rules.
|
|
28
|
+
|
|
29
|
+
beliq produces and checks the compliant document. Transmission (Peppol, PDP, KSeF, SDI), archiving, and tax-authority reporting stay with your access point.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install beliq
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Requires Python >= 3.10.
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from beliq import Beliq
|
|
43
|
+
|
|
44
|
+
beliq = Beliq(api_key="blq_...")
|
|
45
|
+
|
|
46
|
+
# Account, plan, and quota context (no quota cost).
|
|
47
|
+
account = beliq.me()
|
|
48
|
+
|
|
49
|
+
# Generate an XRechnung document from an EN 16931 invoice.
|
|
50
|
+
generated = beliq.generate(
|
|
51
|
+
standard="xrechnung",
|
|
52
|
+
verify=True,
|
|
53
|
+
invoice={
|
|
54
|
+
"number": "INV-2026-001",
|
|
55
|
+
"issueDate": "2026-01-15",
|
|
56
|
+
"currencyCode": "EUR",
|
|
57
|
+
"seller": {"name": "Seller GmbH", "address": {"city": "Berlin", "postalCode": "10115", "countryCode": "DE"}},
|
|
58
|
+
"buyer": {"name": "Buyer GmbH", "address": {"city": "Munich", "postalCode": "80331", "countryCode": "DE"}},
|
|
59
|
+
"lines": [
|
|
60
|
+
{"description": "Consulting", "quantity": 10, "unitCode": "HUR", "unitPrice": 100, "lineTotal": 1000, "vatRate": 19, "vatCategoryCode": "S"}
|
|
61
|
+
],
|
|
62
|
+
"totalNetAmount": 1000,
|
|
63
|
+
"totalTaxAmount": 190,
|
|
64
|
+
"totalGrossAmount": 1190,
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
print(generated.xml, generated.meta.schematron_version)
|
|
68
|
+
|
|
69
|
+
# Validate any document against authority-pinned rules.
|
|
70
|
+
result = beliq.validate(generated.xml, format="auto")
|
|
71
|
+
if not result.valid:
|
|
72
|
+
for issue in result.errors:
|
|
73
|
+
print(issue.rule_id, issue.message)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Authentication
|
|
77
|
+
|
|
78
|
+
Create an API key in the beliq dashboard under API Keys:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
Beliq(api_key="blq_...") # sends X-API-Key (default)
|
|
82
|
+
Beliq(api_key="blq_...", auth="bearer") # sends Authorization: Bearer
|
|
83
|
+
Beliq(api_key="blq_...", base_url="https://staging.beliq.eu")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Async
|
|
87
|
+
|
|
88
|
+
`AsyncBeliq` mirrors the sync client with `await`:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
import asyncio
|
|
92
|
+
from beliq import AsyncBeliq
|
|
93
|
+
|
|
94
|
+
async def main():
|
|
95
|
+
async with AsyncBeliq(api_key="blq_...") as beliq:
|
|
96
|
+
result = await beliq.validate(open("invoice.xml", "rb").read(), format="auto")
|
|
97
|
+
print(result.valid)
|
|
98
|
+
|
|
99
|
+
asyncio.run(main())
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## API
|
|
103
|
+
|
|
104
|
+
| Method | Endpoint | Input | Returns |
|
|
105
|
+
|---|---|---|---|
|
|
106
|
+
| `me()` | GET /v1/me | none | `AccountInfo` (no quota cost) |
|
|
107
|
+
| `generate(...)` | POST /v1/generate | EN 16931 invoice dict | `GenerateResult` |
|
|
108
|
+
| `validate(document, ...)` | POST /v1/validate | XML or PDF | `ValidationResult` |
|
|
109
|
+
| `parse(document, ...)` | POST /v1/parse | XML or PDF | `ParseResult` |
|
|
110
|
+
| `convert(document, ...)` | POST /v1/convert | XML or PDF | `ConvertResult` |
|
|
111
|
+
|
|
112
|
+
`document` accepts a `str`, `bytes`, or `bytearray`. The content type is sniffed from the bytes (PDF vs XML) unless you pass `content_type=`. `generate` and `convert` return the raw document `content` (bytes) plus the response-header metadata (`schematron_version`, `pdf_kind`, `source_format`/`target_format`, `lost_elements`, `conversion_tools`); for an XML output, `generate` also decodes `xml`.
|
|
113
|
+
|
|
114
|
+
JSON responses are Pydantic models. Any field not explicitly typed (such as the per-country authority versions on a validation result) is preserved and accessible. Errors raise `BeliqApiError` with a typed `.code`, HTTP `.status`, and any `.details`:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from beliq import BeliqApiError
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
beliq.validate("not xml")
|
|
121
|
+
except BeliqApiError as err:
|
|
122
|
+
print(err.code, err.status, err.message)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Development
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
python -m venv .venv && . .venv/bin/activate
|
|
129
|
+
pip install -e ".[dev]"
|
|
130
|
+
ruff check src tests
|
|
131
|
+
mypy
|
|
132
|
+
pytest # unit tests (no network)
|
|
133
|
+
BELIQ_API_KEY=blq_xxx pytest tests/test_integration.py # hits the live API; draws quota
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`tests/test_spec_contract.py` reads the vendored `openapi.json` and fails if the error-code set or the core validate fields drift from the spec. Refresh the vendored spec with `python scripts/sync_spec.py`.
|
|
137
|
+
|
|
138
|
+
## Publishing
|
|
139
|
+
|
|
140
|
+
Released to PyPI as [`beliq`](https://pypi.org/project/beliq/). Releases run from `.github/workflows/release.yml` via PyPI Trusted Publishing (OIDC, with attestations): push a `v*.*.*` tag to publish. No token is stored in the repo.
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT
|
beliq-0.1.0/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# beliq
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the [beliq](https://beliq.eu) e-invoicing compliance API. Generate, validate, parse, and convert EN 16931 invoices (XRechnung, ZUGFeRD, Factur-X, Peppol BIS) against authority-pinned, nightly-drift-checked rules.
|
|
4
|
+
|
|
5
|
+
beliq produces and checks the compliant document. Transmission (Peppol, PDP, KSeF, SDI), archiving, and tax-authority reporting stay with your access point.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install beliq
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires Python >= 3.10.
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from beliq import Beliq
|
|
19
|
+
|
|
20
|
+
beliq = Beliq(api_key="blq_...")
|
|
21
|
+
|
|
22
|
+
# Account, plan, and quota context (no quota cost).
|
|
23
|
+
account = beliq.me()
|
|
24
|
+
|
|
25
|
+
# Generate an XRechnung document from an EN 16931 invoice.
|
|
26
|
+
generated = beliq.generate(
|
|
27
|
+
standard="xrechnung",
|
|
28
|
+
verify=True,
|
|
29
|
+
invoice={
|
|
30
|
+
"number": "INV-2026-001",
|
|
31
|
+
"issueDate": "2026-01-15",
|
|
32
|
+
"currencyCode": "EUR",
|
|
33
|
+
"seller": {"name": "Seller GmbH", "address": {"city": "Berlin", "postalCode": "10115", "countryCode": "DE"}},
|
|
34
|
+
"buyer": {"name": "Buyer GmbH", "address": {"city": "Munich", "postalCode": "80331", "countryCode": "DE"}},
|
|
35
|
+
"lines": [
|
|
36
|
+
{"description": "Consulting", "quantity": 10, "unitCode": "HUR", "unitPrice": 100, "lineTotal": 1000, "vatRate": 19, "vatCategoryCode": "S"}
|
|
37
|
+
],
|
|
38
|
+
"totalNetAmount": 1000,
|
|
39
|
+
"totalTaxAmount": 190,
|
|
40
|
+
"totalGrossAmount": 1190,
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
print(generated.xml, generated.meta.schematron_version)
|
|
44
|
+
|
|
45
|
+
# Validate any document against authority-pinned rules.
|
|
46
|
+
result = beliq.validate(generated.xml, format="auto")
|
|
47
|
+
if not result.valid:
|
|
48
|
+
for issue in result.errors:
|
|
49
|
+
print(issue.rule_id, issue.message)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Authentication
|
|
53
|
+
|
|
54
|
+
Create an API key in the beliq dashboard under API Keys:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
Beliq(api_key="blq_...") # sends X-API-Key (default)
|
|
58
|
+
Beliq(api_key="blq_...", auth="bearer") # sends Authorization: Bearer
|
|
59
|
+
Beliq(api_key="blq_...", base_url="https://staging.beliq.eu")
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Async
|
|
63
|
+
|
|
64
|
+
`AsyncBeliq` mirrors the sync client with `await`:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import asyncio
|
|
68
|
+
from beliq import AsyncBeliq
|
|
69
|
+
|
|
70
|
+
async def main():
|
|
71
|
+
async with AsyncBeliq(api_key="blq_...") as beliq:
|
|
72
|
+
result = await beliq.validate(open("invoice.xml", "rb").read(), format="auto")
|
|
73
|
+
print(result.valid)
|
|
74
|
+
|
|
75
|
+
asyncio.run(main())
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## API
|
|
79
|
+
|
|
80
|
+
| Method | Endpoint | Input | Returns |
|
|
81
|
+
|---|---|---|---|
|
|
82
|
+
| `me()` | GET /v1/me | none | `AccountInfo` (no quota cost) |
|
|
83
|
+
| `generate(...)` | POST /v1/generate | EN 16931 invoice dict | `GenerateResult` |
|
|
84
|
+
| `validate(document, ...)` | POST /v1/validate | XML or PDF | `ValidationResult` |
|
|
85
|
+
| `parse(document, ...)` | POST /v1/parse | XML or PDF | `ParseResult` |
|
|
86
|
+
| `convert(document, ...)` | POST /v1/convert | XML or PDF | `ConvertResult` |
|
|
87
|
+
|
|
88
|
+
`document` accepts a `str`, `bytes`, or `bytearray`. The content type is sniffed from the bytes (PDF vs XML) unless you pass `content_type=`. `generate` and `convert` return the raw document `content` (bytes) plus the response-header metadata (`schematron_version`, `pdf_kind`, `source_format`/`target_format`, `lost_elements`, `conversion_tools`); for an XML output, `generate` also decodes `xml`.
|
|
89
|
+
|
|
90
|
+
JSON responses are Pydantic models. Any field not explicitly typed (such as the per-country authority versions on a validation result) is preserved and accessible. Errors raise `BeliqApiError` with a typed `.code`, HTTP `.status`, and any `.details`:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from beliq import BeliqApiError
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
beliq.validate("not xml")
|
|
97
|
+
except BeliqApiError as err:
|
|
98
|
+
print(err.code, err.status, err.message)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Development
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
python -m venv .venv && . .venv/bin/activate
|
|
105
|
+
pip install -e ".[dev]"
|
|
106
|
+
ruff check src tests
|
|
107
|
+
mypy
|
|
108
|
+
pytest # unit tests (no network)
|
|
109
|
+
BELIQ_API_KEY=blq_xxx pytest tests/test_integration.py # hits the live API; draws quota
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`tests/test_spec_contract.py` reads the vendored `openapi.json` and fails if the error-code set or the core validate fields drift from the spec. Refresh the vendored spec with `python scripts/sync_spec.py`.
|
|
113
|
+
|
|
114
|
+
## Publishing
|
|
115
|
+
|
|
116
|
+
Released to PyPI as [`beliq`](https://pypi.org/project/beliq/). Releases run from `.github/workflows/release.yml` via PyPI Trusted Publishing (OIDC, with attestations): push a `v*.*.*` tag to publish. No token is stored in the repo.
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|