"""Druckansicht als eigenständige HTML-Seite. Warum serverseitig, obwohl die App schon `@media print` mitbringt: Der Ausdruck soll auch ohne geöffnete App möglich sein - aus einem Lesezeichen, über einen öffentlichen Link, oder von einem Rechner, auf dem niemand die PWA installiert hat. Die Gliederung stammt aus `app.list_view`, also derselben Quelle wie die Bildschirmansicht; ein zweiter Sortieralgorithmus würde mit der Zeit abweichen. """ from datetime import datetime from jinja2 import Environment, select_autoescape from app.config import settings from app.schemas_shopping import ListView # autoescape ist hier keine Formsache: Artikelnamen und Notizen sind # freie Nutzereingaben und landen direkt im HTML. _env = Environment(autoescape=select_autoescape(default=True, default_for_string=True)) _TEMPLATE = _env.from_string("""
Die Liste ist leer.
{% endif %} {% for market in view.markets %}| {{ item.count }}× | {{ item.article_name }} {% set detail = [] %} {%- if item.pack_size %}{% set _ = detail.append(pack(item)) %}{% endif %} {%- if item.variant %}{% set _ = detail.append(item.variant) %}{% endif %} {%- if item.note %}{% set _ = detail.append(item.note) %}{% endif %} {%- if detail %}{{ detail | join(" · ") }}{% endif %} | {{ money(item.total_cents) if item.total_cents else "" }} |
{{ app_name }} · erzeugt {{ printed_at }}
""") def _money(cents: int | None) -> str: value = (cents or 0) / 100 return f"{value:,.2f} €".replace(",", "\u00a0").replace(".", ",") def _pack(item) -> str: """Gebindegröße lesbar machen: 250 statt 250.000, 1,5 statt 1.500. Nicht über rstrip("0"): Bei einem Wert ohne Dezimalpunkt - und den liefert Decimal("250") - würde das die letzte Null abschneiden und aus 250 g plötzlich 25 g machen. `normalize()` entfernt nur tatsächlich überflüssige Nachkommastellen. """ value = item.pack_size text = format(value.normalize(), "f") if hasattr(value, "normalize") else str(value) return f"{text.replace('.', ',')} {item.pack_unit or ''}".strip() def render_print(view: ListView, *, include_bought: bool = True) -> str: open_count = sum(market.open_count for market in view.markets) return _TEMPLATE.render( view=view, app_name=settings.app_name, printed_at=datetime.now().strftime("%d.%m.%Y, %H:%M"), open_count=open_count, include_bought=include_bought, money=_money, pack=_pack, )