Files
Holzleitner---Backend--aktu…/crates/api/templates/delivery_details.html
Dennis Nemec 02aa216bc6 feat(admin): GET /admin/belege/{belegnummer}/html - HTML-Detailansicht
Rendert dieselben Daten wie der JSON-Detail-Endpunkt als schoen kategorisierte
HTML-Seite (askama, compile-time-Template). Der Handler destilliert das
DeliveryDetails-Aggregat in ein flaches View-Modell (Artikel-/Lagernamen
aufgeloest, Euro/Datum formatiert, deutsche Status-/Rollen-Labels); die
Template-Datei bleibt frei von Domain-Logik. Kategorien: Uebersicht, Kunde,
Lieferadresse, Positionen (Tabelle, veraenderte Zeilen hervorgehoben),
Geld-Gutschrift, Dienstleistungen, Notizen, Kontakte. Nutzt den bestehenden
get_delivery_details-Use-Case; admin-key-geschuetzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 00:11:05 +02:00

221 lines
8.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Beleg {{ belegnummer }} Lieferdetails</title>
<style>
:root {
--bg: #f4f5f7; --card: #ffffff; --ink: #1f2733; --muted: #6b7684;
--line: #e4e8ee; --accent: #2f6fed; --ok: #1f9d63; --warn: #d97706;
--danger: #d83a3a; --chip: #eef2fb;
}
* { box-sizing: border-box; }
body {
margin: 0; background: var(--bg); color: var(--ink);
font: 15px/1.5 -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
padding: 24px;
}
.wrap { max-width: 1040px; margin: 0 auto; }
header.page {
display: flex; align-items: center; flex-wrap: wrap; gap: 12px;
margin-bottom: 20px;
}
header.page h1 { font-size: 26px; margin: 0; letter-spacing: .3px; }
header.page .sub { color: var(--muted); font-size: 14px; margin-top: 2px; }
.badges { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
.badge {
display: inline-block; padding: 4px 12px; border-radius: 999px;
font-size: 13px; font-weight: 600; background: var(--chip); color: var(--accent);
}
.badge.state-completed { background: #e5f6ec; color: var(--ok); }
.badge.state-active { background: #e8f0fe; color: var(--accent); }
.badge.state-held { background: #fef3e2; color: var(--warn); }
.badge.state-canceled { background: #fce8e8; color: var(--danger); }
.badge.modified { background: #fef3e2; color: var(--warn); }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.card {
background: var(--card); border: 1px solid var(--line); border-radius: 12px;
padding: 16px 18px;
}
.card.full { grid-column: 1 / -1; }
.card h2 {
font-size: 13px; text-transform: uppercase; letter-spacing: .6px;
color: var(--muted); margin: 0 0 12px; font-weight: 700;
}
dl { display: grid; grid-template-columns: minmax(120px, 40%) 1fr; gap: 6px 14px; margin: 0; }
dt { color: var(--muted); }
dd { margin: 0; font-variant-numeric: tabular-nums; }
.addr { line-height: 1.45; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { padding: 8px 10px; text-align: left; border-bottom: 1px solid var(--line); }
th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .4px; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
tr.modified td { background: #fff8ec; }
tfoot td { font-weight: 700; border-top: 2px solid var(--line); border-bottom: none; }
.pill { display: inline-block; padding: 1px 8px; border-radius: 6px; font-size: 12px; font-weight: 600; }
.pill.done { background: #e5f6ec; color: var(--ok); }
.pill.removed, .pill.held { background: #fef3e2; color: var(--warn); }
.pill.in_progress { background: #eef2fb; color: var(--accent); }
.note { border-left: 3px solid var(--line); padding: 4px 0 4px 12px; margin: 10px 0; }
.note .meta { color: var(--muted); font-size: 13px; margin-top: 2px; }
.empty { color: var(--muted); font-style: italic; }
.contact { padding: 8px 0; border-bottom: 1px solid var(--line); }
.contact:last-child { border-bottom: none; }
.contact .role { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .4px; }
.chan { color: var(--muted); font-size: 14px; }
footer.page { color: var(--muted); font-size: 12px; margin-top: 22px; text-align: right; }
@media (max-width: 720px) { .grid { grid-template-columns: 1fr; } body { padding: 14px; } }
</style>
</head>
<body>
<div class="wrap">
<header class="page">
<div>
<h1>Beleg {{ belegnummer }}</h1>
<div class="sub">{{ customer_name }} · Tour {{ tour_date }}</div>
</div>
<div class="badges">
<span class="badge state-{{ state_class }}">{{ state_label }}</span>
{% if positions_modified %}<span class="badge modified">Positionen verändert</span>{% endif %}
</div>
</header>
<div class="grid">
<section class="card">
<h2>Übersicht</h2>
<dl>
<dt>Status</dt><dd>{{ state_label }}</dd>
<dt>Tourdatum</dt><dd>{{ tour_date }}</dd>
<dt>Position in Tour</dt><dd>{{ sort_order }}</dd>
<dt>Wunschzeit</dt><dd>{{ desired_time }}</dd>
<dt>Sondervereinbarung</dt><dd>{{ special_agreements }}</dd>
<dt>Vorausbezahlt</dt><dd>{{ prepaid }}</dd>
<dt>Warenwert (geliefert)</dt><dd>{{ total }}</dd>
</dl>
</section>
<section class="card">
<h2>Kunde</h2>
<dl>
<dt>Name</dt><dd>{{ customer_name }}</dd>
<dt>ERP-Kundennr.</dt><dd>{{ erp_customer_id }}</dd>
</dl>
<div class="addr" style="margin-top:10px">
{% for line in customer_address_lines %}{{ line }}<br>{% endfor %}
</div>
{% if !customer_contacts.is_empty() %}
<div style="margin-top:12px">
{% for c in customer_contacts %}
<div class="contact">
<strong>{{ c.name }}</strong>
{% if !c.phone.is_empty() %}<div class="chan">☎ {{ c.phone }}</div>{% endif %}
{% if !c.email.is_empty() %}<div class="chan">✉ {{ c.email }}</div>{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
</section>
<section class="card">
<h2>Lieferadresse</h2>
<div class="addr">
{% for line in delivery_address_lines %}{{ line }}<br>{% endfor %}
</div>
</section>
<section class="card">
<h2>Geld-Gutschrift</h2>
{% if has_credit %}
<dl>
<dt>Betrag</dt><dd>{{ credit_amount }}</dd>
<dt>Grund</dt><dd>{{ credit_reason }}</dd>
</dl>
{% else %}
<div class="empty">Keine Geld-Gutschrift</div>
{% endif %}
</section>
<section class="card full">
<h2>Positionen</h2>
<table>
<thead>
<tr>
<th>Art.-Nr.</th><th>Bezeichnung</th><th>Lager</th>
<th class="num">Soll</th><th class="num">Gescannt</th>
<th class="num">Gutschr.</th><th class="num">Geliefert</th>
<th class="num">Einzelpreis</th><th class="num">Summe</th><th>Status</th>
</tr>
</thead>
<tbody>
{% for it in items %}
<tr class="{% if it.modified %}modified{% endif %}">
<td>{{ it.article_number }}</td>
<td>{{ it.article_name }}{% if !it.held_reason.is_empty() %}<br><span class="chan">{{ it.held_reason }}</span>{% endif %}</td>
<td>{{ it.warehouse }}</td>
<td class="num">{{ it.required }}</td>
<td class="num">{{ it.scanned }}</td>
<td class="num">{{ it.credited }}</td>
<td class="num">{{ it.delivered }}</td>
<td class="num">{{ it.unit_price }}</td>
<td class="num">{{ it.line_total }}</td>
<td><span class="pill {{ it.status_class }}">{{ it.status_label }}</span></td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr><td colspan="8" class="num">Warenwert (geliefert)</td><td class="num">{{ total }}</td><td></td></tr>
</tfoot>
</table>
</section>
<section class="card">
<h2>Dienstleistungen</h2>
{% if services.is_empty() %}
<div class="empty">Keine Dienstleistungen gesetzt</div>
{% else %}
<dl>
{% for s in services %}<dt>{{ s.name }}</dt><dd>{{ s.value }}</dd>{% endfor %}
</dl>
{% endif %}
</section>
<section class="card">
<h2>Notizen</h2>
{% if notes.is_empty() %}
<div class="empty">Keine Notizen</div>
{% else %}
{% for n in notes %}
<div class="note">
<div>{{ n.text }}</div>
<div class="meta">
#{{ n.author }} · {{ n.created_at }}
{% if n.credit_note %} · Gutschrift-Notiz{% endif %}
{% if n.image %} · 📎 Bild{% endif %}
</div>
</div>
{% endfor %}
{% endif %}
</section>
<section class="card full">
<h2>Kontakte</h2>
{% if contacts.is_empty() %}
<div class="empty">Keine Kontaktquellen</div>
{% else %}
{% for c in contacts %}
<div class="contact">
<div class="role">{{ c.role }}</div>
<strong>{{ c.name }}</strong>
{% for ch in c.channels %}<div class="chan">{{ ch.kind }}: {{ ch.value }}</div>{% endfor %}
</div>
{% endfor %}
{% endif %}
</section>
</div>
<footer class="page">Erzeugt am {{ generated_at }} · Holzleitner Backend</footer>
</div>
</body>
</html>