Feat: Entwurf bearbeiten (PATCH), erneut senden; SW by-v968
This commit is contained in:
parent
a2d089bce4
commit
b14a251bdc
5 changed files with 96 additions and 11 deletions
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue