Files
2026-06-24 14:40:43 +00:00

125 lines
8.6 KiB
Django/Jinja

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Monatsreport {{ data.report.period.label }} - {{ data.customer.name }}</title>
<style>
:root {
--primary: {{ data.design.primary_color }};
--accent: {{ data.design.accent_color }};
--text: {{ data.design.text_color }};
--muted: {{ data.design.muted_color }};
--surface: {{ data.design.surface_color }};
--line: #cfd6da;
--positive: #236a4f;
--negative: #9d3e3e;
}
@page { size: A4; margin: 15mm 16mm 19mm; }
* { box-sizing: border-box; }
html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body { margin: 0; font-family: {{ data.design.font_family }}, Georgia, serif; color: var(--text); font-size: 9.5pt; line-height: 1.5; }
h1, h2, h3, p { margin-top: 0; }
h1 { font-size: 25pt; font-weight: 500; line-height: 1.12; margin-bottom: 2mm; }
h2 { font-size: 15pt; font-weight: 600; color: var(--primary); margin-bottom: 3mm; }
h3 { font-size: 10.5pt; margin-bottom: 1mm; }
p { margin-bottom: 3mm; }
.sans { font-family: Arial, sans-serif; }
.header { border-top: 2.5mm solid var(--primary); padding-top: 7mm; margin-bottom: 8mm; display: grid; grid-template-columns: 1fr auto; gap: 8mm; }
.brand { text-align: right; font-family: Arial, sans-serif; font-size: 8pt; color: var(--muted); }
.brand strong { display: block; color: var(--primary); font-size: 10pt; margin-bottom: 1mm; }
.period { font-family: Arial, sans-serif; text-transform: uppercase; letter-spacing: 1.4px; color: var(--accent); font-size: 8pt; font-weight: 700; }
.meta { display: flex; gap: 6mm; color: var(--muted); font-family: Arial, sans-serif; font-size: 7.5pt; }
.rule-heading { display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 4mm; margin: 7mm 0 3mm; }
.rule-heading h2 { margin: 0; white-space: nowrap; }
.rule { height: .25mm; background: var(--line); }
.summary { padding: 5mm 0 5mm 5mm; border-left: 1.2mm solid var(--accent); break-inside: avoid; }
.summary h2 { color: var(--text); }
.summary p { margin: 0; }
.kpis { display: grid; grid-template-columns: repeat(4,1fr); border: .25mm solid var(--line); break-inside: avoid; }
.kpi { padding: 4mm; border-right: .25mm solid var(--line); min-height: 28mm; }
.kpi:last-child { border-right: 0; }
.kpi-label { font-family: Arial, sans-serif; font-size: 7pt; text-transform: uppercase; letter-spacing: .8px; color: var(--muted); }
.kpi-value { color: var(--primary); font-size: 15pt; font-weight: 600; margin: 2.5mm 0 1mm; white-space: nowrap; }
.kpi-delta { font-family: Arial, sans-serif; font-size: 7pt; color: var(--muted); }
.facts { display: grid; grid-template-columns: repeat(4,1fr); background: var(--surface); padding: 3mm 4mm; font-family: Arial, sans-serif; font-size: 7.5pt; }
.facts strong { display: block; color: var(--primary); font-size: 9pt; margin-top: .5mm; }
table { width: 100%; border-collapse: collapse; break-inside: avoid; font-family: Arial, sans-serif; font-size: 7.6pt; }
th { text-align: left; color: var(--muted); font-size: 6.8pt; text-transform: uppercase; letter-spacing: .7px; padding: 2.5mm; border-bottom: .5mm solid var(--primary); }
td { padding: 2.5mm; border-bottom: .25mm solid var(--line); }
td.num, th.num { text-align: right; }
.developments { columns: 2; column-gap: 6mm; }
.development { break-inside: avoid; padding: 0 0 3mm 4mm; margin-bottom: 3mm; border-left: .8mm solid var(--line); }
.development.positive { border-left-color: var(--positive); }
.development.negative { border-left-color: var(--negative); }
.development p { margin: 0; }
.recommendation { break-inside: avoid; display: grid; grid-template-columns: 10mm 1fr; gap: 4mm; padding: 4mm 0; border-bottom: .25mm solid var(--line); }
.recommendation:last-child { border-bottom: 0; }
.number { width: 9mm; height: 9mm; border: .4mm solid var(--primary); color: var(--primary); display: flex; align-items: center; justify-content: center; align-self: start; border-radius: 50%; font-family: Arial, sans-serif; font-weight: 700; }
.category { color: var(--accent); font-family: Arial, sans-serif; font-size: 7pt; text-transform: uppercase; letter-spacing: .8px; font-weight: 700; }
.recommendation p { margin-bottom: 1.7mm; }
.outlook { margin-top: 6mm; padding: 5mm; background: var(--surface); border-top: 1mm solid var(--primary); break-inside: avoid; }
.outlook p { margin: 0; }
.notes { font-family: Arial, sans-serif; font-size: 7pt; color: var(--muted); margin-top: 4mm; }
.notes ul { margin: 1mm 0; padding-left: 4mm; }
.legal { font-family: Arial, sans-serif; font-size: 6.6pt; color: var(--muted); margin-top: 4mm; }
</style>
</head>
<body>
<header class="header">
<div>
<div class="period">{{ data.report.period.label }}</div>
<h1>Monatsreport</h1>
<div class="meta"><span>{{ data.customer.property_name }}</span><span>{{ data.customer.location }}</span><span>{{ data.report.generated_at|date_de }}</span></div>
</div>
<div class="brand"><strong>{{ data.customer.name }}</strong>{{ data.customer.contact_line }}</div>
</header>
<section class="summary">
<div class="period">Kurzfazit</div>
<h2>{{ data.content.summary.headline }}</h2>
<p>{{ data.content.summary.text }}</p>
</section>
<div class="rule-heading"><h2>Kennzahlen</h2><div class="rule"></div></div>
<section class="kpis">
<div class="kpi"><div class="kpi-label">Umsatz</div><div class="kpi-value">{{ data.metrics.revenue|currency_de(data.metrics.currency) }}</div><div class="kpi-delta">{{ data.metrics.comparison.revenue_delta_pct|delta_de(1, '%') }}</div></div>
<div class="kpi"><div class="kpi-label">Auslastung</div><div class="kpi-value">{{ data.metrics.occupancy_rate|percent_de }}</div><div class="kpi-delta">{{ data.metrics.comparison.occupancy_delta_pp|delta_de(1, 'PP') }}</div></div>
<div class="kpi"><div class="kpi-label">Ø Tagespreis</div><div class="kpi-value">{{ data.metrics.adr|currency_de(data.metrics.currency) }}</div><div class="kpi-delta">{{ data.metrics.comparison.adr_delta_pct|delta_de(1, '%') }}</div></div>
<div class="kpi"><div class="kpi-label">Buchungen</div><div class="kpi-value">{{ data.metrics.bookings|number_de(0) }}</div><div class="kpi-delta">{{ data.metrics.comparison.bookings_delta|delta_de(0, '') }} Buchungen</div></div>
</section>
<section class="facts">
<div>RevPAR<strong>{{ data.metrics.revpar|currency_de(data.metrics.currency) }}</strong></div>
<div>Verkaufte Nächte<strong>{{ data.metrics.nights_sold|number_de(0) }}</strong></div>
<div>Ø Aufenthalt<strong>{{ data.metrics.average_stay|number_de(1) }} Nächte</strong></div>
<div>Stornoquote<strong>{{ data.metrics.cancellation_rate|percent_de }}</strong></div>
</section>
<div class="rule-heading"><h2>Monatsverlauf</h2><div class="rule"></div></div>
<table>
<thead><tr><th>Monat</th><th class="num">Umsatz</th><th class="num">Auslastung</th><th class="num">Ø Tagespreis</th></tr></thead>
<tbody>
{% for item in data.trends %}<tr><td>{{ item.label }}</td><td class="num">{{ item.revenue|currency_de(data.metrics.currency) }}</td><td class="num">{{ item.occupancy_rate|percent_de }}</td><td class="num">{{ item.adr|currency_de(data.metrics.currency) }}</td></tr>{% endfor %}
</tbody>
</table>
<div class="rule-heading"><h2>Wichtige Entwicklungen</h2><div class="rule"></div></div>
<section class="developments">
{% for item in data.content.developments %}<article class="development {{ item.impact }}"><h3>{{ item.title }}</h3><p>{{ item.text }}</p></article>{% endfor %}
</section>
<div class="rule-heading"><h2>Empfehlungen</h2><div class="rule"></div></div>
<section>
{% for item in data.content.recommendations|sort(attribute='priority') %}
<article class="recommendation">
<div class="number">{{ item.priority }}</div>
<div><div class="category">{{ item.category }}</div><h3>{{ item.title }}</h3><p><strong>Begründung:</strong> {{ item.reason }}</p><p><strong>Massnahme:</strong> {{ item.action }}</p><p><strong>Erwarteter Effekt:</strong> {{ item.expected_effect }}</p></div>
</article>
{% endfor %}
</section>
<section class="outlook"><div class="period">Ausblick</div><h2>{{ data.content.outlook.headline }}</h2><p>{{ data.content.outlook.text }}</p></section>
{% if data.content.data_quality_notes %}<section class="notes"><strong>Hinweise zur Datengrundlage</strong><ul>{% for note in data.content.data_quality_notes %}<li>{{ note }}</li>{% endfor %}</ul></section>{% endif %}
<div class="legal">{{ data.legal_note }}</div>
</body>
</html>