### 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" ``` ```