diff --git a/backend/routes/invoices.py b/backend/routes/invoices.py index 66a6d63..dd71609 100644 --- a/backend/routes/invoices.py +++ b/backend/routes/invoices.py @@ -599,7 +599,7 @@ def pay_invoice(invoice_id: int, data: PayBody, admin=Depends(require_admin)): @router.post("/{invoice_id}/cancel") -def cancel_invoice(invoice_id: int, data: CancelBody, admin=Depends(require_admin)): +async def cancel_invoice(invoice_id: int, data: CancelBody, admin=Depends(require_admin)): with db() as conn: row = conn.execute("SELECT * FROM invoices WHERE id=?", (invoice_id,)).fetchone() if not row: @@ -613,4 +613,66 @@ def cancel_invoice(invoice_id: int, data: CancelBody, admin=Depends(require_admi (now, data.reason, cancellation_number, invoice_id) ) row = conn.execute("SELECT * FROM invoices WHERE id=?", (invoice_id,)).fetchone() - return _row_to_dict(row) + items = _fetch_items(conn, invoice_id) + + invoice = _row_to_dict(row) + + # Storno-PDF: invoice-Dict als Stornobeleg aufbereiten + storno_invoice = dict(invoice) + storno_invoice["invoice_number"] = cancellation_number + storno_invoice["notes"] = ( + f"Stornierung zu Rechnung {invoice['invoice_number']}\n" + f"Grund: {data.reason}" + ) + storno_invoice["amount_net"] = -invoice["amount_net"] + storno_invoice["discount_amount"] = -invoice.get("discount_amount", 0) + storno_invoice["amount_after_discount"] = -invoice["amount_after_discount"] + storno_invoice["tax_amount"] = -invoice.get("tax_amount", 0) + storno_invoice["amount_gross"] = -invoice["amount_gross"] + for item in items: + item["unit_price"] = -item["unit_price"] + item["total"] = -item["total"] + + try: + pdf_bytes = _generate_pdf(storno_invoice, items) + except Exception as e: + logger.error(f"Storno-PDF fehlgeschlagen: {e}") + return _row_to_dict(row) + + filename = f"{cancellation_number}_banyaro.pdf" + + try: + await _save_to_paperless(pdf_bytes, cancellation_number, filename) + except Exception as e: + logger.warning(f"Storno Paperless fehlgeschlagen: {e}") + + # Mail an Kunden + try: + body_html = mailer.email_html(f""" +

Hallo {invoice['recipient_name']},

+

+ Ihre Rechnung {invoice['invoice_number']} wurde storniert + (Stornonummer: {cancellation_number}). +

+

Grund: {data.reason}

+

+ Das Stornodokument liegt diesem Schreiben bei. +

+ """) + plain = ( + f"Hallo {invoice['recipient_name']},\n\n" + f"Ihre Rechnung {invoice['invoice_number']} wurde storniert " + f"(Stornonummer: {cancellation_number}).\n" + f"Grund: {data.reason}\n" + ) + await mailer.send_email( + to=invoice["recipient_email"], + subject=f"Stornierung Rechnung {invoice['invoice_number']} — Ban Yaro", + html=body_html, + plain=plain, + attachments=[{"filename": filename, "content": pdf_bytes, "content_type": "application/pdf"}], + ) + except Exception as e: + logger.error(f"Storno-Mail fehlgeschlagen: {e}") + + return invoice diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml index d9ef97e..38b4182 100644 --- a/docker-compose.staging.yml +++ b/docker-compose.staging.yml @@ -8,6 +8,7 @@ services: volumes: - ./data:/data - /volume1/docker/banyaro/data/media:/prod-media:ro + - /volume1/scaninput:/scaninput env_file: - .env environment: