### Install Development Dependencies
Source: https://github.com/pretix/python-drafthorse/blob/master/README.rst
This command installs the necessary dependencies for development, including testing tools.
```bash
pip install -r requirements_dev.txt
py.test tests
```
--------------------------------
### Create and Set BillingSpecifiedPeriod Example
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/payment-accounting.md
Shows how to instantiate a BillingSpecifiedPeriod, set its description, start date, and end date, and assign it to the trade settlement period. Requires importing BillingSpecifiedPeriod and date.
```python
from drafthorse.models.accounting import BillingSpecifiedPeriod
from datetime import date
period = BillingSpecifiedPeriod()
period.description = "June 2024"
period.start = date(2024, 6, 1)
period.end = date(2024, 6, 30)
doc.trade.settlement.period = period
```
--------------------------------
### Log Example with Basic Configuration
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/utilities.md
A simple example demonstrating how to enable logging for drafthorse and observe debug output during serialization. Requires basic logging configuration.
```python
import logging
logging.basicConfig(level=logging.DEBUG)
from drafthorse.models.document import Document
doc = Document()
# Debug output will show during parsing/serialization
xml = doc.serialize(schema="FACTUR-X_BASIC")
```
--------------------------------
### IncludedNote Examples
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/products-notes.md
Demonstrates creating and adding notes to a document header and a line item. Includes examples for general notes and notes with specific codes.
```python
from drafthorse.models.note import IncludedNote
note = IncludedNote()
note.content = "Goods delivered with standard packaging"
doc.header.notes.add(note)
# With code
line_note = IncludedNote()
line_note.content_code = "95"
line_note.content = "Important: This item is fragile"
li.document.notes.add(line_note)
```
--------------------------------
### ProductClassification Example
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/products-notes.md
Instantiates a ProductClassification and sets the class code. This classification can be added to a product's classifications.
```python
from drafthorse.models.product import ProductClassification
classification = ProductClassification()
classification.class_code = ("8001", "1.0", "88") # list_id, version, code
li.product.classifications.add(classification)
```
--------------------------------
### ProductCharacteristic Example
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/products-notes.md
Creates a ProductCharacteristic instance and assigns a description and value. This characteristic can then be added to a product.
```python
from drafthorse.models.product import ProductCharacteristic
char = ProductCharacteristic()
char.description = "Color"
char.value = "Blue"
li.product.characteristics.add(char)
```
--------------------------------
### Create and Populate a LineItem
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/line-items.md
Demonstrates how to create a LineItem instance and populate its fields, including product details, pricing, quantity, and tax information. This is a comprehensive example for setting up a single line item.
```python
from drafthorse.models.tradelines import LineItem
from decimal import Decimal
li = LineItem()
li.document.line_id = "1"
li.product.name = "Professional Services"
li.agreement.net.amount = Decimal("500.00")
li.agreement.net.basis_quantity = (Decimal("1"), "C62") # Unit
li.delivery.billed_quantity = (Decimal("1"), "C62")
li.settlement.trade_tax.type_code = "VAT"
li.settlement.trade_tax.category_code = "S"
li.settlement.trade_tax.rate_applicable_percent = Decimal("19")
li.settlement.monetary_summation.total_amount = Decimal("500.00")
doc.trade.items.add(li)
```
--------------------------------
### Set Document Context Parameters
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/document.md
Example of setting the guideline parameter ID and the test indicator for the document context.
```python
doc.context.guideline_parameter.id = "urn:cen.eu:en16931:2017"
doc.context.test_indicator = False
```
--------------------------------
### TradeProduct Example
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/products-notes.md
Instantiates a LineItem and sets basic product details like name, description, and IDs. This is useful for creating a product entry in a line item.
```python
from drafthorse.models.tradelines import LineItem
li = LineItem()
li.product.name = "Premium Widget"
li.product.description = "High-quality aluminum widget"
li.product.id = "PWD-001"
li.product.seller_assigned_id = "SELLER-PWD-001"
```
--------------------------------
### Populate Header Properties
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/document.md
Example of how to set various properties of the Header class, including ID, name, type code, issue date, and language. It also shows how to add notes.
```python
from drafthorse.models.document import Document
from drafthorse.models.note import IncludedNote
from datetime import date
doc = Document()
doc.header.id = "RE1337"
doc.header.name = "Invoice"
doc.header.type_code = "380"
doc.header.issue_date_time = date(2024, 6, 15)
doc.header.language = "en"
doc.header.notes.add(IncludedNote(content="Payment due within 30 days"))
```
--------------------------------
### CurrencyField Usage Examples
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/types.md
Examples demonstrating CurrencyField for 'tax_total' and 'grand_total'. Shows assignment with and without currency code.
```python
class MonetarySummation(Element):
tax_total = CurrencyField(NS_RAM, "TaxTotalAmount", required=True)
grand_total = CurrencyField(NS_RAM, "GrandTotalAmount", required=True)
# Usage
summation.tax_total = (Decimal("200.00"), "EUR")
summation.grand_total = Decimal("1200.00") # Also accepts Decimal alone
```
--------------------------------
### Setting BuyerTradeParty Name and Country
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/parties.md
Example of setting the name and country ID for a buyer party. This demonstrates a typical use case for the BuyerTradeParty class.
```python
doc.trade.agreement.buyer.name = "Customer Ltd"
doc.trade.agreement.buyer.address.country_id = "DE"
```
--------------------------------
### Container Usage Example
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/types.md
Demonstrates how to use the Container type for multi-valued fields, such as lists of payment means or trade taxes.
```python
class TradeSettlement(Element):
payment_means: Container = MultiField(PaymentMeans)
trade_tax: Container = MultiField(ApplicableTradeTax)
# Usage
doc.trade.settlement.payment_means.add(PaymentMeans(type_code="30"))
doc.trade.settlement.trade_tax.add(tax_instance)
```
--------------------------------
### ProductInstance Serial ID Example
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/products-notes.md
Assigns a serial number to the product instance. This is used for tracking individual product items.
```python
li.product.individual_trade_item_instance.serial_id = "SN-12345678"
```
--------------------------------
### DateTimeField Usage Examples
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/types.md
Examples demonstrating DateTimeField for 'issue_date_time' in Header and 'due' in PaymentTerms.
```python
class Header(Element):
issue_date_time = DateTimeField(NS_RAM, "IssueDateTime", required=True)
class PaymentTerms(Element):
due = DateTimeField(NS_RAM, "DueDateDateTime", required=False)
```
--------------------------------
### Populating TradeParty Details
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/parties.md
Example demonstrating how to populate the details of a seller party, including name, ID, address, and tax registrations. This snippet shows practical usage of the TradeParty class and its associated models.
```python
from drafthorse.models.party import TaxRegistration
doc.trade.agreement.seller.name = "Widget Manufacturing Ltd"
doc.trade.agreement.seller.id = "SELLER-001"
doc.trade.agreement.seller.address.line_one = "456 Industrial Blvd"
doc.trade.agreement.seller.address.city_name = "Munich"
doc.trade.agreement.seller.address.postcode = "80000"
doc.trade.agreement.seller.address.country_id = "DE"
doc.trade.agreement.seller.tax_registrations.add(
TaxRegistration(id=("VA", "DE123456789"))
)
```
--------------------------------
### Setting SellerTradeParty Name and Country
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/parties.md
Example of setting the name and country ID for a seller party. This illustrates a basic usage scenario for the SellerTradeParty class.
```python
doc.trade.agreement.seller.name = "Acme Corporation"
doc.trade.agreement.seller.address.country_id = "US"
```
--------------------------------
### Set NetPrice Details
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/line-items.md
Example of setting the net amount and basis quantity for a line item's agreement. This defines the price before tax and other charges.
```python
from decimal import Decimal
li.agreement.net.amount = Decimal("99.99")
li.agreement.net.basis_quantity = (Decimal("1"), "C62")
```
--------------------------------
### QuantityField Usage Examples
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/types.md
Examples showing how to assign values to QuantityField for 'billed_quantity' and 'basis_quantity'. Supports common unit codes like 'C62'.
```python
class LineDelivery(Element):
billed_quantity = QuantityField(NS_RAM, "BilledQuantity", required=True)
# Usage
li.delivery.billed_quantity = (Decimal("5"), "C62") # 5 units
li.agreement.net.basis_quantity = (Decimal("1.0"), "C62") # 1 unit basis
```
--------------------------------
### Create and Add AdvancePayment Example
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/payment-accounting.md
Demonstrates how to create an AdvancePayment object, set its properties, add associated trade tax, and append it to the settlement's advance payment list. Requires importing necessary classes and datetime objects.
```python
from drafthorse.models.trade import AdvancePayment, IncludedTradeTax
from datetime import datetime, timezone
from decimal import Decimal
advance = AdvancePayment(
received_date=datetime.now(timezone.utc),
paid_amount=Decimal("500.00")
)
advance.included_trade_tax.add(
IncludedTradeTax(
calculated_amount=Decimal("0"),
type_code="VAT",
category_code="E",
rate_applicable_percent=Decimal("0"),
)
)
doc.trade.settlement.advance_payment.add(advance)
```
--------------------------------
### DecimalField Usage Example
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/types.md
Example showing DecimalField usage for 'calculated_amount' and 'rate_applicable_percent' in an ApplicableTradeTax class.
```python
class ApplicableTradeTax(Element):
calculated_amount = DecimalField(NS_RAM, "CalculatedAmount", required=True)
rate_applicable_percent = DecimalField(NS_RAM, "RateApplicablePercent", required=True)
```
--------------------------------
### StringField Usage Example
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/types.md
Example demonstrating how to use StringField to define 'id' and 'name' properties within an Element class.
```python
class Header(Element):
id = StringField(NS_RAM, "ID", required=True, profile=BASIC)
name = StringField(NS_RAM, "Name", required=True, profile=BASIC)
```
--------------------------------
### IDField Usage Example
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/types.md
Example showing IDField definition for 'id' in TaxRegistration and its usage with a VAT scheme ID.
```python
class TaxRegistration(Element):
id = IDField(NS_RAM, "ID")
# Usage
doc.trade.agreement.seller.tax_registrations.add(
TaxRegistration(id=("VA", "DE123456789"))
)
```
--------------------------------
### Set PayeeFinancialAccount Details
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/payment-accounting.md
Example showing how to access and set the IBAN, account name for a payee's financial account. Assumes the account object is already retrieved.
```python
acct = doc.trade.settlement.payment_means[0].payee_account
acct.iban = "DE89370400440532013000"
acct.account_name = "Company GmbH"
```
--------------------------------
### Simple Invoice Creation
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/overview.md
Provides a comprehensive example of creating a new invoice document, setting header, parties, line items, and payment details. The generated XML can be serialized using a specified schema.
```python
from drafthorse.models.document import Document
from drafthorse.models.tradelines import LineItem
from drafthorse.models.note import IncludedNote
from drafthorse.models.party import TaxRegistration
from datetime import date
from decimal import Decimal
# Create document
doc = Document()
# Set context and header
doc.context.guideline_parameter.id = "urn:cen.eu:en16931:2017"
doc.header.id = "INV-2024-001"
doc.header.type_code = "380"
doc.header.issue_date_time = date.today()
# Set parties
doc.trade.agreement.seller.name = "ABC Corporation"
doc.trade.agreement.seller.address.country_id = "US"
doc.trade.agreement.seller.tax_registrations.add(
TaxRegistration(id=("VA", "US123456789"))
)
doc.trade.agreement.buyer.name = "XYZ Limited"
doc.trade.agreement.buyer.address.country_id = "US"
# Set payment details
doc.trade.settlement.currency_code = "USD"
# Add line item
li = LineItem()
li.document.line_id = "1"
li.product.name = "Consulting Services"
li.agreement.net.amount = Decimal("1000.00")
li.agreement.net.basis_quantity = (Decimal("1"), "C62")
li.delivery.billed_quantity = (Decimal("1"), "C62")
li.settlement.trade_tax.type_code = "VAT"
li.settlement.trade_tax.category_code = "S"
li.settlement.trade_tax.rate_applicable_percent = Decimal("0")
li.settlement.monetary_summation.total_amount = Decimal("1000.00")
doc.trade.items.add(li)
# Set totals
doc.trade.settlement.monetary_summation.line_total = Decimal("1000.00")
doc.trade.settlement.monetary_summation.charge_total = Decimal("0.00")
doc.trade.settlement.monetary_summation.allowance_total = Decimal("0.00")
doc.trade.settlement.monetary_summation.tax_basis_total = Decimal("1000.00")
doc.trade.settlement.monetary_summation.tax_total = (Decimal("0.00"), "USD")
doc.trade.settlement.monetary_summation.grand_total = Decimal("1000.00")
doc.trade.settlement.monetary_summation.due_amount = Decimal("1000.00")
# Serialize
xml = doc.serialize(schema="FACTUR-X_BASIC")
# Save to file
with open("invoice.xml", "wb") as f:
f.write(xml)
```
--------------------------------
### Set GrossPrice Details
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/line-items.md
Example of setting the gross amount and basis quantity for a line item's agreement. This is part of defining the overall price including any surcharges.
```python
from decimal import Decimal
li.agreement.gross.amount = Decimal("119.99")
li.agreement.gross.basis_quantity = (Decimal("1"), "C62")
```
--------------------------------
### Accessing single-valued fields
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/overview.md
Demonstrates how to get and set values for single-valued fields, including nested properties.
```python
# Get
name = doc.trade.agreement.seller.name
# Set
doc.trade.agreement.seller.name = "New Name"
# Access nested properties
doc.trade.agreement.seller.address.country_id = "US"
```
--------------------------------
### Setting Trade Delivery Properties
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/trade-transaction.md
This example shows how to set recipient address details and the actual delivery date/time for a trade delivery. Ensure the datetime and timezone modules are imported.
```python
from datetime import datetime, timezone
doc.trade.delivery.ship_to.name = "Delivery Branch"
doc.trade.delivery.ship_to.address.line_one = "123 Main St"
doc.trade.delivery.ship_to.address.city_name = "New York"
doc.trade.delivery.ship_to.address.country_id = "US"
# Set actual delivery date
doc.trade.delivery.event.occurrence = datetime.now(timezone.utc)
```
--------------------------------
### Set PayeeFinancialInstitution BIC
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/payment-accounting.md
Example demonstrating how to set the BIC/SWIFT code for a payee's financial institution. Assumes the institution object is already retrieved.
```python
inst = doc.trade.settlement.payment_means[0].payee_institution
inst.bic = "DEUTDEDD"
```
--------------------------------
### Pretty Print XML Output
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/configuration.md
Example of the pretty-printed XML output generated by the serialize() method, including the XML declaration and namespaces.
```xml
...
```
--------------------------------
### Sort Imports Automatically
Source: https://github.com/pretix/python-drafthorse/blob/master/README.rst
This command automatically sorts imports in the project to comply with CI requirements. Ensure isort is installed.
```bash
pip install isort
isort -rc .
```
--------------------------------
### Generate ZUGFeRD XML and Attach to PDF
Source: https://github.com/pretix/python-drafthorse/blob/master/README.rst
Build a `Document` object with invoice details and use `attach_xml` to embed it into a PDF. This example demonstrates setting various invoice components including header, trade details, line items, and payment information.
```python
from datetime import date, datetime, timedelta, timezone
from decimal import Decimal
from drafthorse.models.accounting import ApplicableTradeTax
from drafthorse.models.document import Document
from drafthorse.models.note import IncludedNote
from drafthorse.models.party import TaxRegistration
from drafthorse.models.payment import PaymentMeans, PaymentTerms
from drafthorse.models.trade import AdvancePayment, IncludedTradeTax
from drafthorse.models.tradelines import LineItem
from drafthorse.pdf import attach_xml
# Build data structure
doc = Document()
doc.context.guideline_parameter.id = "urn:cen.eu:en16931:2017"
doc.header.id = "RE1337"
doc.header.type_code = "380"
doc.header.issue_date_time = date.today()
doc.header.notes.add(IncludedNote(content="Test Note 1"))
doc.trade.agreement.buyer.name = "Kunde GmbH"
doc.trade.agreement.buyer.address.country_id = "DE"
doc.trade.settlement.currency_code = "EUR"
doc.trade.settlement.payment_means.add(PaymentMeans(type_code="ZZZ"))
doc.trade.agreement.seller.name = "Lieferant GmbH"
doc.trade.agreement.seller.address.country_id = "DE"
doc.trade.agreement.seller.address.country_subdivision = "Bayern"
doc.trade.agreement.seller.tax_registrations.add(
TaxRegistration(id=("VA", "DE000000000"))
)
advance = AdvancePayment(
received_date=datetime.now(timezone.utc),
paid_amount=Decimal(42)
)
advance.included_trade_tax.add(
IncludedTradeTax(
calculated_amount=Decimal(0),
type_code="VAT",
category_code="E",
rate_applicable_percent=Decimal(0),
)
)
doc.trade.settlement.advance_payment.add(advance)
li = LineItem()
li.document.line_id = "1"
li.product.name = "Rainbow"
li.agreement.gross.amount = Decimal("1198.8")
li.agreement.gross.basis_quantity = (Decimal("1.0000"), "C62") # C62 == unit
li.agreement.net.amount = Decimal("999")
li.agreement.net.basis_quantity = (Decimal("1.0000"), "C62") # C62 == unit
li.delivery.billed_quantity = (Decimal("1.0000"), "C62") # C62 == unit
li.settlement.trade_tax.type_code = "VAT"
# attach_xml("invoice.pdf", doc.as_xml())
```
--------------------------------
### Declaration-Based Model Example
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/overview.md
Defines a custom model class `MyElement` inheriting from `Element`. It uses `StringField` and `DecimalField` for data representation and includes a `Meta` class for XML configuration. Fields are descriptors managing serialization and validation.
```python
from drafthorse.models.elements import Element
from drafthorse.models.fields import StringField, DecimalField, Field
class MyElement(Element):
name = StringField(NS_RAM, "Name", required=True)
amount = DecimalField(NS_RAM, "Amount", required=True)
description = StringField(NS_RAM, "Description")
class Meta:
namespace = NS_RAM
tag = "MyElementTag"
```
--------------------------------
### Handling XML Parsing Errors
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/errors.md
This example demonstrates how to handle exceptions that occur when parsing invalid or incorrectly encoded XML data. It shows how to catch generic parse errors and encoding issues, providing corrected code for reading XML files properly.
```python
from drafthorse.models.document import Document
try:
# Not valid XML
doc = Document.parse(b"not xml at all")
except Exception as e:
print(f"Parse error: {e}")
# Verify file is valid XML before parsing
with open("invoice.xml", "rb") as f:
doc = Document.parse(f.read())
try:
# Wrong encoding
xml_str = "..." # String instead of bytes
doc = Document.parse(xml_str.encode("utf-16"))
except Exception as e:
print(f"Encoding error: {e}")
# Correct: UTF-8
doc = Document.parse(xml_str.encode("utf-8"))
```
--------------------------------
### Handling TypeError for Non-Bytes Data in PDF Attachment
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/errors.md
This example shows how to handle TypeErrors when non-bytes data is provided to the `attach_xml` function. It demonstrates the correct way to pass PDF and XML data as bytes, including reading files in binary mode and serializing Document objects.
```python
from drafthorse.pdf import attach_xml
try:
# Wrong: string instead of bytes
pdf_bytes = attach_xml("pdf data", b"")
except TypeError as e:
print(f"Type error: {e}")
# Correct: read file in binary mode
with open("template.pdf", "rb") as f: # "rb" for binary
pdf_data = f.read()
pdf_bytes = attach_xml(pdf_data, b"")
try:
# Wrong: Document object instead of bytes
xml = doc # This is a Document, not bytes!
pdf_bytes = attach_xml(pdf_data, xml)
except TypeError as e:
print(f"XML type error: {e}")
# Correct: serialize first
xml = doc.serialize(schema="FACTUR-X_BASIC") # Returns bytes
pdf_bytes = attach_xml(pdf_data, xml)
```
--------------------------------
### Optimizing Document Creation and Serialization
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/utilities.md
Provides tips for performance, including reusing class instances when creating many documents and avoiding repeated serialization with different schemas. Suggests using schema=None for testing.
```python
# If creating many documents, reuse classes
from drafthorse.models.tradelines import LineItem
# Efficient
for i in range(1000):
li = LineItem()
# ... configure ...
doc.trade.items.add(li)
# Avoid repeated serialization with different schemas
xml_basic = doc.serialize(schema="FACTUR-X_BASIC")
# Don't do: for i in range(100): doc.serialize(schema="FACTUR-X_BASIC")
# Use schema=None for testing
test_xml = doc.serialize(schema=None) # Fast
```
--------------------------------
### BillingSpecifiedPeriod
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/payment-accounting.md
Defines a billing period for an invoice or line item, including a description, start date, and end date.
```APIDOC
## BillingSpecifiedPeriod
Billing period for invoice or line item.
### Properties
| Property | Type | Required | Profile | Description |
|----------|------|----------|---------|-------------|
| start | date | Yes | COMFORT | Period start date |
| end | date | Yes | COMFORT | Period end date |
| description | str | Yes | COMFORT | Free text description |
**Example:**
```python
from drafthorse.models.accounting import BillingSpecifiedPeriod
from datetime import date
period = BillingSpecifiedPeriod()
period.description = "June 2024"
period.start = date(2024, 6, 1)
period.end = date(2024, 6, 30)
doc.trade.settlement.period = period
```
```
--------------------------------
### BillingSpecifiedPeriod Model Signature
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/payment-accounting.md
Defines the structure for a billing period, including a description, start date, and end date.
```python
class BillingSpecifiedPeriod(Element):
description: StringField
start: DateTimeField
end: DateTimeField
```
--------------------------------
### Initialize Payment Means (Bank Transfer)
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/payment-accounting.md
Shows how to create a PaymentMeans object for bank transfers and add it to the document's settlement. It also demonstrates how to populate payee account and institution details.
```python
from drafthorse.models.payment import PaymentMeans
pm = PaymentMeans(type_code="30") # Bank transfer
doc.trade.settlement.payment_means.add(pm)
# With bank account
payee_acct = doc.trade.settlement.payment_means[0].payee_account
payee_acct.iban = "DE89370400440532013000"
payee_inst = doc.trade.settlement.payment_means[0].payee_institution
payee_inst.bic = "COBADEDDXXX"
```
--------------------------------
### Add Tax Registration
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/types.md
Adds a tax registration to a seller's tax registrations. This is an example of adding an item to a collection.
```python
doc.trade.agreement.seller.tax_registrations.add(
TaxRegistration(id="DE123456789")
)
```
--------------------------------
### Add Line Item and Configure Settlement
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/trade-transaction.md
Demonstrates how to add a line item with details like ID, name, and net amount, and configure settlement currency and due amount. Requires importing LineItem and Decimal.
```python
from drafthorse.models.tradelines import LineItem
from decimal import Decimal
# Add line item
li = LineItem()
li.document.line_id = "1"
li.product.name = "Widget"
li.agreement.net.amount = Decimal("100.00")
doc.trade.items.add(li)
# Configure settlement
doc.trade.settlement.currency_code = "EUR"
doc.trade.settlement.monetary_summation.due_amount = Decimal("100.00")
```
--------------------------------
### Create a New Invoice
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/README.md
Demonstrates how to create a new invoice document, set header information, define parties, add line items with tax details, and configure settlement totals. This is useful for generating outgoing invoices.
```python
from drafthorse.models.document import Document
from drafthorse.models.tradelines import LineItem
from decimal import Decimal
from datetime import date
# Create document
doc = Document()
doc.context.guideline_parameter.id = "urn:cen.eu:en16931:2017"
doc.header.id = "INV001"
doc.header.type_code = "380"
doc.header.issue_date_time = date.today()
# Set parties
doc.trade.agreement.seller.name = "Seller Inc"
doc.trade.agreement.buyer.name = "Buyer Ltd"
# Add line item
li = LineItem()
li.document.line_id = "1"
li.product.name = "Service"
li.agreement.net.amount = Decimal("100.00")
li.agreement.net.basis_quantity = (Decimal("1"), "C62")
li.delivery.billed_quantity = (Decimal("1"), "C62")
li.settlement.trade_tax.type_code = "VAT"
li.settlement.trade_tax.category_code = "S"
li.settlement.trade_tax.rate_applicable_percent = Decimal("19")
li.settlement.monetary_summation.total_amount = Decimal("100.00")
doc.trade.items.add(li)
# Set settlement
doc.trade.settlement.currency_code = "EUR"
doc.trade.settlement.monetary_summation.line_total = Decimal("100.00")
doc.trade.settlement.monetary_summation.charge_total = Decimal("0.00")
doc.trade.settlement.monetary_summation.allowance_total = Decimal("0.00")
doc.trade.settlement.monetary_summation.tax_basis_total = Decimal("100.00")
doc.trade.settlement.monetary_summation.tax_total = (Decimal("19.00"), "EUR")
doc.trade.settlement.monetary_summation.grand_total = Decimal("119.00")
doc.trade.settlement.monetary_summation.due_amount = Decimal("119.00")
# Serialize
xml = doc.serialize(schema="FACTUR-X_BASIC")
```
--------------------------------
### Configure Trade Agreement Details
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/trade-transaction.md
Shows how to set buyer and seller names and country codes, and add a buyer order reference. This is useful for establishing the parties involved and referencing specific orders within the trade agreement.
```python
doc.trade.agreement.buyer.name = "Acme Corp"
doc.trade.agreement.buyer.address.country_id = "US"
doc.trade.agreement.seller.name = "Widget Inc"
doc.trade.agreement.seller.address.country_id = "US"
# Add buyer order reference
doc.trade.agreement.buyer_order.issuer_assigned_id = "BO-2024-001"
```
--------------------------------
### Initialize Payment Terms
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/payment-accounting.md
Illustrates how to create a PaymentTerms object to define payment conditions, including description and due date, and add it to the document's settlement.
```python
from drafthorse.models.payment import PaymentTerms
from datetime import datetime, timedelta, timezone
terms = PaymentTerms()
terms.description = "Net 30 days"
terms.due = datetime.now(timezone.utc) + timedelta(days=30)
doc.trade.settlement.terms.add(terms)
```
--------------------------------
### Working with Date and DateTime Fields
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/utilities.md
Illustrates how to use Python's datetime module for date-only and date-time fields. Shows setting specific dates, using today's date, and performing date calculations.
```python
from datetime import date, datetime, timezone, timedelta
# For date-only fields
doc.header.issue_date_time = date(2024, 6, 15)
doc.header.issue_date_time = date.today()
# For date-time fields
doc.trade.settlement.terms[0].due = datetime.now(timezone.utc)
doc.trade.settlement.terms[0].due = datetime(2024, 7, 15, 17, 30, 0)
# Calculations
future = date.today() + timedelta(days=30)
doc.trade.settlement.terms[0].due = future
```
--------------------------------
### Set LineAgreement Net and Gross Prices
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/line-items.md
Demonstrates setting the net and gross prices for a line item's agreement, including amount and basis quantity. Use this to define the pricing details for a line item.
```python
from decimal import Decimal
li.agreement.net.amount = Decimal("99.99")
li.agreement.net.basis_quantity = (Decimal("1"), "C62") # C62 = unit
li.agreement.gross.amount = Decimal("119.99")
li.agreement.gross.basis_quantity = (Decimal("1"), "C62")
```
--------------------------------
### Initialize Element Fields with Keyword Arguments
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/configuration.md
When creating Element instances, you can initialize their fields by passing keyword arguments directly to the constructor. Alternatively, fields can be set after instantiation.
```python
from drafthorse.models.payment import PaymentTerms
from datetime import datetime, timezone
# Initialize with keyword arguments
terms = PaymentTerms(description="Net 30 days")
# Or set after creation
terms = PaymentTerms()
terms.description = "Net 30 days"
```
--------------------------------
### Create Invoice PDF with Early Validation
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/errors.md
This snippet demonstrates creating an invoice document, performing early validation against a schema, and then attaching the validated XML to a PDF. It includes basic error handling for the entire process.
```python
def create_invoice_pdf(seller_name, buyer_name, items):
try:
# Create document
doc = Document()
# ... populate fields ...
# Validate early
xml = doc.serialize(schema="FACTUR-X_BASIC")
print("Document is valid")
# Then attach to PDF
with open("template.pdf", "rb") as f:
pdf = attach_xml(f.read(), xml)
return pdf
except Exception as e:
logger.error(f"Invoice creation failed: {e}")
raise
```
--------------------------------
### Initialize Trade Allowance Charge
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/payment-accounting.md
Demonstrates how to create and populate a TradeAllowanceCharge object for discounts or charges. This is useful for applying specific allowances or charges to a trade.
```python
from drafthorse.models.accounting import TradeAllowanceCharge
from decimal import Decimal
allowance = TradeAllowanceCharge()
allowance.indicator = False # Allowance/discount
allowance.actual_amount = Decimal("100.00")
allowance.reason = "Volume discount"
doc.trade.settlement.allowance_charge.add(allowance)
```
--------------------------------
### Handle Invalid Schema Parameter
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/errors.md
Catch `FileNotFoundError` when an invalid or non-existent schema name is provided to the `serialize()` method. This can happen due to typos or case sensitivity issues. The example demonstrates correcting the schema name.
```python
doc = Document()
# ... populate document ...
try:
# Typo: "FACTUR-X_BASICS" instead of "FACTUR-X_BASIC"
xml = doc.serialize(schema="FACTUR-X_BASICS")
except FileNotFoundError as e:
print(f"Schema file not found: {e}")
# Use valid schema name
xml = doc.serialize(schema="FACTUR-X_BASIC")
```
--------------------------------
### Using Decimal for Monetary Amounts and Percentages
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/utilities.md
Demonstrates the correct usage of Python's decimal.Decimal for financial calculations to avoid floating-point errors. Shows how to initialize Decimal values and use them in currency fields.
```python
from decimal import Decimal
# Always use Decimal for amounts
amount = Decimal("100.50")
percentage = Decimal("19.00")
# NOT float
amount = 100.50 # Wrong - will be rejected
# For currency fields, tuple with Decimal
doc.trade.settlement.monetary_summation.tax_total = (Decimal("200.00"), "EUR")
# String to Decimal conversion
user_input = "99.99"
amount = Decimal(user_input) # Safe conversion
```
--------------------------------
### ProductInstance
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/products-notes.md
Provides instance-level details for a product item, specifically its batch/lot ID and serial number.
```APIDOC
## ProductInstance
### Description
Instance-level details for a product item.
### Properties
- **batch_id** (IDField) - Optional - Batch/lot ID
- **serial_id** (str) - Optional - Serial number
### Request Example
```python
# Assuming 'li' is an instance of LineItem
# li.product.individual_trade_item_instance.serial_id = "SN-12345678"
```
```
--------------------------------
### Populate Document with Data
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/overview.md
Assigns data to various fields within the `Document` object, including header information, seller and buyer details, and other required fields. This step populates the invoice structure.
```python
doc.header.id = "INV001"
doc.context.guideline_parameter.id = "urn:..."
doc.trade.agreement.seller.name = "..."
doc.trade.agreement.buyer.name = "..."
... (set all required fields)
```
--------------------------------
### Document Constructor
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/document.md
Creates a new Document instance. This constructor automatically registers default namespaces.
```python
doc = Document()
```
--------------------------------
### Handle XML Schema Validation Failure
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/errors.md
Catch `lxml.etree.DocumentInvalid` when `serialize()` fails schema validation. This typically occurs due to missing required fields or values not matching schema constraints. The example shows how to catch the error and then populate the missing fields.
```python
from drafthorse.models.document import Document
from lxml import etree
doc = Document()
# Missing required fields like header.id, header.type_code, etc.
try:
xml = doc.serialize(schema="FACTUR-X_BASIC")
except etree.DocumentInvalid as e:
print(f"Validation failed: {e}")
# Fix: Set all required fields
doc.header.id = "INV001"
doc.header.type_code = "380"
# ... set other required fields ...
xml = doc.serialize(schema="FACTUR-X_BASIC")
```
--------------------------------
### Locate and List Built-in Schemas
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/utilities.md
Demonstrates how to find the directory containing the bundled XSD schemas within the drafthorse package and list them. This is useful for understanding available schema files.
```python
import os
from drafthorse import models
schema_dir = os.path.join(os.path.dirname(models.__file__), "schema")
print(f"Schema directory: {schema_dir}")
# List available schemas
for filename in os.listdir(schema_dir):
if filename.endswith(".xsd"):
print(f" {filename}")
```
--------------------------------
### Working with Dates
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/utilities.md
Instructions for using Python's `datetime` module for date and date-time fields, including formatting notes.
```APIDOC
## Working with Dates
Use Python's `datetime` module:
```python
from datetime import date, datetime, timezone, timedelta
# For date-only fields
doc.header.issue_date_time = date(2024, 6, 15)
doc.header.issue_date_time = date.today()
# For date-time fields
doc.trade.settlement.terms[0].due = datetime.now(timezone.utc)
doc.trade.settlement.terms[0].due = datetime(2024, 7, 15, 17, 30, 0)
# Calculations
future = date.today() + timedelta(days=30)
doc.trade.settlement.terms[0].due = future
```
**Format Note:** Dates are automatically formatted to YYYYMMDD for ZUGFeRD XML. Times include timezone information.
```
--------------------------------
### Handle TypeError for Container Mismatch
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/errors.md
Catch `TypeError` when adding an item of an incorrect type to a drafthorse container, such as adding a `PaymentMeans` object to a list expecting `LineItem` objects, or adding a string instead of an element instance. The examples demonstrate adding the correct element types.
```python
from drafthorse.models.payment import PaymentMeans
from drafthorse.models.tradelines import LineItem
doc = Document()
try:
# Wrong: PaymentMeans instead of LineItem
doc.trade.items.add(PaymentMeans(type_code="30"))
except TypeError as e:
print(f"Type mismatch: {e}")
# Correct: LineItem
li = LineItem()
doc.trade.items.add(li)
```
```python
try:
# Wrong: string instead of Element
doc.trade.settlement.payment_means.add("30")
except TypeError as e:
print(f"Type mismatch: {e}")
# Correct: PaymentMeans instance
pm = PaymentMeans(type_code="30")
doc.trade.settlement.payment_means.add(pm)
```
--------------------------------
### Amount with Currency Pattern
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/types.md
Illustrates how to set fields that require both an amount and a currency. Currency can be optional, defaulting to a standard if not provided.
```python
# Set separately
item.some_field = (Decimal("100.00"), "EUR")
# Or amount-only (currency optional)
item.some_field = Decimal("100.00")
```
--------------------------------
### Handle AttributeError for Unknown Fields
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/errors.md
Catch `AttributeError` when attempting to set a field that does not exist on a drafthorse model element. This often results from typos in field names or attempting to set a field on the wrong class. The examples show correcting field names and assigning them to the correct element.
```python
doc = Document()
try:
# Typo: "Id" instead of "id"
doc.header.Id = "INV001" # ERROR!
except AttributeError as e:
print(f"Unknown field: {e}")
# Correct: lowercase
doc.header.id = "INV001"
```
```python
try:
# Wrong class: Document has no 'issue_date_time'
doc.issue_date_time = date.today() # ERROR!
except AttributeError as e:
print(f"Wrong element: {e}")
# Correct: Header contains issue_date_time
doc.header.issue_date_time = date.today()
```
--------------------------------
### ProductInstance Class Signature
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/products-notes.md
Holds instance-level details for a product item, specifically batch and serial numbers.
```python
class ProductInstance(Element):
batch_id: IDField
serial_id: StringField
```
--------------------------------
### Working with Decimal Values
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/utilities.md
Guidance on using Python's `decimal.Decimal` for monetary amounts and percentages to ensure precision.
```APIDOC
## Working with Decimal Values
The library uses Python's `decimal.Decimal` type for all monetary amounts and percentages:
```python
from decimal import Decimal
# Always use Decimal for amounts
amount = Decimal("100.50")
percentage = Decimal("19.00")
# NOT float
amount = 100.50 # Wrong - will be rejected
# For currency fields, tuple with Decimal
doc.trade.settlement.monetary_summation.tax_total = (Decimal("200.00"), "EUR")
# String to Decimal conversion
user_input = "99.99"
amount = Decimal(user_input) # Safe conversion
```
**Why Decimal?** Decimals provide exact decimal arithmetic without floating-point rounding errors, critical for financial calculations.
```
--------------------------------
### Serialize document to XML with different profiles
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/overview.md
Shows how to serialize a document to XML using different ZUGFeRD profile levels: BASIC, COMFORT, and EXTENDED.
```python
# BASIC profile (minimal)
xml = doc.serialize(schema="FACTUR-X_BASIC")
# COMFORT profile (more detail)
xml = doc.serialize(schema="FACTUR-X_COMFORT")
# EXTENDED profile (all fields)
xml = doc.serialize(schema="FACTUR-X_EXTENDED")
```
--------------------------------
### Parsing ZUGFeRD XML: Read XML from File
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/overview.md
Demonstrates how to read XML data from a file in binary mode.
```python
with open("invoice.xml", "rb") as f:
xml_bytes = f.read()
```
--------------------------------
### Validate Files with Mustang
Source: https://github.com/pretix/python-drafthorse/blob/master/README.rst
This snippet outlines the steps to validate files using the Mustang project's command-line tool. It includes cloning the repository, checking out a specific version, and running the validation action.
```bash
git clone https://github.com/ZUGFeRD/mustangproject.git
cd mustangproject
git checkout core-2.9.0
./mvnw clean package
java -jar Mustang-CLI/target/Mustang-CLI-2.7.4-SNAPSHOT.jar --action validate --source invoice.pdf
```
--------------------------------
### Logging Configuration
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/utilities.md
Details on configuring the 'drafthorse' logger, including setting log levels and adding handlers.
```APIDOC
## Logging
The library uses Python's standard logging module with logger name "drafthorse":
### Configuring Logging
```python
import logging
# Get the drafthorse logger
logger = logging.getLogger("drafthorse")
# Set level
logger.setLevel(logging.DEBUG)
# Add handler
handler = logging.StreamHandler()
formatter = logging.Formatter("%(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)
```
### Log Levels
| Level | When Used | Example Message |
|-------|-----------|-----------------|
| WARNING | Degraded functionality | "Could not validate output as LXML is not installed." |
| DEBUG | Diagnostic information | XML parsing progress, field initialization |
| ERROR | Exceptions during operation | Validation failures, I/O errors |
### Examples
```python
import logging
logging.basicConfig(level=logging.DEBUG)
from drafthorse.models.document import Document
doc = Document()
# Debug output will show during parsing/serialization
xml = doc.serialize(schema="FACTUR-X_BASIC")
```
```
--------------------------------
### Performance Considerations
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/utilities.md
Overview of performance aspects including serialization, parsing, and memory usage, with optimization tips.
```APIDOC
## Performance Considerations
### Serialization
- `serialize()` is relatively fast for typical invoices (100-1000 line items)
- Validation against XSD (~100KB) is the slowest part
- Use `schema=None` to skip validation if needed
### Parsing
- `parse()` is optimized using lxml
- Parsing 100-line invoices is typically < 100ms
- Lenient parsing (`strict=False`) is slightly slower due to extra checking
### Memory
- Each Element holds references to field descriptors
- Containers hold references to items
- Large documents (10K+ items) use reasonable memory (<100MB typical)
### Optimization Tips
```python
# If creating many documents, reuse classes
from drafthorse.models.tradelines import LineItem
# Efficient
for i in range(1000):
li = LineItem()
# ... configure ...
doc.trade.items.add(li)
# Avoid repeated serialization with different schemas
xml_basic = doc.serialize(schema="FACTUR-X_BASIC")
# Don't do: for i in range(100): doc.serialize(schema="FACTUR-X_BASIC")
# Use schema=None for testing
test_xml = doc.serialize(schema=None) # Fast
```
```
--------------------------------
### Create Document Structure
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/overview.md
Initializes a new `Document` object, which forms the basis for creating a ZUGFeRD invoice. This is the first step in the data flow.
```python
from drafthorse.models.document import Document
doc = Document()
```
--------------------------------
### Reading an Existing Invoice
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/overview.md
Demonstrates how to parse an existing XML invoice file and access its data, including header, parties, totals, and line items.
```python
from drafthorse.models.document import Document
# Parse from file
with open("invoice.xml", "rb") as f:
doc = Document.parse(f.read(), strict=False)
# Access data
print(f"Invoice: {doc.header.id}")
print(f"Seller: {doc.trade.agreement.seller.name}")
print(f"Buyer: {doc.trade.agreement.buyer.name}")
print(f"Total: {doc.trade.settlement.monetary_summation.grand_total}")
for line in doc.trade.items.children:
print(f" - {line.product.name}: {line.settlement.monetary_summation.total_amount}")
```
--------------------------------
### Adding items to multi-valued fields
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/overview.md
Demonstrates how to add new items to multi-valued fields like payment means or trade tax.
```python
# Adding items
doc.trade.settlement.payment_means.add(payment_method)
doc.trade.settlement.trade_tax.add(tax_item)
```
--------------------------------
### Set Payment Terms and Serialize Document
Source: https://github.com/pretix/python-drafthorse/blob/master/README.rst
This snippet shows how to set payment due date and serialize the document to XML using a specific schema. Validation is performed by default.
```python
terms = PaymentTerms()
terms.due = datetime.now(timezone.utc) + timedelta(days=30)
doc.trade.settlement.terms.add(terms)
# Generate XML file
xml = doc.serialize(schema="FACTUR-X_EXTENDED")
```
--------------------------------
### Set Procuring Project ID and Name
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/pdf-references.md
Configure the ID and name for a procuring project. This is used to associate trade agreements with specific projects.
```python
doc.trade.agreement.procuring_project_type.id = "PROJ-001"
doc.trade.agreement.procuring_project_type.name = "Website Redesign"
```
--------------------------------
### Quantity with Unit Pattern
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/types.md
Shows how to represent quantities with their corresponding units. This is useful for fields like item counts or weights.
```python
item.quantity_field = (Decimal("5"), "C62") # 5 units
item.quantity_field = (Decimal("10.5"), "KGM") # 10.5 kilograms
```
--------------------------------
### Configure drafthorse Logging
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/utilities.md
Shows how to configure the 'drafthorse' logger to control the level of detail and format of log messages. Useful for debugging and monitoring library operations.
```python
import logging
# Get the drafthorse logger
logger = logging.getLogger("drafthorse")
# Set level
logger.setLevel(logging.DEBUG)
# Add handler
handler = logging.StreamHandler()
formatter = logging.Formatter("%(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)
```
--------------------------------
### Memory Management
Source: https://github.com/pretix/python-drafthorse/blob/master/_autodocs/api-reference/utilities.md
Explanation of how the library manages element references and memory, particularly within containers.
```APIDOC
## Memory Management
The library maintains references to all added elements:
```python
# Elements are kept in containers
pm1 = PaymentMeans(type_code="30")
doc.trade.settlement.payment_means.add(pm1)
pm2 = PaymentMeans(type_code="31")
doc.trade.settlement.payment_means.add(pm2)
# Both are retained
assert len(doc.trade.settlement.payment_means.children) == 2
# Modifying original also affects container
pm1.type_code = "42"
assert doc.trade.settlement.payment_means.children[0].type_code == "42"
```
```