e79099da6db77a21a6263970f80ebff113811a05
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

1) #!/usr/bin/python
2) 
3) import datetime
4) import json
5) import os
6) import subprocess
7) import sys
8) import tempfile
9) 
10) import qrcode
11) 
12) sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
13) 
14) from Invoice.InvoiceObjects import Invoice, RECHNUNG, InvoiceText, InvoiceTable, KORREKTUR, PAYMENT_LASTSCHRIFT, \
15)     InvoiceImage
16) from Invoice.InvoiceToPDF import InvoiceToPDF
17) from Invoice.InvoiceToText import InvoiceToText
18) from Invoice.InvoiceToZUGFeRD import InvoiceToXML, attach_xml
19) 
20) if __name__ == '__main__':
21)     import rechnungsdaten
22)     data = rechnungsdaten.data['data']
23)     rechnung = rechnungsdaten.data['rechnung']
24)     mandat = rechnungsdaten.data['mandat']
25)     adresse = rechnungsdaten.data['adresse']
26)     daten = rechnungsdaten.data['daten']
27) 
28)     rechnungsnummer = rechnung['id']
29)     filename = 'pdf/rechnung_%05i.pdf' % rechnungsnummer
30) 
31)     referenzen = json.loads(rechnung['referenzen'])
32) 
33)     from io import StringIO
34)     file = StringIO()
35)     invoice = Invoice()
36)     if 'leitwegid' in referenzen:
37)         # Behörden-Rechnung, als XRECHNUNG ausgeben!
38)         invoice.leitweg_id = referenzen['leitwegid']
39)     if 'kundenreferenz' in referenzen:
40)         invoice.buyer_reference = referenzen['kundenreferenz']
41)     if 'bestellnummer' in referenzen:
42)         invoice.order_number = referenzen['bestellnummer']
43)     if 'vertragsnummer' in referenzen:
44)         invoice.contract_number = referenzen['vertragsnummer']
45)     invoice.seller['name'] = 'Bernd Wurst / Johannes Böck'
Bernd Wurst Das XML validiert jetzt als...

Bernd Wurst authored 4 months ago

46)     invoice.seller['contactPerson'] = 'Bernd Wurst / Johannes Böck'
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

47)     invoice.seller['trade_name'] = 'schokokeks.org GbR'
Bernd Wurst Das XML validiert jetzt als...

Bernd Wurst authored 4 months ago

48)     invoice.seller['email'] = 'xxx@example.com'
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

49)     invoice.seller['website'] = 'www.schokokeks.org'
Bernd Wurst Das XML validiert jetzt als...

Bernd Wurst authored 4 months ago

50)     invoice.seller['phone'] = '+49 123456789'
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

51)     invoice.seller['address']['country_id'] = 'DE'
52)     invoice.seller['address']['postcode'] = '71540'
53)     invoice.seller['address']['city_name'] = 'Murrhardt'
54)     invoice.seller['address']['line1'] = 'Köchersberg 32'
55)     invoice.seller['address']['line2'] = None
56)     invoice.seller['address']['line3'] = None
57)     invoice.seller_vat_id = "DE255720588"
Bernd Wurst Käufer-E-Mail und -Telefon...

Bernd Wurst authored 1 month ago

58)     invoice.buyer_vat_id = "AT123456789"
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

59)     invoice.type = RECHNUNG
60)     invoice.logo_image_file = os.path.join(os.path.dirname(__file__), "logo.png")
61)     invoice.seller_bank_data['kontoinhaber'] = "schokokeks.org GbR"
62)     invoice.seller_bank_data['iban'] = "DE91602911200041512006"
63)     invoice.seller_bank_data['bic'] = "GENODES1VBK"
Bernd Wurst Änderungen an der Upstream-...

Bernd Wurst authored 6 months ago

64)     invoice.seller_bank_data['bankname'] = 'Volksbank Backnang'
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

65)     invoice.id = str(rechnungsnummer)
66)     invoice.customerno = rechnung['kunde']
67)     invoice.customer['name'] = adresse['company'] or adresse['name']
68)     invoice.customer['email'] = adresse['email']
Bernd Wurst Käufer-E-Mail und -Telefon...

Bernd Wurst authored 1 month ago

69)     invoice.customer['phone'] = adresse['phone']
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

