This commit is contained in:
dfuhrmann3 2026-07-06 11:12:39 +02:00
parent ec8352682e
commit 37b7986831
1 changed files with 11 additions and 3 deletions

14
main.py
View File

@ -347,12 +347,17 @@ def generate_compliant_zugferd_cii(data):
RSM = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" RSM = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
RAM = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" RAM = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
UDT = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100" UDT = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
QDT = "urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
ET.register_namespace('rsm', RSM) ET.register_namespace('rsm', RSM)
ET.register_namespace('ram', RAM) ET.register_namespace('ram', RAM)
ET.register_namespace('udt', UDT) ET.register_namespace('udt', UDT)
ET.register_namespace('qdt', QDT)
root = ET.Element(f"{{{RSM}}}CrossIndustryInvoice") root = ET.Element(f"{{{RSM}}}CrossIndustryInvoice")
# VD-Valitool-14: Der qdt-Namespace wird in dieser Instanz nicht benötigt, soll aber laut
# Spezifikation trotzdem am Root-Element deklariert werden.
root.set("xmlns:qdt", QDT)
ex_doc_ctx = ET.SubElement(root, f"{{{RSM}}}ExchangedDocumentContext") ex_doc_ctx = ET.SubElement(root, f"{{{RSM}}}ExchangedDocumentContext")
@ -484,7 +489,10 @@ def generate_compliant_zugferd_cii(data):
ET.SubElement(header_settle, f"{{{RAM}}}InvoiceCurrencyCode").text = "EUR" ET.SubElement(header_settle, f"{{{RAM}}}InvoiceCurrencyCode").text = "EUR"
pay_means = ET.SubElement(header_settle, f"{{{RAM}}}SpecifiedTradeSettlementPaymentMeans") pay_means = ET.SubElement(header_settle, f"{{{RAM}}}SpecifiedTradeSettlementPaymentMeans")
ET.SubElement(pay_means, f"{{{RAM}}}TypeCode").text = "30" # BT-81 Zahlungsart: Code 58 = SEPA-Überweisung. Code 30 ist für Überweisungen AUSSERHALB
# des SEPA-Raums gedacht (VD-Valitool-169-Hinweis) - da hier immer eine IBAN verwendet wird,
# ist 58 der korrekte Code.
ET.SubElement(pay_means, f"{{{RAM}}}TypeCode").text = "58"
payee_acc = ET.SubElement(pay_means, f"{{{RAM}}}PayeePartyCreditorFinancialAccount") payee_acc = ET.SubElement(pay_means, f"{{{RAM}}}PayeePartyCreditorFinancialAccount")
ET.SubElement(payee_acc, f"{{{RAM}}}IBANID").text = str(data["iban"]).replace(" ", "") ET.SubElement(payee_acc, f"{{{RAM}}}IBANID").text = str(data["iban"]).replace(" ", "")
@ -570,7 +578,7 @@ def fix_xrechnung_xmp_conformance(pdf_bytes: bytes) -> bytes:
Die factur-x-Bibliothek schreibt standardmäßig fx:ConformanceLevel = 'EN 16931' und Die factur-x-Bibliothek schreibt standardmäßig fx:ConformanceLevel = 'EN 16931' und
fx:Version = '1.0' in die XMP-Metadaten (generischer Factur-X-Standard). Da unsere CII-XML fx:Version = '1.0' in die XMP-Metadaten (generischer Factur-X-Standard). Da unsere CII-XML
aber explizit das KoSIT-XRechnung-Profil deklariert (GuidelineID .../xrechnung_3.0), erwarten aber explizit das KoSIT-XRechnung-Profil deklariert (GuidelineID .../xrechnung_3.0), erwarten
Validatoren hier stattdessen 'XRECHNUNG' und '3.0.2' - sonst schlagen z. B. Validatoren hier stattdessen 'XRECHNUNG' und '3.0' - sonst schlagen z. B.
VD-Valitool-H-022/H-024 fehl, weil PDF-Metadaten und eingebettetes XML nicht zusammenpassen. VD-Valitool-H-022/H-024 fehl, weil PDF-Metadaten und eingebettetes XML nicht zusammenpassen.
""" """
pdf = pikepdf.open(io.BytesIO(pdf_bytes)) pdf = pikepdf.open(io.BytesIO(pdf_bytes))
@ -583,7 +591,7 @@ def fix_xrechnung_xmp_conformance(pdf_bytes: bytes) -> bytes:
"<fx:ConformanceLevel>XRECHNUNG</fx:ConformanceLevel>", "<fx:ConformanceLevel>XRECHNUNG</fx:ConformanceLevel>",
).replace( ).replace(
"<fx:Version>1.0</fx:Version>", "<fx:Version>1.0</fx:Version>",
"<fx:Version>3.0.2</fx:Version>", "<fx:Version>3.0</fx:Version>",
) )
pdf.Root.Metadata.write(xmp_str.encode("utf-8")) pdf.Root.Metadata.write(xmp_str.encode("utf-8"))
buf = io.BytesIO() buf = io.BytesIO()