]> git.ipfire.org Git - thirdparty/python-drafthorse.git/commitdiff
Fix #42 -- Always include all namespaces
authorRaphael Michel <michel@rami.io>
Wed, 22 Jan 2025 15:58:54 +0000 (16:58 +0100)
committerRaphael Michel <michel@rami.io>
Wed, 22 Jan 2025 15:58:54 +0000 (16:58 +0100)
drafthorse/models/document.py
tests/test_minimal.py

index 2056eed85f8d12ef9755240684d919bc6e321fc4..bbdcc11140e8b62f24d1399731c7203d4a6b2a80 100644 (file)
@@ -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
index d21a3b4ad7de58549c9ae77f483cc7e34665a775..d450509ab9f679f14a6e81aa203aa9f93f8a0471 100644 (file)
@@ -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()