Bernd Wurst commited on 2025-03-12 11:43:24
Zeige 3 geänderte Dateien mit 28 Einfügungen und 14 Löschungen.
... | ... |
@@ -47,8 +47,8 @@ class InvoiceTable(object): |
47 | 47 |
'count': e['count'], |
48 | 48 |
'unit': e['unit'], |
49 | 49 |
'subject': e['subject'], |
50 |
- 'price': Decimal(e['price']), |
|
51 |
- 'total': Decimal(e['price'] * e['count']), |
|
50 |
+ 'price': e['price'], |
|
51 |
+ 'total': e['price'] * e['count'], |
|
52 | 52 |
'vat': Decimal(e['vat']), |
53 | 53 |
'tender': False, |
54 | 54 |
'print_date': True, |
... | ... |
@@ -149,9 +149,14 @@ def InvoiceToXML(invoice): |
149 | 149 |
|
150 | 150 |
# Line Items |
151 | 151 |
summe_netto = Decimal('0.0') |
152 |
+ summe_brutto_rechnerisch = Decimal('0.0') |
|
152 | 153 |
summe_brutto = Decimal('0.0') |
153 | 154 |
summe_bezahlt = Decimal('0.0') |
154 | 155 |
summe_ust = Decimal('0.0') |
156 |
+ summe_rundung = Decimal('0.0') |
|
157 |
+ # Problem: Die Rechnung muss netto gestellt werden. Wenn wir die Rechnungsposten brutto erfassen, ergibt sich ein Rundungsfehler. |
|
158 |
+ # Daher konvertieren wir die Line-Items in stimmige Netto-Items und erfassen für jeden Posten einen Rundungsbetrag, |
|
159 |
+ # um den wir abgewichen sind. |
|
155 | 160 |
line_id_count = 0 |
156 | 161 |
textparts = [] |
157 | 162 |
for part in invoice.parts: |
... | ... |
@@ -160,6 +165,9 @@ def InvoiceToXML(invoice): |
160 | 165 |
if isinstance(part, InvoiceTable): |
161 | 166 |
last_title = None |
162 | 167 |
for el in part.entries: |
168 |
+ from pprint import pprint |
|
169 |
+ pprint(el) |
|
170 |
+ |
|
163 | 171 |
if el['type'] == 'title': |
164 | 172 |
# Diese Information ist im XML nicht auf diese Weise darstellbar |
165 | 173 |
last_title = el['title'] |
... | ... |
@@ -186,7 +194,7 @@ def InvoiceToXML(invoice): |
186 | 194 |
for key, value in UNITS.items(): |
187 | 195 |
if el['unit'] in value: |
188 | 196 |
unit = key |
189 |
- li.delivery.billed_quantity = (Decimal(el['count']), unit) |
|
197 |
+ li.delivery.billed_quantity = (Decimal(f"{el['count']:.4f}"), unit) |
|
190 | 198 |
# C62 = ohne Einheit |
191 | 199 |
# H87 = Stück |
192 | 200 |
# MON = Month, SEC = second, MIN = minute, HUR = hour, DAY = day, WEE = week, ANN = year |
... | ... |
@@ -211,23 +219,25 @@ def InvoiceToXML(invoice): |
211 | 219 |
# K = Intra-Community supply (specific reverse charge) |
212 | 220 |
# G = Exempt VAT for Export outside EU |
213 | 221 |
# O = Outside VAT scope |
214 |
- li.settlement.trade_tax.rate_applicable_percent = Decimal(f"{el['vat'] * 100:.1f}") |
|
222 |
+ li.settlement.trade_tax.rate_applicable_percent = Decimal(f"{el['vat'] * 100:.2f}") |
|
215 | 223 |
|
216 | 224 |
nettopreis = el['price'] |
217 | 225 |
if part.vatType == 'gross': |
218 |
- nettopreis = el['price'] / (1 + el['vat']) |
|
219 |
- li.agreement.net.amount = Decimal(f"{nettopreis:.2f}") |
|
226 |
+ nettopreis = el['price'] / (el['vat'] + 1) |
|
227 |
+ nettopreis = round(nettopreis, 2) |
|
228 |
+ li.agreement.net.amount = nettopreis |
|
220 | 229 |
|
221 |
- nettosumme = el['total'] |
|
222 |
- if part.vatType == 'gross': |
|
223 |
- nettosumme = el['total'] / (1 + el['vat']) |
|
230 |
+ nettosumme = nettopreis * el['count'] |
|
224 | 231 |
li.settlement.monetary_summation.total_amount = Decimal(f"{nettosumme:.2f}") |
225 | 232 |
|
233 |
+ brutto_rechnerisch = round(nettosumme * (el['vat'] + 1), 2) |
|
226 | 234 |
summe_netto += nettosumme |
227 | 235 |
if part.vatType == 'net': |
228 |
- summe_brutto += el['total'] * (1 + el['vat']) |
|
236 |
+ summe_brutto += el['total'] * (el['vat'] + 1) |
|
229 | 237 |
else: |
230 | 238 |
summe_brutto += el['total'] |
239 |
+ summe_brutto_rechnerisch += brutto_rechnerisch |
|
240 |
+ summe_rundung += (el['total'] - brutto_rechnerisch) |
|
231 | 241 |
doc.trade.items.add(li) |
232 | 242 |
|
233 | 243 |
for pay in part.payments: |
... | ... |
@@ -265,7 +275,6 @@ def InvoiceToXML(invoice): |
265 | 275 |
note.content.add(paragraph) |
266 | 276 |
doc.header.notes.add(note) |
267 | 277 |
|
268 |
- rest = summe_brutto - summe_bezahlt |
|
269 | 278 |
|
270 | 279 |
if invoice.creditor_reference_id: |
271 | 280 |
# Gläubiger-ID für SEPA |
... | ... |
@@ -298,13 +307,18 @@ def InvoiceToXML(invoice): |
298 | 307 |
terms.description = 'Wir buchen von Ihrem Konto ab.' |
299 | 308 |
doc.trade.settlement.terms.add(terms) |
300 | 309 |
|
310 |
+ summe_brutto = round(summe_brutto, 2) |
|
311 |
+ rest = summe_brutto - summe_bezahlt |
|
312 |
+ summe_netto = round(summe_netto, 2) |
|
313 |
+ summe_ust = round(summe_ust, 2) |
|
301 | 314 |
doc.trade.settlement.monetary_summation.line_total = Decimal(f"{summe_netto:.2f}") |
302 | 315 |
doc.trade.settlement.monetary_summation.charge_total = Decimal("0.00") |
303 | 316 |
doc.trade.settlement.monetary_summation.allowance_total = Decimal("0.00") |
304 | 317 |
doc.trade.settlement.monetary_summation.tax_basis_total = Decimal(f"{summe_netto:.2f}") |
305 | 318 |
doc.trade.settlement.monetary_summation.tax_total = (Decimal(f"{summe_ust:.2f}"), "EUR") |
319 |
+ doc.trade.settlement.monetary_summation.grand_total = Decimal(f"{summe_brutto_rechnerisch:.2f}") |
|
306 | 320 |
doc.trade.settlement.monetary_summation.prepaid_total = Decimal(f"{summe_bezahlt:.2f}") |
307 |
- doc.trade.settlement.monetary_summation.grand_total = Decimal(f"{summe_brutto:.2f}") |
|
321 |
+ doc.trade.settlement.monetary_summation.rounding_amount = summe_rundung |
|
308 | 322 |
doc.trade.settlement.monetary_summation.due_amount = Decimal(f"{rest:.2f}") |
309 | 323 |
|
310 | 324 |
# Generate XML file |
... | ... |
@@ -94,8 +94,8 @@ if __name__ == '__main__': |
94 | 94 |
tab.addTitle("Zwischenüberschrift in der Tabelle") |
95 | 95 |
for item in data: |
96 | 96 |
# Der Betrag muss immer positiv sein, bei Gutschriften wird nur die Anzahl negativ. |
97 |
- count = float(item['anzahl']) |
|
98 |
- price = float(item['betrag']) |
|
97 |
+ count = item['anzahl'] |
|
98 |
+ price = item['betrag'] |
|
99 | 99 |
if price < 0: |
100 | 100 |
count = -count |
101 | 101 |
price = abs(price) |
102 | 102 |