""" Email-Renderer fuer den Vendor-Redundanz + EU-Alternativen + Cost-/Savings-Block. Wird im Email-Body unter dem VVT eingebaut. """ from __future__ import annotations def _fmt_eur(low: int, high: int) -> str: if not low and not high: return "im Listpreis bundled" if low == high: return f"~{low:,} €".replace(",", ".") return f"{low:,}–{high:,} €".replace(",", ".") def build_redundancy_html(report: dict | None) -> str: if not report: return "" s = report.get("summary") or {} redundancies = report.get("redundancies") or [] eu_alts = report.get("eu_alternatives") or [] multi = report.get("multi_function_tools") or [] cur = s.get("estimated_current_year_eur") or [0, 0] sav = s.get("estimated_saving_year_eur") or [0, 0] pct = s.get("estimated_saving_pct") or "n/a" parts = [ '
', '

' 'Optimierungspotenzial: Redundanzen + EU-Alternativen

', f'

' f'{s.get("redundancy_count", 0)} Kategorien mit ' f'mehreren Anbietern · {s.get("consolidation_potential", 0)} ' f'Anbieter konsolidierbar · ' f'{s.get("eu_alternative_count", 0)} EU-Alternativen verfuegbar

', '
', '
' 'Diese Schaetzung umfasst NUR die als redundant erkannten Tools — ' 'nicht den Gesamt-Stack der Website
', f'
' f'Listpreis-Schaetzung der redundanten Tools ' f'(Mehrfach-Anbieter in derselben Funktions-Kategorie):' f' {_fmt_eur(*cur)}/Jahr
', f'
' f'Sparpotenzial durch Konsolidierung auf je 1 EU-Tool pro Kategorie:' f' {_fmt_eur(*sav)}/Jahr ({pct})
', '
' 'Wichtige Einschraenkungen:
' '• Konzern-Konditionen liegen ueblicherweise 30–50% unter Listpreis — ' 'realistisches Saving entsprechend €X·0,5 bis €X·0,7.
' '• Eintraege "Eigene Marke — Tool" (z.B. "BMW AG — Adobe Analytics") ' 'gehoeren oft zu einem einzigen Master-Vertrag, nicht zu mehreren Lizenzen.
' '• Media-Spend (Google Ads, Meta Ads) ist NICHT enthalten — nur Tooling-Lizenzen.
' '• Quelle: Gartner/Forrester 2025 + oeffentliche Listpreise.' '
', ] if redundancies: parts.append( '' '' '' '' '' '' '' '' ) for r in redundancies[:12]: vendors_str = ", ".join(r.get("vendors", [])[:6]) if len(r.get("vendors", [])) > 6: vendors_str += f" (+{len(r['vendors']) - 6} weitere)" sav_r = r.get("estimated_saving_year_eur") or [0, 0] parts.append( f'' f'' f'' f'' f'' f'' ) hint = r.get("consolidation_hint") if hint: parts.append( f'' ) caveats = r.get("caveats") or [] if caveats: parts.append( f'' ) parts.append('
Kategorie#AnbieterEU-EmpfehlungSaving / Jahr
{r["category_label"]}{r["count"]}{vendors_str}{r.get("suggested_eu_tool") or "–"}' f'{_fmt_eur(*sav_r)}
' f'Hinweis: {hint}
' f'Moegliche Gruende fuer Mehrfach-Einsatz: ' + "; ".join(caveats) + '
') if multi: parts.append( '
' 'Multi-Funktions-Tools (1 Tool ersetzt mehrere Kategorien):' '
') if eu_alts: parts.append( '
EU-Alternativen pro Anbieter (Details)' '
') parts.append('
') return "".join(parts)