Flatten budget-app contents into repo root
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
<!doctype html>
|
||||
<html lang="{{ t.lang }}">
|
||||
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ title }}</title><style>{{ css|safe }}</style></head>
|
||||
<body>
|
||||
<section class="page budget">
|
||||
<h1>{{ title }}</h1>
|
||||
<div class="sub">{{ t('budget_sub') }}{% if site %} · {{ t('site_filter', site=site) }}{% endif %} · {{ t('status') }}: {{ t(year.status) }} · {{ t('exported') }}: {{ exported }}</div>
|
||||
<table class="grid">
|
||||
<thead><tr><th>{{ t('line') }}</th><th>{{ t('site') }}</th>{% for m in t('months') %}<th>{{ m }}</th>{% endfor %}<th>{{ t("total") }} (€)</th></tr></thead>
|
||||
<tbody>
|
||||
{% for c in cats %}
|
||||
<tr class="cat"><td colspan="15">{{ c.name }}</td></tr>
|
||||
{% for l in c.lines %}
|
||||
<tr><td class="name">{{ l.line.name }}</td><td>{{ l.line.site.name if l.line.site else t('group') }}</td>
|
||||
{% for m in l.figures.months %}<td class="num">{{ t.num(m) }}</td>{% endfor %}<td class="num">{{ t.num(l.figures.adjusted) }}</td></tr>
|
||||
{% endfor %}
|
||||
<tr class="subtotal"><td colspan="2">{{ t('revenue_total') if c.category.section == 'revenue' else t('sub_total', name=c.name) }}</td>
|
||||
{% for m in c.totals.months %}<td class="num">{{ t.num(m) }}</td>{% endfor %}<td class="num">{{ t.num(c.totals.adjusted) }}</td></tr>
|
||||
{% endfor %}
|
||||
<tr class="grand"><td colspan="2">{{ t('total_costs') }}</td>{% for m in costs.months %}<td class="num">{{ t.num(m) }}</td>{% endfor %}<td class="num">{{ t.num(costs.adjusted) }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</body></html>
|
||||
@@ -0,0 +1,75 @@
|
||||
<!doctype html>
|
||||
<html lang="{{ t.lang }}">
|
||||
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ title }}</title><style>{{ css|safe }}</style>
|
||||
{% if screen %}<script>
|
||||
function fit(){var w=document.documentElement.clientWidth-32;var z=Math.min(1,w/1280);document.documentElement.style.setProperty('--zoom',z);}
|
||||
window.addEventListener('resize',fit);document.addEventListener('DOMContentLoaded',fit);
|
||||
</script>{% endif %}
|
||||
</head>
|
||||
<body>
|
||||
{% set total_pages = 3 + cats|length %}
|
||||
<section class="page cover">
|
||||
<h1>{{ title }}</h1>
|
||||
<p class="sub">{{ t('cost_report_subtitle') }}{% if site %} · {{ t('site_filter', site=site) }}{% endif %}</p>
|
||||
<div class="kicker">{{ t('total_across', n=cats|length) }}</div>
|
||||
<div class="big">{{ t.eur(costs.prior, 0) }} → {{ t.eur(costs.adjusted, 0) }}</div>
|
||||
<div class="delta {{ 'up' if costs.change > 0 else 'down' }}">{{ t.pct(costs.change_pct) }} ({{ t.eur(costs.change, 0, true) }})</div>
|
||||
<div class="meta">{{ t('status') }}: {{ t(year.status) }} · {{ t('exported') }}: {{ exported }}</div>
|
||||
</section>
|
||||
|
||||
<section class="page">
|
||||
<h1>{{ t('by_category') }}</h1>
|
||||
<p class="sub">{{ t('by_category_sub') }}</p>
|
||||
<div class="rule"></div>
|
||||
<div class="legend"><span><i style="background:#2f6fd6"></i>{{ t('prior_actual') }}</span><span><i style="background:#e8632b"></i>{{ t('adjusted') }}</span></div>
|
||||
<div class="chartbox full">{{ overview_chart|safe }}</div>
|
||||
<div class="footer">{{ title }}<span class="n">2 / {{ total_pages }}</span></div>
|
||||
</section>
|
||||
|
||||
<section class="page">
|
||||
<h1>{{ t('change_by_category') }}</h1>
|
||||
<p class="sub">{{ t('change_by_category_sub') }}</p>
|
||||
<div class="rule"></div>
|
||||
<div class="legend" style="text-align:center">{{ t('change_legend') }}</div>
|
||||
<div class="chartbox full">{{ change_chart|safe }}</div>
|
||||
<div class="footer">{{ title }}<span class="n">3 / {{ total_pages }}</span></div>
|
||||
</section>
|
||||
|
||||
{% for c in cats %}
|
||||
<section class="page">
|
||||
<h1>{{ c.name }}</h1>
|
||||
<p class="sub">{{ t('cost_line') if c.count == 1 else t('cost_lines', n=c.count) }}</p>
|
||||
<div class="rule"></div>
|
||||
<div class="legend"><span><i style="background:#2f6fd6"></i>{{ t('prior_actual') }}</span><span><i style="background:#e8632b"></i>{{ t('adjusted') }}</span></div>
|
||||
<div class="body">
|
||||
<div class="chartbox">{{ c.chart|safe }}</div>
|
||||
<div class="kpis">
|
||||
<div class="kpi"><div class="k">{{ t('prior_actual') }}</div><div class="v">{{ t.eur(c.totals.prior) }}</div></div>
|
||||
<div class="kpi"><div class="k">{{ t('adjusted') }}</div><div class="v">{{ t.eur(c.totals.adjusted) }}</div></div>
|
||||
<div class="kpi"><div class="k">{{ t('change_eur') }}</div><div class="v {{ 'up' if c.totals.change > 0 else 'down' }}">{{ t.eur(c.totals.change, 2, true) }}</div></div>
|
||||
<div class="kpi"><div class="k">{{ t('change_pct') }}</div><div class="v {{ 'up' if c.totals.change > 0 else 'down' }}">{{ t.pct(c.totals.change_pct) }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">{{ title }}<span class="n">{{ 3 + loop.index }} / {{ total_pages }}</span></div>
|
||||
</section>
|
||||
{% endfor %}
|
||||
|
||||
<section class="page">
|
||||
<h1>{{ t('summary') }}</h1>
|
||||
<p class="sub">{{ t('summary_sub') }}</p>
|
||||
<div class="rule"></div>
|
||||
<table class="summary">
|
||||
<thead><tr><th>{{ t('category') }}</th><th class="num">{{ t('prior_actual') }}</th><th class="num">{{ t('adjusted') }}</th><th class="num">{{ t('change_eur') }}</th><th class="num">{{ t('change_pct') }}</th></tr></thead>
|
||||
<tbody>
|
||||
{% for c in cats %}
|
||||
<tr><td>{{ c.name }}</td><td class="num">{{ t.eur(c.totals.prior) }}</td><td class="num">{{ t.eur(c.totals.adjusted) }}</td>
|
||||
<td class="num {{ 'up' if c.totals.change > 0 else 'down' }}">{{ t.eur(c.totals.change, 2, true) }}</td><td class="num {{ 'up' if c.totals.change > 0 else 'down' }}">{{ t.pct(c.totals.change_pct) }}</td></tr>
|
||||
{% endfor %}
|
||||
<tr class="total"><td>{{ t('total') }}</td><td class="num">{{ t.eur(costs.prior) }}</td><td class="num">{{ t.eur(costs.adjusted) }}</td>
|
||||
<td class="num {{ 'up' if costs.change > 0 else 'down' }}">{{ t.eur(costs.change, 2, true) }}</td><td class="num {{ 'up' if costs.change > 0 else 'down' }}">{{ t.pct(costs.change_pct) }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="footer">{{ title }}<span class="n">{{ total_pages }} / {{ total_pages }}</span></div>
|
||||
</section>
|
||||
</body></html>
|
||||
@@ -0,0 +1,62 @@
|
||||
@page { size: 338.67mm 190.5mm; margin: 0; }
|
||||
@page budget { size: A3 landscape; margin: 12mm 10mm 14mm 10mm;
|
||||
@bottom-left { content: string(doc-title); font: 8pt Helvetica, Arial, sans-serif; color: #888; }
|
||||
@bottom-right { content: counter(page) " / " counter(pages); font: 8pt Helvetica, Arial, sans-serif; color: #888; } }
|
||||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
body { font-family: "DejaVu Sans", "Liberation Sans", Arial, sans-serif; color: #1d1d1f; background: #ececee; }
|
||||
.page { position: relative; width: 338.67mm; height: 190.5mm; padding: 13mm 16mm 12mm; background: #fbfbf9;
|
||||
overflow: hidden; page-break-after: always; break-after: page; }
|
||||
.page:last-child { page-break-after: auto; break-after: auto; }
|
||||
.page h1 { font-size: 22pt; margin: 0 0 1.5mm; }
|
||||
.page .sub { font-size: 10pt; color: #444; margin: 0 0 3mm; }
|
||||
.rule { border-top: 1px solid #ddd; margin: 2mm 0 3mm; }
|
||||
.legend { font-size: 8.5pt; color: #333; margin-bottom: 2mm; }
|
||||
.legend span { display: inline-block; margin-right: 18mm; }
|
||||
.legend i { display: inline-block; width: 3mm; height: 3mm; margin-right: 2mm; vertical-align: -0.4mm; }
|
||||
.footer { position: absolute; left: 16mm; right: 16mm; bottom: 6mm; font-size: 7pt; color: #888; }
|
||||
.footer .n { float: right; }
|
||||
.cover { padding-left: 22mm; }
|
||||
.cover::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4mm; background: #2f6fd6; }
|
||||
.cover h1 { font-size: 34pt; margin-top: 48mm; }
|
||||
.cover .sub { font-size: 13pt; }
|
||||
.cover .kicker { margin-top: 16mm; font-size: 10pt; color: #444; }
|
||||
.cover .big { font-size: 22pt; font-weight: bold; margin: 2mm 0; }
|
||||
.cover .delta { font-size: 13pt; font-weight: bold; }
|
||||
.up { color: #c2410c; } .down { color: #15803d; }
|
||||
.body { display: flex; gap: 8mm; }
|
||||
.chartbox { flex: 1 1 auto; height: 128mm; }
|
||||
.chartbox.full { height: 136mm; }
|
||||
.chartbox svg { width: 100%; height: 100%; }
|
||||
.kpis { flex: 0 0 72mm; padding-top: 2mm; }
|
||||
.kpi { margin-bottom: 7mm; }
|
||||
.kpi .k { font-size: 9pt; color: #333; }
|
||||
.kpi .v { font-size: 18pt; font-weight: bold; margin-top: 1.5mm; }
|
||||
table.summary { width: 100%; border-collapse: collapse; font-size: 9pt; margin-top: 3mm; }
|
||||
table.summary th { text-align: left; font-weight: bold; border-bottom: 1px solid #bbb; padding: 2mm; }
|
||||
table.summary td { padding: 1.6mm 2mm; border-bottom: 1px solid #e4e4e4; }
|
||||
table.summary .num { text-align: right; white-space: nowrap; }
|
||||
table.summary tr.total td { font-weight: bold; border-top: 1px solid #999; }
|
||||
.meta { font-size: 8pt; color: #777; margin-top: 2mm; }
|
||||
|
||||
/* annual budget */
|
||||
.budget { page: budget; background: #fff; padding: 0; width: auto; height: auto; overflow: visible; }
|
||||
.budget h1 { font-size: 16pt; margin: 0 0 1mm; string-set: doc-title content(); }
|
||||
.budget .sub { font-size: 9pt; color: #555; margin-bottom: 4mm; }
|
||||
table.grid { width: 100%; border-collapse: collapse; font-size: 7.2pt; }
|
||||
table.grid th, table.grid td { padding: 1.1mm 1.2mm; border-bottom: 1px solid #eee; }
|
||||
table.grid thead th { background: #1f3b63; color: #fff; text-align: right; font-weight: bold; }
|
||||
table.grid thead th:first-child, table.grid thead th:nth-child(2) { text-align: left; }
|
||||
table.grid thead { display: table-header-group; }
|
||||
table.grid td.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
|
||||
table.grid tr.cat td { font-weight: bold; background: #eef2f8; padding-top: 2mm; }
|
||||
table.grid tr.subtotal td { font-weight: bold; border-top: 1px solid #9aa; border-bottom: 1px solid #9aa; }
|
||||
table.grid tr.grand td { font-weight: bold; background: #1f3b63; color: #fff; }
|
||||
table.grid tr { page-break-inside: avoid; }
|
||||
table.grid td.name { max-width: 70mm; }
|
||||
|
||||
@media screen {
|
||||
body { padding: 16px 0; }
|
||||
.page { margin: 0 auto 16px; box-shadow: 0 1px 4px rgba(0,0,0,.15); zoom: var(--zoom, 1); }
|
||||
.budget { width: auto; max-width: 1600px; height: auto; padding: 16px 20px; margin: 0 auto; overflow: visible; }
|
||||
}
|
||||
Reference in New Issue
Block a user