incomplete: implement rounding
Bernd Wurst

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