PyCheval 0.1.0__tar.gz → 0.2.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.
- pycheval-0.2.0/PKG-INFO +123 -0
- pycheval-0.2.0/README.md +105 -0
- pycheval-0.2.0/pyproject.toml +77 -0
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/__init__.py +6 -1
- pycheval-0.2.0/src/pycheval/_test_data.py +176 -0
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/const.py +9 -0
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/exc.py +4 -0
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/format.py +2 -2
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/generate.py +10 -157
- pycheval-0.2.0/src/pycheval/locale/de/LC_MESSAGES/pycheval.mo +0 -0
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/locale/de/LC_MESSAGES/pycheval.po +164 -104
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/model.py +51 -10
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/money.py +21 -13
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/parse.py +11 -10
- pycheval-0.2.0/src/pycheval/pdf_common.py +23 -0
- pycheval-0.2.0/src/pycheval/pdf_embed.py +233 -0
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/pdf_extract.py +1 -22
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/pdf_parse.py +2 -1
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/types.py +4 -1
- pycheval-0.1.0/PKG-INFO +0 -93
- pycheval-0.1.0/README.md +0 -70
- pycheval-0.1.0/pyproject.toml +0 -54
- {pycheval-0.1.0 → pycheval-0.2.0}/LICENSE +0 -0
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/_locale.py +0 -0
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/countries.py +0 -0
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/py.typed +0 -0
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/quantities.py +0 -0
- {pycheval-0.1.0 → pycheval-0.2.0}/src/pycheval/type_codes.py +0 -0
pycheval-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: PyCheval
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Factur-X/ZUGFeRD parsing and generation library for Python
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Keywords: factur-x,zugferd,invoice,billing
|
|
7
|
+
Author: Sebastian Rittau
|
|
8
|
+
Author-email: sebastian.rittau@zfutura.de
|
|
9
|
+
Requires-Python: >= 3.11
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Topic :: File Formats
|
|
13
|
+
Classifier: Topic :: Office/Business :: Financial :: Accounting
|
|
14
|
+
Requires-Dist: pypdf (>=5.4.0,<6)
|
|
15
|
+
Project-URL: GitHub, https://github.com/zfutura/pycheval
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# PyCheval – Factur-X/ZUGFeRD parsing and generation library for Python
|
|
19
|
+
|
|
20
|
+
[](https://github.com/zfutura/pycheval/releases/)
|
|
21
|
+

|
|
22
|
+
[](https://github.com/zfutura/pycheval/blob/main/LICENSE)
|
|
23
|
+
[](https://github.com/zfutura/pycheval/actions/workflows/test-and-lint)
|
|
24
|
+
|
|
25
|
+
Factur-X (also called ZUGFeRD in Germany) is a Franco-German standard for
|
|
26
|
+
electronic invoices. Structured XML data is embedded in PDF-A/3 files,
|
|
27
|
+
allowing invoices to be processed automatically while still being displayed in
|
|
28
|
+
standard PDF readers. Factur-X supports EN 16931, the European standard for
|
|
29
|
+
electronic invoicing.
|
|
30
|
+
|
|
31
|
+
See the [Factur-X website (French)](https://www.factur-x.org/) or
|
|
32
|
+
[FeRD website (German)](https://www.ferd-net.de/) for more information.
|
|
33
|
+
|
|
34
|
+
This library supports reading and writing PDF and XML files according to
|
|
35
|
+
Factur-X Version 1.07.3 (aka ZUGFeRD 2.3.3). The following Factur-X profiles
|
|
36
|
+
are currently supported:
|
|
37
|
+
|
|
38
|
+
- Minimum
|
|
39
|
+
- Basic WL
|
|
40
|
+
- Basic
|
|
41
|
+
- EN 16931 (Comfort)
|
|
42
|
+
|
|
43
|
+
Extended and XRechnung profiles are not yet supported.
|
|
44
|
+
|
|
45
|
+
**Warning**: This library is still in early development. The API may change
|
|
46
|
+
frequently, and not all features are implemented yet.
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
### Installation
|
|
51
|
+
|
|
52
|
+
You can install PyCheval from PyPI:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install PyCheval
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Generating Factur-X
|
|
59
|
+
|
|
60
|
+
PyCheval supports several Factur-X profile levels, each with different levels of detail and complexity. First, you need to create an instance of the appropriate profile class. Then, you can pass that instance to one of the generation functions.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from datetime import date
|
|
64
|
+
from pycheval import EN16931Invoice, Money, generate_xml
|
|
65
|
+
|
|
66
|
+
invoice = EN16931Invoice(
|
|
67
|
+
invoice_number="2021-123",
|
|
68
|
+
invoice_date=date(2021, 4, 13),
|
|
69
|
+
grand_total=Money("100.00", "EUR"),
|
|
70
|
+
... # See the class documentation for all required and optional fields.
|
|
71
|
+
)
|
|
72
|
+
xml_string = generate_xml(invoice)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
To embed the generated XML into a PDF, you can use the `embed_invoice_in_pdf` function:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from pathlib import Path
|
|
79
|
+
from pycheval import embed_invoice_in_pdf
|
|
80
|
+
|
|
81
|
+
invoice = ...
|
|
82
|
+
pdf_bytes = embed_invoice_in_pdf("invoice.pdf", invoice)
|
|
83
|
+
Path("invoice_with_facturx.pdf").write_bytes(pdf_bytes)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Parsing Factur-X PDF files
|
|
87
|
+
|
|
88
|
+
PyCheval can parse Factur-X PDF files and extract the embedded invoice data. The parser will return an instance of the appropriate profile class.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from pycheval import parse_pdf
|
|
92
|
+
|
|
93
|
+
invoice = parse_pdf("invoice.pdf") # Returns MinimumInvoice or a subclass
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Printing invoices
|
|
97
|
+
|
|
98
|
+
To display a formatted Factur-X invoice in the terminal, use the `format_invoice_as_text()` function:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from pycheval import format_invoice_as_text
|
|
102
|
+
|
|
103
|
+
invoice = EN16931Invoice(...)
|
|
104
|
+
print(format_invoice_as_text(invoice))
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### License and Warranty
|
|
108
|
+
|
|
109
|
+
**Copyright © ZFutura GmbH**
|
|
110
|
+
|
|
111
|
+
This project is licensed under the [Apache License 2.0](LICENSE).
|
|
112
|
+
|
|
113
|
+
**Disclaimer**: The software is provided "as is", without warranty of any kind.
|
|
114
|
+
The authors are not liable for any damages arising from the use of this
|
|
115
|
+
software. In particular, the authors do not guarantee that invoices generated
|
|
116
|
+
or parsed by this library will be valid or compliant with any standards, nor
|
|
117
|
+
that they are suitable for any specific purpose.
|
|
118
|
+
|
|
119
|
+
**Important**: It is the user's responsibility to ensure that generated
|
|
120
|
+
invoices meet all legal and regulatory requirements for their jurisdiction.
|
|
121
|
+
|
|
122
|
+
See the [LICENSE](LICENSE) file for the complete disclaimer and license terms.
|
|
123
|
+
|
pycheval-0.2.0/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# PyCheval – Factur-X/ZUGFeRD parsing and generation library for Python
|
|
2
|
+
|
|
3
|
+
[](https://github.com/zfutura/pycheval/releases/)
|
|
4
|
+

|
|
5
|
+
[](https://github.com/zfutura/pycheval/blob/main/LICENSE)
|
|
6
|
+
[](https://github.com/zfutura/pycheval/actions/workflows/test-and-lint)
|
|
7
|
+
|
|
8
|
+
Factur-X (also called ZUGFeRD in Germany) is a Franco-German standard for
|
|
9
|
+
electronic invoices. Structured XML data is embedded in PDF-A/3 files,
|
|
10
|
+
allowing invoices to be processed automatically while still being displayed in
|
|
11
|
+
standard PDF readers. Factur-X supports EN 16931, the European standard for
|
|
12
|
+
electronic invoicing.
|
|
13
|
+
|
|
14
|
+
See the [Factur-X website (French)](https://www.factur-x.org/) or
|
|
15
|
+
[FeRD website (German)](https://www.ferd-net.de/) for more information.
|
|
16
|
+
|
|
17
|
+
This library supports reading and writing PDF and XML files according to
|
|
18
|
+
Factur-X Version 1.07.3 (aka ZUGFeRD 2.3.3). The following Factur-X profiles
|
|
19
|
+
are currently supported:
|
|
20
|
+
|
|
21
|
+
- Minimum
|
|
22
|
+
- Basic WL
|
|
23
|
+
- Basic
|
|
24
|
+
- EN 16931 (Comfort)
|
|
25
|
+
|
|
26
|
+
Extended and XRechnung profiles are not yet supported.
|
|
27
|
+
|
|
28
|
+
**Warning**: This library is still in early development. The API may change
|
|
29
|
+
frequently, and not all features are implemented yet.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
### Installation
|
|
34
|
+
|
|
35
|
+
You can install PyCheval from PyPI:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install PyCheval
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Generating Factur-X
|
|
42
|
+
|
|
43
|
+
PyCheval supports several Factur-X profile levels, each with different levels of detail and complexity. First, you need to create an instance of the appropriate profile class. Then, you can pass that instance to one of the generation functions.
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from datetime import date
|
|
47
|
+
from pycheval import EN16931Invoice, Money, generate_xml
|
|
48
|
+
|
|
49
|
+
invoice = EN16931Invoice(
|
|
50
|
+
invoice_number="2021-123",
|
|
51
|
+
invoice_date=date(2021, 4, 13),
|
|
52
|
+
grand_total=Money("100.00", "EUR"),
|
|
53
|
+
... # See the class documentation for all required and optional fields.
|
|
54
|
+
)
|
|
55
|
+
xml_string = generate_xml(invoice)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
To embed the generated XML into a PDF, you can use the `embed_invoice_in_pdf` function:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from pathlib import Path
|
|
62
|
+
from pycheval import embed_invoice_in_pdf
|
|
63
|
+
|
|
64
|
+
invoice = ...
|
|
65
|
+
pdf_bytes = embed_invoice_in_pdf("invoice.pdf", invoice)
|
|
66
|
+
Path("invoice_with_facturx.pdf").write_bytes(pdf_bytes)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Parsing Factur-X PDF files
|
|
70
|
+
|
|
71
|
+
PyCheval can parse Factur-X PDF files and extract the embedded invoice data. The parser will return an instance of the appropriate profile class.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from pycheval import parse_pdf
|
|
75
|
+
|
|
76
|
+
invoice = parse_pdf("invoice.pdf") # Returns MinimumInvoice or a subclass
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Printing invoices
|
|
80
|
+
|
|
81
|
+
To display a formatted Factur-X invoice in the terminal, use the `format_invoice_as_text()` function:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from pycheval import format_invoice_as_text
|
|
85
|
+
|
|
86
|
+
invoice = EN16931Invoice(...)
|
|
87
|
+
print(format_invoice_as_text(invoice))
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### License and Warranty
|
|
91
|
+
|
|
92
|
+
**Copyright © ZFutura GmbH**
|
|
93
|
+
|
|
94
|
+
This project is licensed under the [Apache License 2.0](LICENSE).
|
|
95
|
+
|
|
96
|
+
**Disclaimer**: The software is provided "as is", without warranty of any kind.
|
|
97
|
+
The authors are not liable for any damages arising from the use of this
|
|
98
|
+
software. In particular, the authors do not guarantee that invoices generated
|
|
99
|
+
or parsed by this library will be valid or compliant with any standards, nor
|
|
100
|
+
that they are suitable for any specific purpose.
|
|
101
|
+
|
|
102
|
+
**Important**: It is the user's responsibility to ensure that generated
|
|
103
|
+
invoices meet all legal and regulatory requirements for their jurisdiction.
|
|
104
|
+
|
|
105
|
+
See the [LICENSE](LICENSE) file for the complete disclaimer and license terms.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "PyCheval"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Factur-X/ZUGFeRD parsing and generation library for Python"
|
|
5
|
+
authors = [{ name = "Sebastian Rittau", email = "sebastian.rittau@zfutura.de" }]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = "Apache-2.0"
|
|
8
|
+
keywords = ["factur-x", "zugferd", "invoice", "billing"]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Development Status :: 3 - Alpha",
|
|
11
|
+
"Intended Audience :: Developers",
|
|
12
|
+
"Topic :: File Formats",
|
|
13
|
+
"Topic :: Office/Business :: Financial :: Accounting",
|
|
14
|
+
]
|
|
15
|
+
requires-python = ">= 3.11"
|
|
16
|
+
dependencies = [
|
|
17
|
+
"pypdf >= 5.4.0, < 6",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
GitHub = "https://github.com/zfutura/pycheval"
|
|
22
|
+
|
|
23
|
+
[tool.poetry]
|
|
24
|
+
include = [{ path = "src/pycheval/locale/**/*.mo", format = ["sdist", "wheel"] }]
|
|
25
|
+
exclude = ["src/pycheval/test_*"]
|
|
26
|
+
|
|
27
|
+
[tool.poetry.group.dev.dependencies]
|
|
28
|
+
babel = "~=2.17.0"
|
|
29
|
+
mypy = "~=1.17.0"
|
|
30
|
+
poethepoet = "^0.36.0"
|
|
31
|
+
pytest = "^8.4.1"
|
|
32
|
+
ruff = "^0.12.1"
|
|
33
|
+
|
|
34
|
+
[tool.poe.tasks.lint]
|
|
35
|
+
cmd = "ruff check src"
|
|
36
|
+
help = "Lint the source code with ruff"
|
|
37
|
+
|
|
38
|
+
[tool.poe.tasks.test]
|
|
39
|
+
cmd = "pytest src"
|
|
40
|
+
help = "Run unit and integration tests with pytest"
|
|
41
|
+
|
|
42
|
+
[tool.poe.tasks.typecheck]
|
|
43
|
+
cmd = "mypy src"
|
|
44
|
+
help = "Type check the source code with mypy"
|
|
45
|
+
|
|
46
|
+
[tool.poe.tasks.i18n-extract]
|
|
47
|
+
cmd = "pybabel extract -o messages.pot src"
|
|
48
|
+
help = "Extract messages for translation"
|
|
49
|
+
|
|
50
|
+
[tool.poe.tasks.i18n-init]
|
|
51
|
+
cmd = "pybabel init -D pycheval -i messages.pot -d src/pycheval/locale -l "
|
|
52
|
+
help = "Initialize translation files for a new language"
|
|
53
|
+
|
|
54
|
+
[tool.poe.tasks.i18n-update]
|
|
55
|
+
cmd = "pybabel update -D pycheval -i messages.pot -d src/pycheval/locale"
|
|
56
|
+
help = "Update translation files from the template"
|
|
57
|
+
|
|
58
|
+
[tool.poe.tasks.i18n-compile]
|
|
59
|
+
cmd = "pybabel compile -D pycheval -d src/pycheval/locale"
|
|
60
|
+
help = "Compile translation files"
|
|
61
|
+
|
|
62
|
+
[tool.ruff]
|
|
63
|
+
target-version = "py312"
|
|
64
|
+
line-length = 79
|
|
65
|
+
|
|
66
|
+
[tool.ruff.lint]
|
|
67
|
+
select = ["E", "F", "B", "I"]
|
|
68
|
+
|
|
69
|
+
[tool.ruff.lint.isort]
|
|
70
|
+
combine-as-imports = true
|
|
71
|
+
|
|
72
|
+
[tool.mypy]
|
|
73
|
+
strict = true
|
|
74
|
+
|
|
75
|
+
[build-system]
|
|
76
|
+
requires = ["poetry-core"]
|
|
77
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -3,12 +3,17 @@ from typing import Final
|
|
|
3
3
|
from .exc import * # noqa: F403
|
|
4
4
|
from .format import format_invoice_as_text as format_invoice_as_text
|
|
5
5
|
from .generate import (
|
|
6
|
-
generate as generate,
|
|
7
6
|
generate_et as generate_et,
|
|
7
|
+
generate_xml as generate_xml,
|
|
8
8
|
)
|
|
9
9
|
from .model import * # noqa: F403
|
|
10
10
|
from .money import Money as Money
|
|
11
11
|
from .parse import parse_xml as parse_xml
|
|
12
|
+
from .pdf_common import FileRelationship as FileRelationship
|
|
13
|
+
from .pdf_embed import (
|
|
14
|
+
embed_facturx_file_in_pdf as embed_facturx_file_in_pdf,
|
|
15
|
+
embed_invoice_in_pdf as embed_invoice_in_pdf,
|
|
16
|
+
)
|
|
12
17
|
from .pdf_parse import parse_pdf as parse_pdf
|
|
13
18
|
|
|
14
19
|
FACTURX_VERSION: Final = "1.0.07"
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from typing import Final
|
|
4
|
+
|
|
5
|
+
from .model import (
|
|
6
|
+
EN16931Invoice,
|
|
7
|
+
EN16931LineItem,
|
|
8
|
+
IncludedNote,
|
|
9
|
+
LineAllowance,
|
|
10
|
+
LineCharge,
|
|
11
|
+
LineItem,
|
|
12
|
+
PaymentMeans,
|
|
13
|
+
PaymentTerms,
|
|
14
|
+
PostalAddress,
|
|
15
|
+
ProductCharacteristic,
|
|
16
|
+
ProductClassification,
|
|
17
|
+
ReferenceDocument,
|
|
18
|
+
Tax,
|
|
19
|
+
TradeContact,
|
|
20
|
+
TradeParty,
|
|
21
|
+
)
|
|
22
|
+
from .money import Money
|
|
23
|
+
from .quantities import QuantityCode
|
|
24
|
+
from .type_codes import (
|
|
25
|
+
AllowanceChargeCode,
|
|
26
|
+
DocumentTypeCode,
|
|
27
|
+
ItemTypeCode,
|
|
28
|
+
PaymentMeansCode,
|
|
29
|
+
ReferenceQualifierCode,
|
|
30
|
+
TaxCategoryCode,
|
|
31
|
+
TextSubjectCode,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
TEST_PNG = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\n\x00\x00\x00\n\x08\x02\x00\x00\x00\x02PX\xea\x00\x00\x00&IDAT\x18\xd3c\xfc\xff\xff?\x03n\xc0\xc4\xc0\xc0\xc0\xc0\xc8\x88E\x86\x91\x91\x81\x81\x81\x91\x08\xdd\x83X\x9al\x8f\x01\x00\xdf\x94\t\x11%\xfb\xb4\xe3\x00\x00\x00\x00IEND\xaeB`\x82" # noqa: E501
|
|
35
|
+
|
|
36
|
+
TEST_SELLER: Final = TradeParty(
|
|
37
|
+
"Seller GmbH",
|
|
38
|
+
PostalAddress(
|
|
39
|
+
"DE", None, "44331", "Test City", "Test Street 42", None, None
|
|
40
|
+
),
|
|
41
|
+
"seller@example.com",
|
|
42
|
+
vat_id="DE123456789",
|
|
43
|
+
tax_number="123/456/789",
|
|
44
|
+
ids=["SELLER-123"],
|
|
45
|
+
global_ids=[("123456789", "0088")],
|
|
46
|
+
description="being formed",
|
|
47
|
+
legal_id=("DE123456789", "0088"),
|
|
48
|
+
trading_business_name="Great Business",
|
|
49
|
+
contact=TradeContact(
|
|
50
|
+
"Max Mustermann",
|
|
51
|
+
"Sales",
|
|
52
|
+
phone="+49 123 4567890",
|
|
53
|
+
email="foo@example.com",
|
|
54
|
+
),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
TEST_BUYER: Final = TradeParty(
|
|
58
|
+
"Buyer AG",
|
|
59
|
+
PostalAddress("DE"),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
TEST_EN16931_INVOICE: Final = EN16931Invoice(
|
|
63
|
+
"INV-12345",
|
|
64
|
+
DocumentTypeCode.INVOICE,
|
|
65
|
+
datetime.date(2024, 8, 20),
|
|
66
|
+
TEST_SELLER,
|
|
67
|
+
TEST_BUYER,
|
|
68
|
+
"EUR",
|
|
69
|
+
tax_basis_total_amount=Money("10090.00", "EUR"),
|
|
70
|
+
tax_total_amounts=[Money("1917.10", "EUR")],
|
|
71
|
+
grand_total_amount=Money("12007.10", "EUR"),
|
|
72
|
+
due_payable_amount=Money("12007.10", "EUR"),
|
|
73
|
+
line_total_amount=Money("10090.00", "EUR"),
|
|
74
|
+
tax=[
|
|
75
|
+
Tax(
|
|
76
|
+
Money("1917.10", "EUR"),
|
|
77
|
+
Money("10090.00", "EUR"),
|
|
78
|
+
Decimal(19),
|
|
79
|
+
TaxCategoryCode.STANDARD_RATE,
|
|
80
|
+
),
|
|
81
|
+
],
|
|
82
|
+
delivery_date=datetime.date(2024, 8, 21),
|
|
83
|
+
notes=[
|
|
84
|
+
IncludedNote("This is a test invoice."),
|
|
85
|
+
IncludedNote(
|
|
86
|
+
"This is seller note.", TextSubjectCode.COMMENTS_BY_SELLER
|
|
87
|
+
),
|
|
88
|
+
],
|
|
89
|
+
line_items=[
|
|
90
|
+
LineItem(
|
|
91
|
+
"1",
|
|
92
|
+
"Fixed amount item\nWith multiple lines",
|
|
93
|
+
Money("10000.00", "EUR"),
|
|
94
|
+
(Decimal(1), QuantityCode.PIECE),
|
|
95
|
+
Money("10000.00", "EUR"),
|
|
96
|
+
Decimal("19"),
|
|
97
|
+
),
|
|
98
|
+
EN16931LineItem(
|
|
99
|
+
"2",
|
|
100
|
+
"Hourly item",
|
|
101
|
+
Money("30.00", "EUR"),
|
|
102
|
+
(Decimal(3), QuantityCode.HOUR),
|
|
103
|
+
Money("90.00", "EUR"),
|
|
104
|
+
Decimal("19"),
|
|
105
|
+
global_id=("9781529044195", "0160"),
|
|
106
|
+
basis_quantity=(Decimal(1), QuantityCode.HOUR),
|
|
107
|
+
description="This is a line item description.",
|
|
108
|
+
note=IncludedNote("This is a line item note."),
|
|
109
|
+
seller_assigned_id="ISBN-44",
|
|
110
|
+
buyer_assigned_id="TT-123",
|
|
111
|
+
product_characteristics=[
|
|
112
|
+
ProductCharacteristic("color", "red"),
|
|
113
|
+
],
|
|
114
|
+
product_classifications=[
|
|
115
|
+
ProductClassification("1234-5679", list_id=ItemTypeCode.ISSN),
|
|
116
|
+
ProductClassification(
|
|
117
|
+
"9781529044195",
|
|
118
|
+
list_id=ItemTypeCode.ISBN,
|
|
119
|
+
list_version_id="99",
|
|
120
|
+
),
|
|
121
|
+
],
|
|
122
|
+
origin_country="DE",
|
|
123
|
+
buyer_order_line_id="BUY-DOC",
|
|
124
|
+
gross_unit_price=(
|
|
125
|
+
Money("40.00", "EUR"),
|
|
126
|
+
(Decimal(1), QuantityCode.HOUR),
|
|
127
|
+
),
|
|
128
|
+
gross_allowance_or_charge=LineAllowance(
|
|
129
|
+
Money("10.00", "EUR"),
|
|
130
|
+
reason_code=AllowanceChargeCode.AHEAD_OF_SCHEDULE,
|
|
131
|
+
),
|
|
132
|
+
charges=[
|
|
133
|
+
LineCharge(
|
|
134
|
+
Money("0.05", "EUR"), reason="Complexity surcharge"
|
|
135
|
+
),
|
|
136
|
+
],
|
|
137
|
+
allowances=[
|
|
138
|
+
LineAllowance(
|
|
139
|
+
Money("1.00", "EUR"),
|
|
140
|
+
reason_code=AllowanceChargeCode.AHEAD_OF_SCHEDULE,
|
|
141
|
+
reason="Ahead of schedule",
|
|
142
|
+
basis_amount=Money("20.00", "EUR"),
|
|
143
|
+
percent=Decimal("5"),
|
|
144
|
+
),
|
|
145
|
+
],
|
|
146
|
+
billing_period=(
|
|
147
|
+
datetime.date(2024, 8, 1),
|
|
148
|
+
datetime.date(2024, 8, 31),
|
|
149
|
+
),
|
|
150
|
+
doc_ref=("REFDOC-1", None),
|
|
151
|
+
),
|
|
152
|
+
],
|
|
153
|
+
buyer_reference="BUYER-1234",
|
|
154
|
+
seller_order_id="SELL-DOC",
|
|
155
|
+
buyer_order_id="BUY-DOC",
|
|
156
|
+
contract_id="CONTRACT-123",
|
|
157
|
+
referenced_docs=[
|
|
158
|
+
ReferenceDocument("REFDOC-1", DocumentTypeCode.INVOICING_DATA_SHEET),
|
|
159
|
+
ReferenceDocument(
|
|
160
|
+
"REFDOC-2",
|
|
161
|
+
DocumentTypeCode.RELATED_DOCUMENT,
|
|
162
|
+
"Test ref doc",
|
|
163
|
+
"https://example.com/refdoc.txt",
|
|
164
|
+
attachment=(TEST_PNG, "image/png", "refdoc.png"),
|
|
165
|
+
reference_type_code=ReferenceQualifierCode.PRICE_LIST_VERSION,
|
|
166
|
+
),
|
|
167
|
+
],
|
|
168
|
+
procuring_project=("PROJ-123", "Project X"),
|
|
169
|
+
sepa_reference="ABC-dddd",
|
|
170
|
+
payment_means=[
|
|
171
|
+
PaymentMeans(PaymentMeansCode.BANK_PAYMENT),
|
|
172
|
+
],
|
|
173
|
+
payment_terms=PaymentTerms(
|
|
174
|
+
due_date=datetime.date(2024, 9, 3),
|
|
175
|
+
),
|
|
176
|
+
)
|
|
@@ -16,3 +16,12 @@ URN_XRECHNUNG_PROFILE = "urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:sta
|
|
|
16
16
|
NS_CII = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
|
|
17
17
|
NS_RAM = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" # noqa: E501
|
|
18
18
|
NS_UDT = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
|
|
19
|
+
|
|
20
|
+
ALLOWED_ATTACHMENT_MIME_TYPES = (
|
|
21
|
+
"application/pdf",
|
|
22
|
+
"image/png",
|
|
23
|
+
"image/jpeg",
|
|
24
|
+
"text/csv",
|
|
25
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
26
|
+
"application/vnd.oasis.opendocument.spreadsheet",
|
|
27
|
+
)
|
|
@@ -6,6 +6,10 @@ class PDFError(FacturXError):
|
|
|
6
6
|
"""Base class for PDF processing exceptions."""
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
class InsufficientPDFError(PDFError):
|
|
10
|
+
"""Raised when a PDF file is insufficient for Factur-X."""
|
|
11
|
+
|
|
12
|
+
|
|
9
13
|
class PDFParseError(FacturXError):
|
|
10
14
|
"""Raise when a PDF file cannot be processed."""
|
|
11
15
|
|
|
@@ -473,8 +473,8 @@ def format_trade_party(trade_party: TradeParty) -> str:
|
|
|
473
473
|
lines.append(_format_global_id(gid))
|
|
474
474
|
if trade_party.legal_id:
|
|
475
475
|
lines.append(_format_global_id(trade_party.legal_id))
|
|
476
|
-
|
|
477
|
-
lines.append(format_trade_contact(contact))
|
|
476
|
+
if trade_party.contact is not None:
|
|
477
|
+
lines.append(format_trade_contact(trade_party.contact))
|
|
478
478
|
|
|
479
479
|
return "\n".join(lines)
|
|
480
480
|
|