# -*- coding: utf-8 -*- """Den spiegelbildlichen Storno zu einer Rechnung rechnen.""" import berechnung def storno_rechnung_bauen(meta, cfg): """Baut aus den Kenndaten der alten Rechnung den spiegelbildlichen Storno. Alle Betraege negativ. Je USt-Satz eine Zeile - die einzelnen Leistungen der alten Rechnung werden NICHT wiederholt: aufgehoben wird der Beleg als Ganzes, und die Positionen stehen in aelteren PDFs gar nicht in den Kenndaten. """ r = berechnung.Rechnung() nummer = meta.get("rechnungsnummer", "") bloecke = meta.get("ust_bloecke") or [] if not bloecke: # v1-PDF ohne Aufteilung: alles auf den ermaessigten Satz, sonst faellt # die USt unter den Tisch. brutto = float(meta.get("zwischensumme") or 0) # Der erste Satz OHNE "extra_blatt" - also ein echter USt-Satz. Nimmt man # blind den ersten aus der Liste, rechnet ein umsortierter Katalog die # Steuer mit 19 % oder der Beherbergungssteuer heraus. echte = [s for s in cfg.get("steuersaetze", []) if not s.get("extra_blatt")] satz = int((echte or [{"satz": 7}])[0].get("satz", 7)) netto = berechnung.runde(brutto * 100 / (100 + satz)) if satz else brutto bloecke = [{"satz": satz, "netto": netto, "ust": berechnung.runde(brutto - netto), "brutto": brutto}] saetze = {int(s.get("satz", -1)): s for s in cfg.get("steuersaetze", [])} for i, b in enumerate(bloecke, start=1): satz = int(float(b.get("satz", 0) or 0)) brutto = -abs(berechnung.runde(b.get("brutto") or 0)) netto = -abs(berechnung.runde(b.get("netto") or 0)) ust = -abs(berechnung.runde(b.get("ust") or 0)) r.zeilen.append(berechnung.PositionsZeile( nr=i, leistung=f"Storno der Rechnung {nummer} ({satz} % USt)", satz=satz, preis=brutto, anzahl=1, personen=0, pro_person=False, brutto=brutto, netto=netto)) info = saetze.get(satz, {}) r.bloecke.append(berechnung.Steuerblock( satz=satz, netto=netto, ust=ust, bezeichnung=str(info.get("bezeichnung", "") or "Mehrwertsteuer"), extra_blatt=bool(info.get("extra_blatt", False)))) r.netto_gesamt = berechnung.runde(sum(b.netto for b in r.bloecke)) r.ust_gesamt = berechnung.runde(sum(b.ust for b in r.bloecke)) r.brutto = berechnung.runde(r.netto_gesamt + r.ust_gesamt) # Beherbergungssteuer wird mitstorniert, sonst stimmt die Meldung ans Amt nicht. # Die Bezeichnung kommt aus dem Satz mit "extra_blatt" - genau dem, aus dem # berechne() den Aufschlag baut. steuer = berechnung.runde(meta.get("steuer_betrag") or 0) if steuer: extra = next((s for s in cfg.get("steuersaetze", []) if s.get("extra_blatt")), {}) r.aufschlaege.append(berechnung.Aufschlag( satz=float(meta.get("steuer_satz") or extra.get("satz", 0) or 0), bezeichnung=str(extra.get("bezeichnung", "") or "Beherbergungssteuer"), basis=r.brutto, betrag=-abs(steuer), extra_blatt=True)) r.gesamt = berechnung.runde(r.brutto + sum(a.betrag for a in r.aufschlaege)) return r