From: Raphael Michel Date: Wed, 22 Jan 2025 15:58:54 +0000 (+0100) Subject: Fix #42 -- Always include all namespaces X-Git-Tag: 2025.1.0~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1f80911ebc8e9155ccd849dc6c451878a90f004e;p=thirdparty%2Fpython-drafthorse.git Fix #42 -- Always include all namespaces --- diff --git a/drafthorse/models/document.py b/drafthorse/models/document.py index 2056eed..bbdcc11 100644 --- a/drafthorse/models/document.py +++ b/drafthorse/models/document.py @@ -100,14 +100,30 @@ class Document(Element): context: DocumentContext = Field(DocumentContext, required=True) header: Header = Field(Header, required=True) trade: TradeTransaction = Field(TradeTransaction, required=True) + __namespaces = { + "rsm": NS_RSM, + "qdt": NS_QDT, + "ram": NS_RAM, + "xs": "http://www.w3.org/2001/XMLSchema", + "xsi": "http://www.w3.org/2001/XMLSchema-instance", + "udt": NS_UDT, + } def __init__(self): super().__init__() - ET.register_namespace("rsm", NS_RSM) - ET.register_namespace("qdt", NS_QDT) - ET.register_namespace("ram", NS_RAM) - ET.register_namespace("xs", "http://www.w3.org/2001/XMLSchema") - ET.register_namespace("udt", NS_UDT) + for ns, url in self.__namespaces.items(): + ET.register_namespace(ns, url) + + def serialize(self, schema="FACTUR-X_BASIC"): + # First pass + xml = super().serialize(schema) + + # Second pass to ensure all namespaces are defined even if they are unused + root = ET.fromstring(xml) + for ns, url in self.__namespaces.items(): + if ns.encode() not in xml: + root.set(f"xmlns:{ns}", url) + return ET.tostring(root, "utf-8") class Meta: namespace = NS_RSM diff --git a/tests/test_minimal.py b/tests/test_minimal.py index d21a3b4..d450509 100644 --- a/tests/test_minimal.py +++ b/tests/test_minimal.py @@ -109,3 +109,17 @@ def test_invalid_invoice_exceptions(invoice_document, invoice_pdf17_bytes): attach_xml(invoice_pdf17_bytes, "invalid_xml_type") assert str(exc_info.value) == "Please supply XML data as bytes." + + +@pytest.mark.parametrize("invoice_document", ["380"], indirect=True) +def test_unused_name_spaces_included(invoice_document): + doc = invoice_document + doc.context.guideline_parameter.id = ( + "urn:cen.eu:en16932:2017#conformant#urn:factur-x.eu:1p0:wrong" + ) + xml = doc.serialize(schema="FACTUR-X_EXTENDED") + assert 'xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"' in xml.decode() + assert 'xmlns:xs="http://www.w3.org/2001/XMLSchema"' in xml.decode() + assert 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' in xml.decode() + assert 'xs:' not in xml.decode() + assert 'xsi:' not in xml.decode()