WiP
Bernd Wurst authored 5 months ago
|
1) # -*- coding: utf-8 -*-
2) # (C) 2011 by Bernd Wurst <bernd@schokokeks.org>
3)
4) # This file is part of Bib2011.
5) #
6) # Bib2011 is free software: you can redistribute it and/or modify
7) # it under the terms of the GNU General Public License as published by
8) # the Free Software Foundation, either version 3 of the License, or
9) # (at your option) any later version.
10) #
11) # Bib2011 is distributed in the hope that it will be useful,
12) # but WITHOUT ANY WARRANTY; without even the implied warranty of
13) # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14) # GNU General Public License for more details.
15) #
16) # You should have received a copy of the GNU General Public License
17) # along with Bib2011. If not, see <http://www.gnu.org/licenses/>.
18)
|
Kontoinhaber im XML einsetzen
Bernd Wurst authored 5 months ago
|
19) import os.path
20) import sys
|
WiP
Bernd Wurst authored 5 months ago
|
21) from decimal import Decimal
22)
23) # Search for included submodule python-drafthorse
24) atoms = os.path.abspath(os.path.dirname(__file__)).split('/')
25) dir = ''
26) while atoms:
27) candidate = os.path.join('/'.join(atoms), 'external/python-drafthorse')
28) if os.path.exists(candidate):
29) dir = candidate
30) break
31) atoms = atoms[:-1]
32) sys.path.insert(0, dir)
33) from drafthorse.models.document import Document
34) from drafthorse.models.accounting import ApplicableTradeTax
35) from drafthorse.models.tradelines import LineItem
|
Codestyle-Fixes
Bernd Wurst authored 5 months ago
|
36) from .InvoiceObjects import InvoiceTable, InvoiceText, RECHNUNG, GUTSCHRIFT, KORREKTUR, \
|
WiP
Bernd Wurst authored 5 months ago
|
37) VAT_REGULAR, VAT_KLEINUNTERNEHMER, VAT_INNERGEM, PAYMENT_UEBERWEISUNG, PAYMENT_LASTSCHRIFT
|
WiP
Bernd Wurst authored 5 months ago
|
38) from drafthorse.models.party import TaxRegistration, URIUniversalCommunication
39) from drafthorse.models.payment import PaymentTerms
40) from drafthorse.models.note import IncludedNote
41) from drafthorse.pdf import attach_xml
42)
43) def InvoiceToXML(invoice):
44) doc = Document()
|
Falscher ID-String
Bernd Wurst authored 4 months ago
|
45) doc.context.guideline_parameter.id = "urn:cen.eu:en16931:2017"
|
WiP
Bernd Wurst authored 5 months ago
|
46) doc.header.id = invoice.id
47) # Typecodes:
48) # 380: Handelsrechnungen
49) # 381: Gutschrift
50) # 384: Korrekturrechnung
51) # 389: Eigenrechnung (vom Käufer im Namen des Lieferanten erstellt).
52) # 261: Selbstverfasste Gutschrift.
53) # 386: Vorauszahlungsrechnung
54) # 326: Teilrechnung
55) # 751: Rechnungsinformation - KEINE RECHNUNG
56) if invoice.type == RECHNUNG:
57) doc.header.type_code = "380"
58) elif invoice.type == GUTSCHRIFT:
59) doc.header.type_code = "381"
60) elif invoice.type == KORREKTUR:
61) doc.header.type_code = "384"
62) else:
63) raise TypeError("Unbekannter Rechnungstyp, kann kein XML erstellen")
64) doc.header.issue_date_time = invoice.date
65)
66) # Seller-Address
67) if invoice.seller['trade_name']:
68) pass
69) # FIXME: specified_legal_organization ist in der Library nicht implementiert, pull request ist vorhanden
|
Kontoinhaber im XML einsetzen
Bernd Wurst authored 5 months ago
|
70) # doc.trade.agreement.seller.specified_legal_organization.trade_name = invoice.seller['trade_name']
|
WiP
Bernd Wurst authored 5 months ago
|
71) doc.trade.agreement.seller.name = invoice.seller['name']
72) doc.trade.agreement.seller.address.country_id = invoice.seller['address']['country_id']
73) doc.trade.agreement.seller.address.postcode = invoice.seller['address']['postcode']
74) doc.trade.agreement.seller.address.city_name = invoice.seller['address']['city_name']
75) doc.trade.agreement.seller.address.line_one = invoice.seller['address']['line1']
76) doc.trade.agreement.seller.address.line_two = invoice.seller['address']['line2']
77) doc.trade.agreement.seller.address.line_three = invoice.seller['address']['line3']
|
Telefonnummer
Bernd Wurst authored 4 months ago
|
78) if invoice.seller['phone']:
79) doc.trade.agreement.seller.contact.telephone.number = invoice.seller['phone']
|
WiP
Bernd Wurst authored 5 months ago
|
80) if invoice.seller_vat_id:
81) tax_reg = TaxRegistration()
82) tax_reg.id = ('VA', invoice.seller_vat_id)
83) doc.trade.agreement.seller.tax_registrations.add(tax_reg)
84) if invoice.seller['email']:
85) email = URIUniversalCommunication()
86) email.uri_ID = ('EM', invoice.seller['email'])
87) # FIXME: Typo in der Library ("adress")?
88) doc.trade.agreement.seller.electronic_adress.add(email)
89)
90) # Buyer-Address
91) doc.trade.agreement.buyer.name = invoice.customer['name']
92) doc.trade.agreement.buyer.address.country_id = invoice.customer['address']['country_id']
93) doc.trade.agreement.buyer.address.postcode = invoice.customer['address']['postcode']
94) doc.trade.agreement.buyer.address.city_name = invoice.customer['address']['city_name']
95) doc.trade.agreement.buyer.address.line_one = invoice.customer['address']['line1']
96) doc.trade.agreement.buyer.address.line_two = invoice.customer['address']['line2']
97) doc.trade.agreement.buyer.address.line_three = invoice.customer['address']['line3']
98)
|
Telefonnummer
Bernd Wurst authored 4 months ago
|
99) if invoice.buyer_reference:
100) # "Leitweg-ID" in XRechnung
101) doc.trade.agreement.buyer_reference = invoice.buyer_reference
102)
|
WiP
Bernd Wurst authored 5 months ago
|
103) # Line Items
104) summe_netto = 0.0
105) summe_brutto = 0.0
106) summe_bezahlt = 0.0
107) summe_ust = 0.0
108) line_id_count = 0
109) textparts = []
110) for part in invoice.parts:
|
Codestyle-Fixes
Bernd Wurst authored 5 months ago
|
111) if isinstance(part, InvoiceText):
|
WiP
Bernd Wurst authored 5 months ago
|
112) textparts += part.paragraphs
|
Codestyle-Fixes
Bernd Wurst authored 5 months ago
|
113) if isinstance(part, InvoiceTable):
|
WiP
Bernd Wurst authored 5 months ago
|
114) for el in part.entries:
115) line_id_count += 1
116) li = LineItem()
117) li.document.line_id = f"{line_id_count}"
118) li.product.name = el['subject']
119) if 'desc' in el and el['desc'] != '':
120) desc = li.product.description = el['desc']
121)
122) if 'period_start' in el and el['period_start']:
123) if 'period_end' in el and el['period_end']:
124) li.settlement.period.start = el['period_start']
125) li.settlement.period.end = el['period_end']
126) else:
127) li.delivery.event.occurrence = el['period_start']
128)
129) # FIXME: Hier sollte der passende Code benutzt werden (z.B. Monat)
|
Kleine Korrekturen, mache B...
Bernd Wurst authored 5 months ago
|
130) li.delivery.billed_quantity = (Decimal(el['count']), 'C62')
131) # C62 = ohne Einheit
132) # H87 = Stück
133) # MON = Month
|
WiP
Bernd Wurst authored 5 months ago
|
134) # LTR = Liter (1 dm3)
135) # KGM = Kilogram
136) # MTR = Meter
137) # TNE = Tonne
138)
139) li.settlement.trade_tax.type_code = "VAT"
140) if invoice.vat_type == VAT_REGULAR:
141) li.settlement.trade_tax.category_code = "S"
142) elif invoice.vat_type == VAT_KLEINUNTERNEHMER:
143) li.settlement.trade_tax.category_code = "E"
144) elif invoice.vat_type == VAT_INNERGEM:
145) li.settlement.trade_tax.category_code = "K"
146) # FIXME: Typ bei uns nur global gesetzt, nicht pro Artikel
147) # S = Standard VAT rate
148) # Z = Zero rated goods
149) # E = VAT exempt
150) # AE = Reverse charge
151) # K = Intra-Community supply (specific reverse charge)
152) # G = Exempt VAT for Export outside EU
153) # O = Outside VAT scope
|
Kontoinhaber im XML einsetzen
Bernd Wurst authored 5 months ago
|
154) li.settlement.trade_tax.rate_applicable_percent = Decimal(f"{el['vat'] * 100:.1f}")
|
WiP
Bernd Wurst authored 5 months ago
|
155)
156) nettopreis = el['price']
157) if part.vatType == 'gross':
158) nettopreis = el['price'] / (1 + el['vat'])
159) li.agreement.net.amount = Decimal(f"{nettopreis:.2f}")
160)
161) nettosumme = el['total']
162) if part.vatType == 'gross':
163) nettosumme = el['total'] / (1 + el['vat'])
164) li.settlement.monetary_summation.total_amount = Decimal(f"{nettosumme:.2f}")
165)
166) summe_netto += nettosumme
167) summe_brutto += el['total']
168) doc.trade.items.add(li)
169)
170) for pay in part.payments:
171) summe_bezahlt += pay['amount']
172)
173) for vat, vatdata in part.vat.items():
174) trade_tax = ApplicableTradeTax()
175) # Steuerbetrag dieses Steuersatzes
176) trade_tax.calculated_amount = Decimal(f"{(vatdata[0] / (vat + 1)) * vat:.2f}")
177) # Nettosumme dieses Steuersatzes
178) trade_tax.basis_amount = Decimal(f"{(vatdata[0] / (vat + 1)):.2f}")
179) trade_tax.type_code = "VAT"
180) if invoice.vat_type == VAT_REGULAR:
181) trade_tax.category_code = "S"
182) elif invoice.vat_type == VAT_KLEINUNTERNEHMER:
183) trade_tax.category_code = "E"
184) trade_tax.exemption_reason = 'Als Kleinunternehmer wird gemäß §19 UStG keine USt in Rechnung gestellt.'
185) elif invoice.vat_type == VAT_INNERGEM:
186) trade_tax.category_code = "K"
|
Kontoinhaber im XML einsetzen
Bernd Wurst authored 5 months ago
|
187) trade_tax.rate_applicable_percent = Decimal(f"{vat * 100:.1f}")
|
WiP
Bernd Wurst authored 5 months ago
|
188) summe_ust += (vatdata[0] / (vat + 1)) * vat
189) doc.trade.settlement.trade_tax.add(trade_tax)
190)
191) for paragraph in textparts:
192) note = IncludedNote()
193) note.content.add(paragraph)
194) doc.header.notes.add(note)
195)
196) rest = summe_brutto - summe_bezahlt
197)
198) if invoice.creditor_reference_id:
199) # Gläubiger-ID für SEPA
200) doc.trade.settlement.creditor_reference_id = invoice.creditor_reference_id
201) doc.trade.settlement.payment_reference = invoice.id
202) doc.trade.settlement.currency_code = 'EUR'
|
WiP
Bernd Wurst authored 5 months ago
|
203) if invoice.payment_type:
204) doc.trade.settlement.payment_means.type_code = invoice.payment_type
205)
206) if invoice.seller_bank_data['iban'] and invoice.payment_type == PAYMENT_UEBERWEISUNG:
|
Kontoinhaber im XML einsetzen
Bernd Wurst authored 5 months ago
|
207) doc.trade.settlement.payment_means.payee_account.account_name = \
208) invoice.seller_bank_data['kontoinhaber'] or invoice.seller['trade_name'] or invoice.seller['name']
|
WiP
Bernd Wurst authored 5 months ago
|
209) doc.trade.settlement.payment_means.payee_account.iban = invoice.seller_bank_data['iban']
|
Kleine Korrekturen, mache B...
Bernd Wurst authored 5 months ago
|
210) if invoice.seller_bank_data['bic']:
211) doc.trade.settlement.payment_means.payee_institution.bic = invoice.seller_bank_data['bic']
|
WiP
Bernd Wurst authored 5 months ago
|
212) if invoice.buyer_bank_data['iban'] and invoice.payment_type == PAYMENT_LASTSCHRIFT:
|
WiP
Bernd Wurst authored 5 months ago
|
213) # Kunden-Bankverbindung bei Lastschrift
214) doc.trade.settlement.payment_means.payer_account.iban = invoice.buyer_bank_data['iban']
215)
216) terms = PaymentTerms()
|
WiP
Bernd Wurst authored 5 months ago
|
217) if invoice.due_date and invoice.payment_type == PAYMENT_UEBERWEISUNG:
|
WiP
Bernd Wurst authored 5 months ago
|
218) terms.description = f"Bitte begleichen Sie den Betrag bis zum {invoice.due_date.strftime('%d.%m.%Y')} ohne Abzüge."
219) terms.due = invoice.due_date
|
Kleine Korrekturen, mache B...
Bernd Wurst authored 5 months ago
|
220) if invoice.type in [GUTSCHRIFT, KORREKTUR]:
|
WiP
Bernd Wurst authored 5 months ago
|
221) terms.description = f"Wir überweisen den Betrag auf Ihr Konto."
222) elif invoice.debit:
|
WiP
Bernd Wurst authored 5 months ago
|
223) if invoice.debit_mandate_id:
224) # Mandatsreferenz für Lastschrift
225) terms.debit_mandate_id = invoice.debit_mandate_id
226) terms.description = 'Wir buchen von Ihrem Konto ab.'
227) doc.trade.settlement.terms.add(terms)
228)
229) doc.trade.settlement.monetary_summation.line_total = Decimal(f"{summe_netto:.2f}")
230) doc.trade.settlement.monetary_summation.charge_total = Decimal("0.00")
231) doc.trade.settlement.monetary_summation.allowance_total = Decimal("0.00")
232) doc.trade.settlement.monetary_summation.tax_basis_total = Decimal(f"{summe_netto:.2f}")
233) doc.trade.settlement.monetary_summation.tax_total = (Decimal(f"{summe_ust:.2f}"), "EUR")
234) doc.trade.settlement.monetary_summation.prepaid_total = Decimal(f"{summe_bezahlt:.2f}")
235) doc.trade.settlement.monetary_summation.grand_total = Decimal(f"{summe_brutto:.2f}")
236) doc.trade.settlement.monetary_summation.due_amount = Decimal(f"{rest:.2f}")
237)
238) # Generate XML file
|
WiP
Bernd Wurst authored 5 months ago
|
239) xml = doc.serialize(schema="FACTUR-X_EN16931")
|