Das XML validiert jetzt als XRechnung für service-bw.de
Bernd Wurst

Bernd Wurst commited on 2024-05-17 15:17:16
Zeige 4 geänderte Dateien mit 31 Einfügungen und 7 Löschungen.

... ...
@@ -155,6 +155,7 @@ class Invoice(object):
155 155
         self.buyer = self.customer
156 156
         self.seller = {
157 157
             "name": None,  # juristischer Name
158
+            "contactPerson": None, # Name der Kontaktperson
158 159
             "trade_name": None,  # Firmenname
159 160
             "address": {
160 161
                 "postcode": None,
... ...
@@ -28,6 +28,10 @@ from drafthorse.pdf import attach_xml
28 28
 def InvoiceToXML(invoice):
29 29
     doc = Document()
30 30
     doc.context.guideline_parameter.id = "urn:cen.eu:en16931:2017"
31
+    if invoice.leitweg_id:
32
+        doc.context.guideline_parameter.id = "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0"
33
+    # Standardwert für XRechnung 3.0.1
34
+    doc.context.business_parameter.id = "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0"
31 35
     doc.header.id = invoice.id
32 36
     # Typecodes:
33 37
     # 380: Handelsrechnungen
... ...
@@ -58,6 +62,9 @@ def InvoiceToXML(invoice):
58 62
     doc.trade.agreement.seller.address.line_one = invoice.seller['address']['line1']
59 63
     doc.trade.agreement.seller.address.line_two = invoice.seller['address']['line2']
60 64
     doc.trade.agreement.seller.address.line_three = invoice.seller['address']['line3']
65
+    # Für XRECHNUNG muss das Contact-Array mindestens eine Angabe enthalten
66
+    if invoice.seller['contactPerson']:
67
+        doc.trade.agreement.seller.contact.person_name = invoice.seller['contactPerson']
61 68
     if invoice.seller['phone']:
62 69
         doc.trade.agreement.seller.contact.telephone.number = invoice.seller['phone']
63 70
     if invoice.seller_vat_id:
... ...
@@ -68,6 +75,7 @@ def InvoiceToXML(invoice):
68 75
         email = URIUniversalCommunication()
69 76
         email.uri_ID = ('EM', invoice.seller['email'])
70 77
         doc.trade.agreement.seller.electronic_address.add(email)
78
+        doc.trade.agreement.seller.contact.email.address = invoice.seller['email']
71 79
 
72 80
     # Buyer-Address
73 81
     doc.trade.agreement.buyer.name = invoice.customer['name']
... ...
@@ -77,10 +85,24 @@ def InvoiceToXML(invoice):
77 85
     doc.trade.agreement.buyer.address.line_one = invoice.customer['address']['line1']
78 86
     doc.trade.agreement.buyer.address.line_two = invoice.customer['address']['line2']
79 87
     doc.trade.agreement.buyer.address.line_three = invoice.customer['address']['line3']
88
+    if invoice.leitweg_id:
89
+        leitwegid = URIUniversalCommunication()
90
+        leitwegid.uri_ID = ('0204', invoice.leitweg_id)
91
+        doc.trade.agreement.buyer.electronic_address.add(leitwegid)
92
+    # Bei ZUGFeRD 2.1 darf das Feld nur einmal vorkommen. In XRechnung 3.0 darf es mehrmals drin sein.
93
+    # drafthorse kann aktuell noch kein XRechnung 3.0 erzeugen, daher hier als "elif".
94
+    elif invoice.buyer['email']:
95
+        email = URIUniversalCommunication()
96
+        email.uri_ID = ('EM', invoice.buyer['email'])
97
+        doc.trade.agreement.buyer.electronic_address.add(email)
80 98
 
81 99
     if invoice.buyer_reference:
82
-        # "Leitweg-ID" in XRechnung
83 100
         doc.trade.agreement.buyer_reference = invoice.buyer_reference
101
+    elif invoice.leitweg_id:
102
+        # "Leitweg-ID" in XRechnung
103
+        # Wenn wir hier sind, ist die aber bereits in der electronic_address eingetragen,
104
+        # daher hier nur noch wiederholen, wenn wir das Feld nicht für was anderes brauchen (Kunden-Referenz).
105
+        doc.trade.agreement.buyer_reference = invoice.leitweg_id
84 106
     if invoice.order_number:
85 107
         doc.trade.agreement.buyer_order.issuer_assigned_id = invoice.order_number
86 108
     if invoice.contract_number:
... ...
@@ -236,5 +258,5 @@ def InvoiceToXML(invoice):
236 258
     doc.trade.settlement.monetary_summation.due_amount = Decimal(f"{rest:.2f}")
237 259
 
238 260
     # Generate XML file
239
-    xml = doc.serialize(schema="FACTUR-X_EN16931")
261
+    xml = doc.serialize(schema="FACTUR-X_EXTENDED")
240 262
     return xml
... ...
@@ -43,10 +43,11 @@ if __name__ == '__main__':
43 43
     if 'vertragsnummer' in referenzen:
44 44
         invoice.contract_number = referenzen['vertragsnummer']
45 45
     invoice.seller['name'] = 'Bernd Wurst / Johannes Böck'
46
+    invoice.seller['contactPerson'] = 'Bernd Wurst / Johannes Böck'
46 47
     invoice.seller['trade_name'] = 'schokokeks.org GbR'
47
-    invoice.seller['email'] = 'xxxxxxx'
48
+    invoice.seller['email'] = 'xxx@example.com'
48 49
     invoice.seller['website'] = 'www.schokokeks.org'
49
-    invoice.seller['phone'] = None
50
+    invoice.seller['phone'] = '+49 123456789'
50 51
     invoice.seller['address']['country_id'] = 'DE'
51 52
     invoice.seller['address']['postcode'] = '71540'
52 53
     invoice.seller['address']['city_name'] = 'Murrhardt'
... ...
@@ -6,7 +6,7 @@ data = {
6 6
              'city': 'Murrhardt',
7 7
              'company': None,
8 8
              'country': 'DE',
9
-             'email': None,
9
+             'email': "kunde@kunde.de",
10 10
              'fax': None,
11 11
              'mobile': None,
12 12
              'name': 'Bernd Wurst',
... ...
@@ -43,6 +43,6 @@ data = {
43 43
               'mahnung': None,
44 44
               'notizen': None,
45 45
               'pdfdata': None,
46
-              'referenzen': '{"kundenreferenz": "KrfzNr", "bestellnummer": '
47
-                            '"08/15-BestNr4711"}',
46
+              'referenzen': '{"kundenreferenz": "KrfzNr", "vertragsnummer": "654321", "bestellnummer": '
47
+                            '"08/15-BestNr4711", "leitwegid": "012345678"}',
48 48
               'sepamandat': None}}
49 49