70)     invoice.customer['address']['country_id'] = adresse['country']
71)     invoice.customer['address']['postcode'] = adresse['zip']
72)     invoice.customer['address']['city_name'] = adresse['city']
73)     lines = list(filter(None, [
74)         adresse['name'] if adresse['company'] else None,
75)     ] + adresse['address'].split('\n')))
76)     while len(lines) < 3:
77)         lines.append(None)
78)     invoice.customer['address']['line1'] = lines[0]
79)     invoice.customer['address']['line2'] = lines[1]
80)     invoice.customer['address']['line3'] = lines[2]
81) 
82)     invoice.setDate(rechnung['datum'])
83) 
84)     for item in data:
85)         if item['info_text']:
86)             text = InvoiceText(item['info_text'], urgent=True, headline=item['info_headline'])
87)             invoice.parts.append(text)
88) 
89)     vatType = 'net'
90)     if data[0]['brutto']:
91)         vatType = 'gross'  # Entscheidet anhand des ersten Eintrags ob die Rechnung Netto oder Brutto gestellt wird
92)     tab = InvoiceTable(vatType=vatType)
93) 
Bernd Wurst Zwischenüberschriften in de...

Bernd Wurst authored 7 months ago

94)     tab.addTitle("Zwischenüberschrift in der Tabelle")
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

95)     for item in data:
96)         # Der Betrag muss immer positiv sein, bei Gutschriften wird nur die Anzahl negativ.
97)         count = float(item['anzahl'])
98)         price = float(item['betrag'])
99)         if price < 0:
100)             count = -count
101)             price = abs(price)
102)         tab.addItem(
103)             {'count': count,
104)              'unit': item['einheit'],
105)              'subject': item['beschreibung'],
106)              'price': price,
107)              'vat': item['mwst'] / 100,
108)              'period_start': item['datum'],
109)              'period_end': item['enddatum'],
Bernd Wurst Neuer Parameter 'print_date...

Bernd Wurst authored 4 months ago

110)              #'print_date': False,
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

111)              }
112)         )
113)     invoice.parts.append(tab)
114) 
115)     if mandat:
116)         print('''Abbuchung von:
117)     Kontoinhaber: %s
118)     IBAN: %s
119)     BIC: %s
120)     Bank: %s
121) ''' % (mandat["kontoinhaber"], mandat["iban"], mandat["bic"], mandat["bankname"]))
122)         iban = mandat['iban']
123)         iban = iban[:8] + '*' * 10 + iban[-4:]
124)         pretty_iban = iban
125)         for i in range(len(iban) - (len(iban) % 4), 0, -4):
126)             pretty_iban = pretty_iban[:i] + ' ' + pretty_iban[i:]
127)         invoice.debit = True
128)         invoice.creditor_reference_id = mandat['glaeubiger_id']
129)         invoice.debit_mandate_id = mandat["mandatsreferenz"]
130)         invoice.buyer_bank_data['kontoinhaber'] = mandat['kontoinhaber']
131)         invoice.buyer_bank_data['iban'] = mandat['iban']
132)         invoice.buyer_bank_data['bic'] = mandat['bic']
133)         invoice.buyer_bank_data['bankname'] = mandat['bankname']
134)         text = None
135)         if tab.sum < 0:
136)             invoice.type = KORREKTUR
137)             invoice.title = 'Korrekturrechnung'
138)             text = InvoiceText("Der verbleibende Betrag wird in wenigen Tagen auf das uns bekannte Konto mit der IBAN %s bei der %s (BIC: %s) überwiesen." % (pretty_iban, mandat["bankname"], mandat["bic"]))
139)         else:
140)             invoice.payment_type = PAYMENT_LASTSCHRIFT
141)             text = InvoiceText("Der fällige Betrag wird gemäß dem von Ihnen erteilten Lastschrift-Mandat in wenigen Tagen vom Konto mit der IBAN %s bei der %s (BIC: %s) abgebucht. Diese Kontodaten beruhen auf dem Mandat Nr. %s vom %s. Unsere Gläubiger-ID lautet %s." % (pretty_iban, mandat["bankname"], mandat["bic"], mandat["mandatsreferenz"], mandat["erteilt"], mandat["glaeubiger_id"]))
142)         invoice.parts.append(text)
143)     else:
144)         if tab.sum < 0:
145)             invoice.type = KORREKTUR
146)             invoice.title = 'Korrekturrechnung'
147)             text = InvoiceText('Der verbleibende Gutschriftsbetrag wird Ihnen in Kürze überwiesen. Sollten Sie uns noch keine Bankverbindung mitgeteilt haben, so bitten wir um eine formlose Nachricht.')
148)             invoice.parts.append(text)
149)         else:
150)             invoice.due_date = datetime.date.today() + datetime.timedelta(days=14)
151)             text = InvoiceText('Bitte begleichen Sie diese Rechnung umgehend nach Erhalt ohne Abzüge auf das unten angegebene Konto. Geben Sie im Verwendungszweck Ihrer Überweisung bitte die Rechnungsnummer %s an, damit Ihre Buchung korrekt zugeordnet werden kann.' % rechnungsnummer)
152)             invoice.parts.append(text)
153)             qr = qrcode.QRCode(box_size=10, border=0, error_correction=qrcode.constants.ERROR_CORRECT_M)
154)             qr.add_data(f"BCD\n002\n1\nSCT\nGENODES1VBK\nschokokeks.org Hosting\nDE91602911200041512006\nEUR{tab.sum:.2f}\n\n\nRE {rechnungsnummer} vom {rechnung['datum']}")
155)             qr.make()
156)             img = qr.make_image()
157)             pilimage = img.get_image()
158)             imgpart = InvoiceImage(pilimage=pilimage, dpi=500, caption="Gerne können Sie diesen GiroCode für Ihre Banking-App verwenden.")
159)             invoice.parts.append(imgpart)
160) 
161)     text = InvoiceText('Wir danken Ihnen, dass Sie unser Angebot in Anspruch genommen haben und hoffen weiterhin auf eine gute Zusammenarbeit. Dieser Rechnung liegen die Allgemeinen Geschäftsbedingungen zum Zeitpunkt des Rechnungsdatums zugrunde, die Sie unter https://www.schokokeks.org/agb abrufen können.')
162)     invoice.parts.append(text)
163) 
Bernd Wurst new feature: indentation fo...

Bernd Wurst authored 7 months ago

164)     invoice.parts.append(InvoiceText('Das ist ein eingerückter Absatz mit etwas Text zum Testen der Line-Wrap-Funktion. Er hat auch eine Überschrift, um auch das zu zeigen.', headline='Schlussbemerkung', indent=2))
165) 
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

166)     textdata = InvoiceToText(invoice)
167)     pdfdata = InvoiceToPDF(invoice)
168)     xmldata = InvoiceToXML(invoice)
169)     if xmldata:
170)         try:
171)             # PDF/A-Konvertierung
172)             with (tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as tmp1,
173)                   tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as tmp2):
174)                 tmp1.write(pdfdata)
175)                 tmp1.close()
176)                 tmp2.close()
Bernd Wurst Ghostscript-Aufruf optimiert

Bernd Wurst authored 7 months ago

177)                 proc = subprocess.run(
178)                     f'gs -dPDFA=3 -sColorConversionStrategy=RGB -sDEVICE=pdfwrite -dPDFACompatibilityPolicy=1 -o {tmp2.name} {tmp1.name}',
179)                     capture_output=True, shell=True, check=True)
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

180)                 # Im Fehlerfall ?!
181)                 with open(tmp2.name, 'rb') as f:
182)                     pdfdata = f.read()
Bernd Wurst Änderungen an der Upstream-...

Bernd Wurst authored 6 months ago

183)         except Exception as e:
184)             print(e)
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

185)             # Wenn hier was schiefgeht, nutzen wir das alte PDF
186)             pass
187)         # FIXME: Die Metadaten sind noch nicht gültig, da scheint es einen Fehler in der drafthorse-Library zu geben
Bernd Wurst Änderungen an der Upstream-...

Bernd Wurst authored 6 months ago

188)         pdfdata = attach_xml(pdfdata, xmldata)
Bernd Wurst Test-Script lauffähig

Bernd Wurst authored 7 months ago

189) 
190)     f = open(filename, "wb")
191)     f.write(pdfdata)
192)     f.close()
193)     print(filename)
194)