adddition for the rechunung and the change for the HRB in the Impressum
This commit is contained in:
@@ -7,8 +7,24 @@ import os
|
||||
import tempfile
|
||||
from fpdf import FPDF
|
||||
from flask import current_app
|
||||
from weasyprint import HTML
|
||||
|
||||
|
||||
SELLER_INFO = {
|
||||
"company_name": "Invario UG",
|
||||
"sub_title": "Inventar- & Schulsoftware",
|
||||
"owner": "Invario Team",
|
||||
"street": "Am Sportplatz 10",
|
||||
"zip_city": "83052 Bruckmühl",
|
||||
"email": "info@invario-software.de",
|
||||
"website": "https://invario-software.de",
|
||||
"bank_name": "Commerzbank AG",
|
||||
"iban": "DE27 7114 0041 0183 3136 00",
|
||||
"bic": "COBADEFFXXX",
|
||||
"tax_number": "143/100/12345",
|
||||
"vat_id": "HRB 35441"
|
||||
}
|
||||
|
||||
def _build_smtp_client():
|
||||
smtp = smtplib.SMTP(
|
||||
"mail.invario-software.de",
|
||||
@@ -336,6 +352,458 @@ def generate_main_contract_pdf(
|
||||
pdf.output(output_path)
|
||||
return output_path
|
||||
|
||||
def generate_invoice_pdf(
|
||||
invoice_number: str,
|
||||
invoice_date: str,
|
||||
due_date: str,
|
||||
customer: dict,
|
||||
seller: dict,
|
||||
items: list,
|
||||
output_path: str,
|
||||
notes: str = None
|
||||
) -> str:
|
||||
"""
|
||||
Generiert eine professionelle PDF-Rechnung mit WeasyPrint.
|
||||
|
||||
:param invoice_number: Rechnungsnummer (z. B. "INV-20260827-100002")
|
||||
:param invoice_date: Rechnungsdatum (z. B. "27.08.2026")
|
||||
:param due_date: Fälligkeitsdatum (z. B. "10.09.2026")
|
||||
:param customer: Dict mit Kundeninformationen (name, attn, street, zip_city, customer_id)
|
||||
:param seller: Dict mit Verkäuferinformationen (company_name, street, zip_city, iban, bic, vat_id, etc.)
|
||||
:param items: Liste von Dicts mit Leistungspositionen (description, details, quantity, unit_price, tax_rate)
|
||||
:param output_path: Zielpfad für die generierte PDF-Datei
|
||||
:param notes: Optionaler Hinweistext (z. B. Zahlungsbedingungen oder Kleinunternehmerregelung)
|
||||
"""
|
||||
# 1. Summen und Steuern berechnen
|
||||
subtotal = 0.0
|
||||
tax_totals = {}
|
||||
|
||||
processed_items = []
|
||||
for item in items:
|
||||
qty = item.get("quantity", 1)
|
||||
unit_price = item.get("unit_price", 0.0)
|
||||
tax_rate = item.get("tax_rate", 19.0)
|
||||
|
||||
line_total = qty * unit_price
|
||||
subtotal += line_total
|
||||
|
||||
tax_amount = line_total * (tax_rate / 100.0)
|
||||
tax_totals[tax_rate] = tax_totals.get(tax_rate, 0.0) + tax_amount
|
||||
|
||||
processed_items.append({
|
||||
"pos": len(processed_items) + 1,
|
||||
"description": item.get("description", ""),
|
||||
"details": item.get("details", ""),
|
||||
"quantity": qty,
|
||||
"unit_price": f"{unit_price:,.2f}".replace(",", "X").replace(".", ",").replace("X", "."),
|
||||
"tax_rate": f"{tax_rate:.0f}%" if tax_rate.is_integer() else f"{tax_rate:.1f}%",
|
||||
"line_total": f"{line_total:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
|
||||
})
|
||||
|
||||
total_tax = sum(tax_totals.values())
|
||||
grand_total = subtotal + total_tax
|
||||
|
||||
formatted_subtotal = f"{subtotal:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
|
||||
formatted_grand_total = f"{grand_total:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
|
||||
|
||||
# Tabellenzeilen für Leistungen erzeugen
|
||||
items_html = ""
|
||||
for item in processed_items:
|
||||
details_html = f'<div class="item-details">{item["details"]}</div>' if item["details"] else ""
|
||||
items_html += f"""
|
||||
<tr>
|
||||
<td class="text-center">{item['pos']}</td>
|
||||
<td>
|
||||
<div class="item-title">{item['description']}</div>
|
||||
{details_html}
|
||||
</td>
|
||||
<td class="text-center">{item['quantity']}</td>
|
||||
<td class="text-right">{item['unit_price']} €</td>
|
||||
<td class="text-center">{item['tax_rate']}</td>
|
||||
<td class="text-right">{item['line_total']} €</td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
# Steuer-Aufschlüsselung erzeugen
|
||||
tax_breakdown_html = ""
|
||||
for rate, amt in tax_totals.items():
|
||||
rate_str = f"{rate:.0f}%" if rate.is_integer() else f"{rate:.1f}%"
|
||||
amt_str = f"{amt:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
|
||||
tax_breakdown_html += f"""
|
||||
<tr>
|
||||
<td class="summary-label">zzgl. MwSt. ({rate_str}):</td>
|
||||
<td class="summary-value">{amt_str} €</td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
notes_html = f'<div class="notes-box"><strong>Hinweis:</strong> {notes}</div>' if notes else ""
|
||||
|
||||
# HTML & CSS Template
|
||||
html_content = f"""<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Rechnung {invoice_number}</title>
|
||||
<style>
|
||||
@page {{
|
||||
size: A4;
|
||||
margin: 15mm 15mm 20mm 15mm;
|
||||
@bottom-right {{
|
||||
content: "Seite " counter(page) " von " counter(pages);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 8pt;
|
||||
color: #64748b;
|
||||
}}
|
||||
@bottom-left {{
|
||||
content: "{seller.get('company_name', '')} • {seller.get('website', '')}";
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 8pt;
|
||||
color: #64748b;
|
||||
}}
|
||||
}}
|
||||
|
||||
*, *::before, *::after {{
|
||||
box-sizing: border-box;
|
||||
}}
|
||||
|
||||
body {{
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 9.5pt;
|
||||
color: #1e293b;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}}
|
||||
|
||||
.header-table {{
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 25px;
|
||||
}}
|
||||
.header-table td {{ vertical-align: top; }}
|
||||
.logo-title {{
|
||||
font-size: 22pt;
|
||||
font-weight: bold;
|
||||
color: #0f172a;
|
||||
letter-spacing: -0.5px;
|
||||
}}
|
||||
.subtitle {{
|
||||
font-size: 9pt;
|
||||
color: #2563eb;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-top: 2px;
|
||||
}}
|
||||
.seller-info-top {{
|
||||
font-size: 8.5pt;
|
||||
color: #64748b;
|
||||
text-align: right;
|
||||
line-height: 1.4;
|
||||
}}
|
||||
|
||||
.meta-table {{
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 25px;
|
||||
}}
|
||||
.meta-table td {{ vertical-align: top; }}
|
||||
.sender-line {{
|
||||
font-size: 7.5pt;
|
||||
color: #64748b;
|
||||
text-decoration: underline;
|
||||
margin-bottom: 8px;
|
||||
}}
|
||||
.recipient-address {{
|
||||
font-size: 10pt;
|
||||
line-height: 1.4;
|
||||
color: #0f172a;
|
||||
}}
|
||||
|
||||
.invoice-details-card {{
|
||||
background-color: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
padding: 12px 16px;
|
||||
width: 230px;
|
||||
float: right;
|
||||
}}
|
||||
.invoice-details-table {{
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 8.5pt;
|
||||
}}
|
||||
.invoice-details-table td {{ padding: 3px 0; }}
|
||||
.invoice-details-table .label {{ color: #64748b; }}
|
||||
.invoice-details-table .value {{
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
color: #0f172a;
|
||||
}}
|
||||
|
||||
.doc-title {{
|
||||
font-size: 16pt;
|
||||
font-weight: bold;
|
||||
color: #0f172a;
|
||||
margin-bottom: 6px;
|
||||
}}
|
||||
.doc-intro {{
|
||||
font-size: 9.5pt;
|
||||
color: #334155;
|
||||
margin-bottom: 20px;
|
||||
}}
|
||||
|
||||
.items-table {{
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 20px;
|
||||
}}
|
||||
.items-table th {{
|
||||
background-color: #0f172a;
|
||||
color: #ffffff;
|
||||
font-size: 8.5pt;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 8px 10px;
|
||||
}}
|
||||
.items-table td {{
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
font-size: 9pt;
|
||||
vertical-align: top;
|
||||
}}
|
||||
.items-table tr:nth-child(even) td {{ background-color: #f8fafc; }}
|
||||
.item-title {{ font-weight: bold; color: #0f172a; }}
|
||||
.item-details {{ font-size: 8.5pt; color: #64748b; margin-top: 3px; }}
|
||||
|
||||
.text-center {{ text-align: center; }}
|
||||
.text-right {{ text-align: right; }}
|
||||
|
||||
.summary-wrapper {{
|
||||
width: 100%;
|
||||
margin-bottom: 25px;
|
||||
}}
|
||||
.summary-table {{
|
||||
width: 280px;
|
||||
margin-left: auto;
|
||||
border-collapse: collapse;
|
||||
font-size: 9.5pt;
|
||||
}}
|
||||
.summary-table td {{ padding: 4px 8px; }}
|
||||
.summary-label {{ text-align: right; color: #64748b; }}
|
||||
.summary-value {{ text-align: right; font-weight: bold; color: #0f172a; }}
|
||||
.grand-total-row td {{
|
||||
border-top: 2px solid #0f172a;
|
||||
border-bottom: 2px double #0f172a;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
font-size: 11pt;
|
||||
}}
|
||||
.grand-total-row .summary-label {{ color: #0f172a; font-weight: bold; }}
|
||||
.grand-total-row .summary-value {{ color: #2563eb; font-weight: bold; }}
|
||||
|
||||
.notes-box {{
|
||||
background-color: #f1f5f9;
|
||||
border-left: 3px solid #2563eb;
|
||||
padding: 10px 12px;
|
||||
font-size: 9pt;
|
||||
color: #334155;
|
||||
margin-bottom: 25px;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}}
|
||||
|
||||
.payment-info {{
|
||||
font-size: 9.5pt;
|
||||
color: #0f172a;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 35px;
|
||||
}}
|
||||
|
||||
.footer-grid {{
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-top: 1px solid #cbd5e1;
|
||||
padding-top: 15px;
|
||||
margin-top: 20px;
|
||||
}}
|
||||
.footer-grid td {{
|
||||
width: 33.33%;
|
||||
vertical-align: top;
|
||||
font-size: 8pt;
|
||||
color: #64748b;
|
||||
line-height: 1.4;
|
||||
}}
|
||||
.footer-heading {{
|
||||
font-weight: bold;
|
||||
color: #0f172a;
|
||||
margin-bottom: 4px;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<table class="header-table">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="logo-title">{seller.get('company_name', 'INVARIO')}</div>
|
||||
<div class="subtitle">{seller.get('sub_title', 'Software & Inventarsysteme')}</div>
|
||||
</td>
|
||||
<td class="seller-info-top">
|
||||
<strong>{seller.get('company_name', '')}</strong><br>
|
||||
{seller.get('street', '')}<br>
|
||||
{seller.get('zip_city', '')}<br>
|
||||
E-Mail: {seller.get('email', '')}<br>
|
||||
Web: {seller.get('website', '')}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="meta-table">
|
||||
<tr>
|
||||
<td style="width: 55%;">
|
||||
<div class="sender-line">
|
||||
{seller.get('company_name', '')} • {seller.get('street', '')} • {seller.get('zip_city', '')}
|
||||
</div>
|
||||
<div class="recipient-address">
|
||||
<strong>{customer.get('name', '')}</strong><br>
|
||||
{f"z. Hd. {customer['attn']}<br>" if customer.get('attn') else ""}
|
||||
{customer.get('street', '')}<br>
|
||||
{customer.get('zip_city', '')}
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 45%;">
|
||||
<div class="invoice-details-card">
|
||||
<table class="invoice-details-table">
|
||||
<tr>
|
||||
<td class="label">Rechnungs-Nr.:</td>
|
||||
<td class="value">{invoice_number}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Datum:</td>
|
||||
<td class="value">{invoice_date}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Fällig am:</td>
|
||||
<td class="value">{due_date}</td>
|
||||
</tr>
|
||||
{f'<tr><td class="label">Kunden-Nr.:</td><td class="value">{customer.get("customer_id")}</td></tr>' if customer.get("customer_id") else ''}
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="doc-title">Rechnung {invoice_number}</div>
|
||||
<div class="doc-intro">
|
||||
Vielen Dank für Ihren Auftrag. Wir stellen Ihnen die nachfolgend aufgeführten Leistungen in Rechnung:
|
||||
</div>
|
||||
|
||||
<table class="items-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 6%;" class="text-center">Pos.</th>
|
||||
<th style="width: 48%;">Beschreibung</th>
|
||||
<th style="width: 8%;" class="text-center">Menge</th>
|
||||
<th style="width: 14%;" class="text-right">Einzelpreis</th>
|
||||
<th style="width: 10%;" class="text-center">MwSt.</th>
|
||||
<th style="width: 14%;" class="text-right">Gesamt</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{items_html}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="summary-wrapper">
|
||||
<table class="summary-table">
|
||||
<tr>
|
||||
<td class="summary-label">Zwischensumme (netto):</td>
|
||||
<td class="summary-value">{formatted_subtotal} €</td>
|
||||
</tr>
|
||||
{tax_breakdown_html}
|
||||
<tr class="grand-total-row">
|
||||
<td class="summary-label">Gesamtbetrag:</td>
|
||||
<td class="summary-value">{formatted_grand_total} €</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{notes_html}
|
||||
|
||||
<div class="payment-info">
|
||||
Bitte überweisen Sie den Rechnungsbetrag von <strong>{formatted_grand_total} €</strong> bis zum <strong>{due_date}</strong> auf das unten stehende Bankkonto.<br>
|
||||
Verwendungszweck: <strong>Rechnung {invoice_number}</strong>
|
||||
</div>
|
||||
|
||||
<table class="footer-grid">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="footer-heading">Unternehmensangaben</div>
|
||||
{seller.get('company_name', '')}<br>
|
||||
Inhaber: {seller.get('owner', '')}<br>
|
||||
{seller.get('street', '')}<br>
|
||||
{seller.get('zip_city', '')}
|
||||
</td>
|
||||
<td>
|
||||
<div class="footer-heading">Bankverbindung</div>
|
||||
Bank: {seller.get('bank_name', '')}<br>
|
||||
IBAN: {seller.get('iban', '')}<br>
|
||||
BIC: {seller.get('bic', '')}<br>
|
||||
Verwendungszweck: {invoice_number}
|
||||
</td>
|
||||
<td>
|
||||
<div class="footer-heading">Steuerangaben</div>
|
||||
Steuernummer: {seller.get('tax_number', '')}<br>
|
||||
USt-IdNr.: {seller.get('vat_id', '')}<br>
|
||||
Amtsgericht: {seller.get('court', 'München')}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
# HTML zu PDF rendern
|
||||
HTML(string=html_content).write_pdf(output_path)
|
||||
return output_path
|
||||
|
||||
def generate_dynamic_invoice(school_name, address, price_val=250.00):
|
||||
"""Erzeugt eine dynamische Rechnungs-PDF für einen Neukunden."""
|
||||
now = datetime.now()
|
||||
inv_num = f"INV-{now.strftime('%Y%m%d')}-{now.strftime('%H%M%S')}"
|
||||
inv_date = now.strftime("%d.%m.%Y")
|
||||
due_date = (now + timedelta(days=14)).strftime("%d.%m.%Y")
|
||||
|
||||
customer = {
|
||||
"name": school_name,
|
||||
"street": address.split(',')[0] if ',' in address else address,
|
||||
"zip_city": address.split(',')[1].strip() if ',' in address else "",
|
||||
}
|
||||
|
||||
items = [
|
||||
{
|
||||
"description": f"Invario Inventarsystem – Jahreslizenz",
|
||||
"details": f"Softwarenutzung für {school_name} (12 Monate)",
|
||||
"quantity": 1,
|
||||
"unit_price": float(price_val),
|
||||
"tax_rate": 19.0
|
||||
}
|
||||
]
|
||||
|
||||
out_path = os.path.join(tempfile.gettempdir(), f"rechnung_{inv_num}.pdf")
|
||||
generate_invoice_pdf(
|
||||
invoice_number=inv_num,
|
||||
invoice_date=inv_date,
|
||||
due_date=due_date,
|
||||
customer=customer,
|
||||
seller=SELLER_INFO,
|
||||
items=items,
|
||||
output_path=out_path,
|
||||
notes="Zahlbar innerhalb von 14 Tagen ohne Abzug."
|
||||
)
|
||||
return out_path
|
||||
|
||||
def send_accreditation_email(
|
||||
recipient: str,
|
||||
domain: str,
|
||||
@@ -348,7 +816,7 @@ def send_accreditation_email(
|
||||
software_name: str = "Invario Inventarsystem",
|
||||
invoice_path: str = None
|
||||
) -> bool:
|
||||
"""Generiert den Hauptvertrag temporär und versendet ihn zusammen mit Rechnung, AGB & AVV per E-Mail."""
|
||||
"""Generiert den Hauptvertrag und die Rechnung temporär und versendet sie zusammen mit AGB & AVV per E-Mail."""
|
||||
|
||||
# 1. Hauptvertrag dynamisch erzeugen
|
||||
safe_school_name = "".join([c for c in school_name if c.isalnum() or c in (' ', '_', '-')]).rstrip()
|
||||
@@ -364,6 +832,24 @@ def send_accreditation_email(
|
||||
output_path=contract_pdf_path
|
||||
)
|
||||
|
||||
# 1b. Rechnung dynamisch erzeugen (falls nicht direkt übergeben)
|
||||
generated_invoice_path = None
|
||||
if not invoice_path:
|
||||
# Konvertiert "250,00" zu 250.00 für die Rechnungsfunktion
|
||||
try:
|
||||
price_float = float(price.replace('.', '').replace(',', '.'))
|
||||
except ValueError:
|
||||
price_float = 250.00
|
||||
|
||||
generated_invoice_path = generate_dynamic_invoice(
|
||||
school_name=school_name,
|
||||
address=address,
|
||||
price_val=price_float
|
||||
)
|
||||
invoice_to_attach = generated_invoice_path
|
||||
else:
|
||||
invoice_to_attach = invoice_path
|
||||
|
||||
# 2. Statischen Ordner aus Flask dynamisch ermitteln (verhindert Pfadfehler im Container)
|
||||
if current_app:
|
||||
static_dir = current_app.static_folder
|
||||
@@ -386,10 +872,6 @@ def send_accreditation_email(
|
||||
agb_pdf_path = get_valid_pdf_path("AGB Invario.pdf")
|
||||
avv_pdf_path = get_valid_pdf_path("AVV Invario.pdf")
|
||||
|
||||
# Standard-Musterrechnung suchen, falls kein Pfad explizit übergeben wurde
|
||||
if not invoice_path:
|
||||
invoice_path = get_valid_pdf_path("rechnung_INV-20260730-100002-577983.pdf")
|
||||
|
||||
# 3. E-Mail Inhalte
|
||||
subject = "Ihre Zugangsdaten und Unterlagen für Invario"
|
||||
|
||||
@@ -409,6 +891,7 @@ def send_accreditation_email(
|
||||
<h2 style="color: #2c3e50; margin-top: 0;">Willkommen bei Invario!</h2>
|
||||
<p style="font-size: 15px; line-height: 1.6;">
|
||||
Vielen Dank für Ihre Akkreditierung. Im Anhang finden Sie Ihren Hauptvertrag, Ihre Rechnung sowie AGB und AVV.
|
||||
Bitte laden Sie die Dokumente herunter und unterschreiben Sie den Hauptvertrag. Danach können Sie die Unterlagen per E-Mail an uns zurücksenden oder über das Invario-Portal hochladen, um die Akkreditierung abzuschließen.
|
||||
</p>
|
||||
|
||||
<h3 style="color: #2c3e50;">Ihre Zugangsdaten:</h3>
|
||||
@@ -430,10 +913,10 @@ def send_accreditation_email(
|
||||
if os.path.exists(contract_pdf_path):
|
||||
attachments.append(contract_pdf_path)
|
||||
|
||||
if invoice_path and os.path.exists(invoice_path):
|
||||
attachments.append(invoice_path)
|
||||
if invoice_to_attach and os.path.exists(invoice_to_attach):
|
||||
attachments.append(invoice_to_attach)
|
||||
else:
|
||||
print("[WARNING] Keine Rechnungs-PDF gefunden.")
|
||||
print(f"[WARNING] Rechnung unter {invoice_to_attach} nicht gefunden.")
|
||||
|
||||
if agb_pdf_path:
|
||||
attachments.append(agb_pdf_path)
|
||||
@@ -445,13 +928,21 @@ def send_accreditation_email(
|
||||
else:
|
||||
print(f"[WARNING] AVV unter static/(downloads) nicht gefunden.")
|
||||
|
||||
# 5. E-Mail versenden & temporären Vertrag aufräumen
|
||||
# 5. E-Mail versenden & temporäre Dateien aufräumen
|
||||
try:
|
||||
success = send(recipient, subject, text_body=text_note, html_body=html_note, attachments=attachments)
|
||||
return success
|
||||
finally:
|
||||
# Hauptvertrag löschen
|
||||
if os.path.exists(contract_pdf_path):
|
||||
try:
|
||||
os.remove(contract_pdf_path)
|
||||
except OSError as e:
|
||||
print(f"[WARNING] Konnte temporären Vertrag nicht löschen: {e}")
|
||||
print(f"[WARNING] Konnte temporären Vertrag nicht löschen: {e}")
|
||||
|
||||
# Temporär erstellte Rechnung löschen
|
||||
if generated_invoice_path and os.path.exists(generated_invoice_path):
|
||||
try:
|
||||
os.remove(generated_invoice_path)
|
||||
except OSError as e:
|
||||
print(f"[WARNING] Konnte temporäre Rechnung nicht löschen: {e}")
|
||||
@@ -29,7 +29,7 @@
|
||||
<h2>2. Registergericht und Registernummer</h2>
|
||||
<p>
|
||||
<strong>Registergericht:</strong> Amtsgericht Traunstein<br>
|
||||
<strong>Registernummer:</strong> HRB 123456<br>
|
||||
<strong>Registernummer:</strong> HRB 35441<br>
|
||||
<strong>USt-ID:</strong> DE 123 456 789
|
||||
</p>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user