Feat: Entwurf bearbeiten (PATCH), erneut senden; SW by-v968

This commit is contained in:
rene 2026-05-15 11:33:48 +02:00
parent a2d089bce4
commit b14a251bdc
5 changed files with 96 additions and 11 deletions

View file

@ -444,6 +444,58 @@ def get_invoice(invoice_id: int, admin=Depends(require_admin)):
return result
@router.patch("/{invoice_id}")
def update_invoice(invoice_id: int, data: InvoiceCreate, admin=Depends(require_admin)):
with db() as conn:
row = conn.execute("SELECT * FROM invoices WHERE id=?", (invoice_id,)).fetchone()
if not row:
raise HTTPException(404, "Rechnung nicht gefunden.")
if row["status"] != "draft":
raise HTTPException(400, "Nur Entwürfe können bearbeitet werden.")
if not data.items:
raise HTTPException(400, "Mindestens eine Position erforderlich.")
KLEINUNTERNEHMER = os.getenv("KLEINUNTERNEHMER", "true").lower() == "true"
TAX_RATE = 0.0 if KLEINUNTERNEHMER else float(os.getenv("RECHNUNG_MWST", "19"))
amount_net = round(sum(i.quantity * i.unit_price for i in data.items), 2)
discount_pct = data.discount_pct or 0.0
discount_amount = round(amount_net * discount_pct / 100, 2)
amount_after_discount = round(amount_net - discount_amount, 2)
tax_amount = round(amount_after_discount * TAX_RATE / 100, 2)
amount_gross = round(amount_after_discount + tax_amount, 2)
description = data.items[0].description if len(data.items) == 1 else f"{len(data.items)} Positionen"
conn.execute("""
UPDATE invoices SET
recipient_name=?, recipient_email=?, recipient_address=?,
description=?, service_period=?,
amount_net=?, discount_pct=?, discount_amount=?,
amount_after_discount=?, tax_rate=?, tax_amount=?, amount_gross=?,
notes=?
WHERE id=?
""", (
data.recipient_name, data.recipient_email, data.recipient_address,
description, data.service_period,
amount_net, discount_pct, discount_amount,
amount_after_discount, TAX_RATE, tax_amount, amount_gross,
data.notes, invoice_id,
))
conn.execute("DELETE FROM invoice_items WHERE invoice_id=?", (invoice_id,))
for item in data.items:
total = round(item.quantity * item.unit_price, 2)
conn.execute(
"INSERT INTO invoice_items (invoice_id, description, quantity, unit_price, total) VALUES (?,?,?,?,?)",
(invoice_id, item.description, item.quantity, item.unit_price, total)
)
row = conn.execute("SELECT * FROM invoices WHERE id=?", (invoice_id,)).fetchone()
items = _fetch_items(conn, invoice_id)
result = _row_to_dict(row)
result["items"] = items
return result
@router.post("", status_code=201)
def create_invoice(data: InvoiceCreate, admin=Depends(require_admin)):
if not data.items:
@ -501,6 +553,8 @@ async def send_invoice(invoice_id: int, admin=Depends(require_admin)):
raise HTTPException(404, "Rechnung nicht gefunden.")
if row["status"] == "cancelled":
raise HTTPException(400, "Stornierte Rechnung kann nicht gesendet werden.")
if row["status"] == "paid":
raise HTTPException(400, "Bezahlte Rechnung kann nicht erneut gesendet werden.")
items = _fetch_items(conn, invoice_id)
invoice = _row_to_dict(row)