Skip to content

Commit 536ca73

Browse files
Make README example pass validation with EN16931 (cog alternative). (#103)
* Make README example pass validation with EN16931. * Avoid copy/pasting the README example, it's too easily out of sync. * Apply fixes from #102. * Alternative implementation using cog. * Run black --------- Co-authored-by: Raphael Michel <michel@rami.io>
1 parent b2f60b7 commit 536ca73

File tree

4 files changed

+135
-100
lines changed

4 files changed

+135
-100
lines changed

README.rst

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -49,85 +49,105 @@ Parsing::
4949

5050
``Document.parse()`` taskes a boolean parameter ``strict`` which defaults to ``True``. This means that the parser will raise an error if it encounters any unknown element. If you set it to ``False``, the parser will not raise an error and parse whatever it can.
5151

52+
.. [[[cog
53+
# Re-run this with `cog -r README.rst`
54+
55+
from pathlib import Path
56+
from textwrap import indent
57+
58+
import cog
59+
60+
cog.outl("Generating::\n")
61+
cog.outl(indent(Path("example.py").read_text(encoding="UTF-8"), " "), dedent=False)
62+
.. ]]]
5263
Generating::
5364

54-
from datetime import date, datetime, timezone
65+
from datetime import date, datetime, timedelta, timezone
5566
from decimal import Decimal
5667

5768
from drafthorse.models.accounting import ApplicableTradeTax
5869
from drafthorse.models.document import Document
5970
from drafthorse.models.note import IncludedNote
6071
from drafthorse.models.party import TaxRegistration
72+
from drafthorse.models.payment import PaymentMeans, PaymentTerms
73+
from drafthorse.models.trade import AdvancePayment, IncludedTradeTax
6174
from drafthorse.models.tradelines import LineItem
6275
from drafthorse.pdf import attach_xml
6376

6477
# Build data structure
6578
doc = Document()
66-
doc.context.guideline_parameter.id = "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended"
79+
doc.context.guideline_parameter.id = "urn:cen.eu:en16931:2017"
6780
doc.header.id = "RE1337"
6881
doc.header.type_code = "380"
69-
doc.header.name = "RECHNUNG"
7082
doc.header.issue_date_time = date.today()
71-
doc.header.language = "ger"
7283

7384
doc.header.notes.add(IncludedNote(content="Test Note 1"))
7485

75-
doc.trade.agreement.seller.name = "Lieferant GmbH"
76-
doc.trade.settlement.payee.name = "Lieferant GmbH"
77-
7886
doc.trade.agreement.buyer.name = "Kunde GmbH"
79-
doc.trade.settlement.invoicee.name = "Kunde GmbH"
87+
doc.trade.agreement.buyer.address.country_id = "DE"
8088

8189
doc.trade.settlement.currency_code = "EUR"
82-
doc.trade.settlement.payment_means.type_code = "ZZZ"
90+
doc.trade.settlement.payment_means.add(PaymentMeans(type_code="ZZZ"))
8391

92+
doc.trade.agreement.seller.name = "Lieferant GmbH"
8493
doc.trade.agreement.seller.address.country_id = "DE"
8594
doc.trade.agreement.seller.address.country_subdivision = "Bayern"
8695
doc.trade.agreement.seller.tax_registrations.add(
87-
TaxRegistration(
88-
id=("VA", "DE000000000")
89-
)
96+
TaxRegistration(id=("VA", "DE000000000"))
9097
)
9198

92-
doc.trade.agreement.seller_order.issue_date_time = datetime.now(timezone.utc)
93-
doc.trade.agreement.buyer_order.issue_date_time = datetime.now(timezone.utc)
94-
doc.trade.settlement.advance_payment.received_date = datetime.now(timezone.utc)
95-
doc.trade.agreement.customer_order.issue_date_time = datetime.now(timezone.utc)
99+
advance = AdvancePayment(
100+
received_date=datetime.now(timezone.utc), paid_amount=Decimal(42)
101+
)
102+
advance.included_trade_tax.add(
103+
IncludedTradeTax(
104+
calculated_amount=Decimal(0),
105+
type_code="VAT",
106+
category_code="E",
107+
rate_applicable_percent=Decimal(0),
108+
)
109+
)
110+
doc.trade.settlement.advance_payment.add(advance)
96111

97112
li = LineItem()
98113
li.document.line_id = "1"
99114
li.product.name = "Rainbow"
100-
li.agreement.gross.amount = Decimal("999.00")
101-
li.agreement.gross.basis_quantity = (Decimal("1.0000"), "H87") # H87 == pieces
102-
li.agreement.net.amount = Decimal("999.00")
103-
li.agreement.net.basis_quantity = (Decimal("999.00"), "EUR")
104-
li.delivery.billed_quantity = (Decimal("1.0000"), "H87") # H87 == pieces
115+
li.agreement.gross.amount = Decimal("1198.8")
116+
li.agreement.gross.basis_quantity = (Decimal("1.0000"), "C62") # C62 == unit
117+
li.agreement.net.amount = Decimal("999")
118+
li.agreement.net.basis_quantity = (Decimal("1.0000"), "C62") # C62 == unit
119+
li.delivery.billed_quantity = (Decimal("1.0000"), "C62") # C62 == unit
105120
li.settlement.trade_tax.type_code = "VAT"
106-
li.settlement.trade_tax.category_code = "E"
107-
li.settlement.trade_tax.rate_applicable_percent = Decimal("0.00")
121+
li.settlement.trade_tax.category_code = "S"
122+
li.settlement.trade_tax.rate_applicable_percent = Decimal("20.00")
108123
li.settlement.monetary_summation.total_amount = Decimal("999.00")
109124
doc.trade.items.add(li)
110125

111126
trade_tax = ApplicableTradeTax()
112-
trade_tax.calculated_amount = Decimal("0.00")
127+
trade_tax.calculated_amount = Decimal("199.80")
113128
trade_tax.basis_amount = Decimal("999.00")
114129
trade_tax.type_code = "VAT"
115-
trade_tax.category_code = "AE"
116-
trade_tax.exemption_reason_code = 'VATEX-EU-AE'
117-
trade_tax.rate_applicable_percent = Decimal("0.00")
130+
trade_tax.category_code = "S"
131+
trade_tax.rate_applicable_percent = Decimal("20.00")
118132
doc.trade.settlement.trade_tax.add(trade_tax)
119133

120134
doc.trade.settlement.monetary_summation.line_total = Decimal("999.00")
121135
doc.trade.settlement.monetary_summation.charge_total = Decimal("0.00")
122136
doc.trade.settlement.monetary_summation.allowance_total = Decimal("0.00")
123137
doc.trade.settlement.monetary_summation.tax_basis_total = Decimal("999.00")
124-
doc.trade.settlement.monetary_summation.tax_total = Decimal("0.00")
125-
doc.trade.settlement.monetary_summation.grand_total = Decimal("999.00")
126-
doc.trade.settlement.monetary_summation.due_amount = Decimal("999.00")
138+
doc.trade.settlement.monetary_summation.tax_total = (Decimal("199.80"), "EUR")
139+
doc.trade.settlement.monetary_summation.grand_total = Decimal("1198.8")
140+
doc.trade.settlement.monetary_summation.due_amount = Decimal("1198.8")
141+
142+
terms = PaymentTerms()
143+
terms.due = datetime.now(timezone.utc) + timedelta(days=30)
144+
doc.trade.settlement.terms.add(terms)
127145

128146
# Generate XML file
129147
xml = doc.serialize(schema="FACTUR-X_EXTENDED")
130148

149+
.. [[[end]]]
150+
131151
# Attach XML to an existing PDF.
132152
# Note that the existing PDF should be compliant to PDF/A-3!
133153
# You can validate this here: https://www.pdf-online.com/osa/validate.aspx

example.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
from datetime import date, datetime, timedelta, timezone
2+
from decimal import Decimal
3+
4+
from drafthorse.models.accounting import ApplicableTradeTax
5+
from drafthorse.models.document import Document
6+
from drafthorse.models.note import IncludedNote
7+
from drafthorse.models.party import TaxRegistration
8+
from drafthorse.models.payment import PaymentMeans, PaymentTerms
9+
from drafthorse.models.trade import AdvancePayment, IncludedTradeTax
10+
from drafthorse.models.tradelines import LineItem
11+
from drafthorse.pdf import attach_xml
12+
13+
# Build data structure
14+
doc = Document()
15+
doc.context.guideline_parameter.id = "urn:cen.eu:en16931:2017"
16+
doc.header.id = "RE1337"
17+
doc.header.type_code = "380"
18+
doc.header.issue_date_time = date.today()
19+
20+
doc.header.notes.add(IncludedNote(content="Test Note 1"))
21+
22+
doc.trade.agreement.buyer.name = "Kunde GmbH"
23+
doc.trade.agreement.buyer.address.country_id = "DE"
24+
25+
doc.trade.settlement.currency_code = "EUR"
26+
doc.trade.settlement.payment_means.add(PaymentMeans(type_code="ZZZ"))
27+
28+
doc.trade.agreement.seller.name = "Lieferant GmbH"
29+
doc.trade.agreement.seller.address.country_id = "DE"
30+
doc.trade.agreement.seller.address.country_subdivision = "Bayern"
31+
doc.trade.agreement.seller.tax_registrations.add(
32+
TaxRegistration(id=("VA", "DE000000000"))
33+
)
34+
35+
advance = AdvancePayment(
36+
received_date=datetime.now(timezone.utc), paid_amount=Decimal(42)
37+
)
38+
advance.included_trade_tax.add(
39+
IncludedTradeTax(
40+
calculated_amount=Decimal(0),
41+
type_code="VAT",
42+
category_code="E",
43+
rate_applicable_percent=Decimal(0),
44+
)
45+
)
46+
doc.trade.settlement.advance_payment.add(advance)
47+
48+
li = LineItem()
49+
li.document.line_id = "1"
50+
li.product.name = "Rainbow"
51+
li.agreement.gross.amount = Decimal("1198.8")
52+
li.agreement.gross.basis_quantity = (Decimal("1.0000"), "C62") # C62 == unit
53+
li.agreement.net.amount = Decimal("999")
54+
li.agreement.net.basis_quantity = (Decimal("1.0000"), "C62") # C62 == unit
55+
li.delivery.billed_quantity = (Decimal("1.0000"), "C62") # C62 == unit
56+
li.settlement.trade_tax.type_code = "VAT"
57+
li.settlement.trade_tax.category_code = "S"
58+
li.settlement.trade_tax.rate_applicable_percent = Decimal("20.00")
59+
li.settlement.monetary_summation.total_amount = Decimal("999.00")
60+
doc.trade.items.add(li)
61+
62+
trade_tax = ApplicableTradeTax()
63+
trade_tax.calculated_amount = Decimal("199.80")
64+
trade_tax.basis_amount = Decimal("999.00")
65+
trade_tax.type_code = "VAT"
66+
trade_tax.category_code = "S"
67+
trade_tax.rate_applicable_percent = Decimal("20.00")
68+
doc.trade.settlement.trade_tax.add(trade_tax)
69+
70+
doc.trade.settlement.monetary_summation.line_total = Decimal("999.00")
71+
doc.trade.settlement.monetary_summation.charge_total = Decimal("0.00")
72+
doc.trade.settlement.monetary_summation.allowance_total = Decimal("0.00")
73+
doc.trade.settlement.monetary_summation.tax_basis_total = Decimal("999.00")
74+
doc.trade.settlement.monetary_summation.tax_total = (Decimal("199.80"), "EUR")
75+
doc.trade.settlement.monetary_summation.grand_total = Decimal("1198.8")
76+
doc.trade.settlement.monetary_summation.due_amount = Decimal("1198.8")
77+
78+
terms = PaymentTerms()
79+
terms.due = datetime.now(timezone.utc) + timedelta(days=30)
80+
doc.trade.settlement.terms.add(terms)
81+
82+
# Generate XML file
83+
xml = doc.serialize(schema="FACTUR-X_EXTENDED")

requirements_dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
cogapp
12
lxml
23
pypdf
34
pytest

tests/conftest.py

Lines changed: 1 addition & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,10 @@
11
import os
22
import pytest
3-
from datetime import date, datetime, timezone
4-
from decimal import Decimal
5-
6-
from drafthorse.models.accounting import ApplicableTradeTax
7-
from drafthorse.models.document import Document
8-
from drafthorse.models.note import IncludedNote
9-
from drafthorse.models.party import TaxRegistration
10-
from drafthorse.models.tradelines import LineItem
3+
from example import doc
114

125

136
@pytest.fixture
147
def invoice_document(request):
15-
doc = Document()
16-
doc.context.guideline_parameter.id = (
17-
"urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended"
18-
)
19-
doc.header.id = "RE1337"
20-
doc.header.type_code = request.param
21-
doc.header.name = "RECHNUNG"
22-
doc.header.issue_date_time = date.today()
23-
doc.header.language = "ger"
24-
25-
doc.header.notes.add(IncludedNote(content="Test Note 1"))
26-
27-
doc.trade.agreement.seller.name = "Lieferant GmbH"
28-
doc.trade.settlement.payee.name = "Lieferant GmbH"
29-
30-
doc.trade.agreement.buyer.name = "Kunde GmbH"
31-
doc.trade.settlement.invoicee.name = "Kunde GmbH"
32-
33-
doc.trade.settlement.currency_code = "EUR"
34-
doc.trade.settlement.payment_means.type_code = "ZZZ"
35-
36-
doc.trade.agreement.seller.address.country_id = "DE"
37-
doc.trade.agreement.seller.address.country_subdivision = "Bayern"
38-
doc.trade.agreement.seller.tax_registrations.add(
39-
TaxRegistration(id=("VA", "DE000000000"))
40-
)
41-
42-
doc.trade.agreement.seller_order.issue_date_time = datetime.now(timezone.utc)
43-
doc.trade.agreement.buyer_order.issue_date_time = datetime.now(timezone.utc)
44-
doc.trade.settlement.advance_payment.received_date = datetime.now(timezone.utc)
45-
doc.trade.agreement.customer_order.issue_date_time = datetime.now(timezone.utc)
46-
47-
li = LineItem()
48-
li.document.line_id = "1"
49-
li.product.name = "Rainbow"
50-
li.agreement.gross.amount = Decimal("999.00")
51-
li.agreement.gross.basis_quantity = (Decimal("1.0000"), "C62") # C62 == pieces
52-
li.agreement.net.amount = Decimal("999.00")
53-
li.agreement.net.basis_quantity = (Decimal("999.00"), "C62")
54-
li.delivery.billed_quantity = (Decimal("1.0000"), "C62") # C62 == pieces
55-
li.settlement.trade_tax.type_code = "VAT"
56-
li.settlement.trade_tax.category_code = "E"
57-
li.settlement.trade_tax.rate_applicable_percent = Decimal("0.00")
58-
li.settlement.monetary_summation.total_amount = Decimal("999.00")
59-
doc.trade.items.add(li)
60-
61-
trade_tax = ApplicableTradeTax()
62-
trade_tax.calculated_amount = Decimal("0.00")
63-
trade_tax.basis_amount = Decimal("999.00")
64-
trade_tax.type_code = "VAT"
65-
trade_tax.category_code = "E"
66-
trade_tax.rate_applicable_percent = Decimal("0.00")
67-
doc.trade.settlement.trade_tax.add(trade_tax)
68-
69-
doc.trade.settlement.monetary_summation.line_total = Decimal("999.00")
70-
doc.trade.settlement.monetary_summation.charge_total = Decimal("0.00")
71-
doc.trade.settlement.monetary_summation.allowance_total = Decimal("0.00")
72-
doc.trade.settlement.monetary_summation.tax_basis_total = Decimal("999.00")
73-
doc.trade.settlement.monetary_summation.tax_total = (Decimal("0.00"), "EUR")
74-
doc.trade.settlement.monetary_summation.grand_total = Decimal("999.00")
75-
doc.trade.settlement.monetary_summation.due_amount = Decimal("999.00")
76-
778
return doc
789

7910

0 commit comments

Comments
 (0)