fix: Style-Namen repariert, Qualitätsversprechen & Investitionslogik verbessert

- Kaputte Style-Namen (Umlaute gefehlt) korrigiert
- Hannover-Datum: Fix für mehrzeilige Runs
- Neue optionale Sektion: Erwartungshaltung & Investitionslogik
- Qualitätsversprechen näher am Original-Wortlaut
- Schlussbestimmung-Heading wird korrekt blau formatiert

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-12 15:41:06 +02:00
parent cee643e7ab
commit 8930a3bc45

View File

@@ -154,10 +154,9 @@ def make_heading(text):
def make_body(text, bold=False, italic=False): def make_body(text, bold=False, italic=False):
"""Standard-Fließtext mit Zeilenabstand.""" """Standard-Fließtext Zeilenabstand kommt vom Style."""
return make_paragraph(text, bold=bold, italic=italic, return make_paragraph(text, bold=bold, italic=italic,
space_before=SP_BODY_BEFORE, space_after=SP_BODY_AFTER, space_before=SP_BODY_BEFORE, space_after=SP_BODY_AFTER)
line_spacing=LINE_SPACING)
def make_empty(): def make_empty():
@@ -284,11 +283,18 @@ def generate_angebot(data: dict, output_path: str):
} }
datum = f"{now.day}. {monatsnamen[now.month]} {now.year}" datum = f"{now.day}. {monatsnamen[now.month]} {now.year}"
# Find the text run with "Hannover" in paragraph 0 # Find the text run with "Hannover" in paragraph 0 and replace date
# The date may span multiple runs, so clear all runs after "Hannover" too
found_hannover = False
for run in p0.runs: for run in p0.runs:
if "Hannover" in (run.text or ""): if "Hannover" in (run.text or ""):
run.text = f" Hannover, {datum}" run.text = f" Hannover, {datum}"
break found_hannover = True
continue
if found_hannover and run.text and not run._element.findall('.//' + qn('w:drawing')):
# Clear remaining text runs after Hannover (old date fragments)
# but don't touch runs with images
run.text = ""
# === 3. AKTENZEICHEN AKTUALISIEREN === # === 3. AKTENZEICHEN AKTUALISIEREN ===
angebotsnummer = data.get("angebotsnummer") angebotsnummer = data.get("angebotsnummer")
@@ -405,6 +411,19 @@ def generate_angebot(data: dict, output_path: str):
]): ]):
new_elements.append(make_body(line)) new_elements.append(make_body(line))
# Erwartungshaltung & Investitionslogik (optional)
if data.get("investitionslogik"):
new_elements.append(make_empty())
il = data["investitionslogik"]
new_elements.append(make_heading(il.get("ueberschrift", "Erwartungshaltung & Investitionslogik")))
new_elements.append(make_empty())
for block in il.get("inhalt", []):
typ = block["typ"]
if typ == "text":
new_elements.append(make_body(block["text"]))
elif typ == "text_bold":
new_elements.append(make_body(block["text"], bold=True))
# Durchführung # Durchführung
if data.get("berater"): if data.get("berater"):
new_elements.append(make_empty()) new_elements.append(make_empty())
@@ -412,21 +431,24 @@ def generate_angebot(data: dict, output_path: str):
new_elements.append(make_empty()) new_elements.append(make_empty())
new_elements.append(make_body(data["berater"])) new_elements.append(make_body(data["berater"]))
# V&S Qualitätsversprechen # V&S Qualitätsversprechen (Originaltext aus Referenz-Angeboten)
kunde_formal = kunde.get("firma_formal", kunde["firma"]) kunde_formal = kunde.get("firma_formal", kunde["firma"])
kunde_firma = kunde["firma"]
laufzeit = data.get("laufzeit", "12 Monate") laufzeit = data.get("laufzeit", "12 Monate")
new_elements.append(make_empty()) new_elements.append(make_empty())
new_elements.append(make_heading("V&S Qualitätsversprechen")) new_elements.append(make_heading("V&S Qualitätsversprechen"))
new_elements.append(make_empty()) new_elements.append(make_empty())
new_elements.append(make_body( new_elements.append(make_body(
f"Sollte {kunde_formal} mit den Ergebnissen des Projektes nicht zufrieden sein, " f"Die Zufriedenheit der {kunde_firma} hat für uns höchste Priorität."
f"so entscheidet {kunde_formal} einen Monat rückwirkend über das Monats-Honorar "
f" bis hin zu einem Honorar von 0 EUR."
)) ))
new_elements.append(make_body( new_elements.append(make_body(
f"Geplant ist eine Begleitung über {laufzeit}. Allerdings kann {kunde_formal} " f"Sollte unser Klient mit den Ergebnissen der Zusammenarbeit nicht zufrieden sein, so "
f"das gemeinsame Vorhaben jederzeit beenden." f"behält sie sich das Recht vor, das jeweilige Monats-Honorar rückwirkend anzupassen "
f" bis hin zu einem Betrag von 0 EUR. Das KIM-Programm ist auf eine Laufzeit von "
f"zwölf Monaten ausgelegt. {kunde_firma} kann das Vorhaben jedoch jederzeit und ohne "
f"Angabe von Gründen beenden. Dieses Qualitätsversprechen spiegelt unseren Anspruch "
f"an Verbindlichkeit, Transparenz und partnerschaftliches Arbeiten wider."
)) ))
# Leere Zeilen vor Schlussbestimmungen # Leere Zeilen vor Schlussbestimmungen
@@ -439,8 +461,29 @@ def generate_angebot(data: dict, output_path: str):
insert_point = elem insert_point = elem
# === 7. SCHLUSSBESTIMMUNGEN AKTUALISIEREN === # === 7. SCHLUSSBESTIMMUNGEN AKTUALISIEREN ===
# Die existierenden Schlussbestimmungen sind schon im Dokument (wurden nicht gelöscht) # Schlussbestimmung-Heading formatieren (kommt aus Template als "Normal (Web)")
# Wir müssen nur "Der Klient" durch den Kundennamen ersetzen for p in doc.paragraphs:
if "Schlussbestimmung" in p.text and len(p.text.strip()) < 25:
# Heading-Formatierung anwenden (blau, Heading 2, 12pt)
for run in p.runs:
run.font.name = FONT_NAME
run.font.size = HEADING_SIZE
run.font.color.rgb = BLUE_HEADING
run.font.bold = True
# Spacing anpassen
pPr = p._element.find(qn('w:pPr'))
if pPr is None:
pPr = OxmlElement('w:pPr')
p._element.insert(0, pPr)
spacing = pPr.find(qn('w:spacing'))
if spacing is None:
spacing = OxmlElement('w:spacing')
pPr.append(spacing)
spacing.set(qn('w:before'), str(SP_HEADING_BEFORE))
spacing.set(qn('w:after'), str(SP_HEADING_AFTER))
break
# "Der Klient" durch den Kundennamen ersetzen
for p in doc.paragraphs: for p in doc.paragraphs:
if p.text.startswith("Der Klient"): if p.text.startswith("Der Klient"):
firma_ref = kunde_formal firma_ref = kunde_formal