Files
RosterChief/management/templates/management/event_referee_form_pdf.html
Bernard Siebens e737de9140 Fix the referee PDF's card background, make locations searchable, and add game end times to the API
Referee PDF: the info-card background used CSS color-mix(), which WeasyPrint
doesn't support -- the rule was silently dropped, leaving the card with no
background at all. Computed in Python instead (management/pdf.py) and baked
into the template as a plain hex value; the tint is based on the club's
primary_color, falling back to secondary_color when primary is itself (near)
black or white, where a straight tint would be invisible or too harsh.

Event forms: the location picker now shows "Name — City" (plus the country
when it isn't Belgium) and is searchable by name or city, reusing the
existing single-select searchable-select.js widget.

Games API: GameOut now carries `end` (explicit, or start + 2h when a GAME was
saved without one -- Event.save() sets this, never overwriting an explicit
end; other event kinds are untouched). /games/upcoming/ now includes
anything not yet finished rather than only things that haven't started, so a
game already in progress keeps showing up until its window closes; `status`
was adjusted to match so a game returned there never calls itself
"finished".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 23:44:11 +02:00

156 lines
8.0 KiB
HTML

{% load i18n %}
{% comment %}
Rendered by WeasyPrint -- modeled on the club's existing paper referee
payment form ("Leeg Document RefereesCompetitie25-26.pdf"): club header,
match details, referee names, a payment sentence, a fee+km breakdown per
referee, and blank signature lines. Header uses club.official_name
(legal_name if set, else name); the payment sentence uses the plain club
name -- the original document does the same, using a longer legal form up
top and a shorter one in the body text.
{% endcomment %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% trans "Referee form" %}</title>
<style>
@page {
size: A4;
margin: 20mm 18mm;
@bottom-center {
content: "{{ club.name }} — {% trans "referee form" %} — " counter(page) " / " counter(pages);
font-size: 8pt;
color: #8a8a8a;
}
}
:root {
--accent: {{ accent_color }};
--info-card-bg: {{ info_card_color }};
--ink: #16181d; --muted: #6b7280; --line: #d8dae0;
}
* { box-sizing: border-box; }
body { font-family: sans-serif; font-size: 10pt; color: var(--ink); line-height: 1.45; }
.header { display: flex; justify-content: space-between; align-items: flex-start; padding-bottom: 5mm; border-bottom: 2.5pt solid var(--accent); margin-bottom: 8mm; }
.header .club-name { font-size: 16pt; font-weight: 700; color: var(--accent); margin: 0 0 1mm; }
.header .club-address { font-size: 9pt; color: var(--muted); }
.header .doc-title { text-align: right; }
.header .doc-title .kicker { font-size: 8pt; letter-spacing: 1pt; text-transform: uppercase; color: var(--muted); }
.header .doc-title .title { font-size: 14pt; font-weight: 700; margin-top: 1mm; }
.info-card { background: var(--info-card-bg); border-radius: 3mm; padding: 5mm 6mm; margin-bottom: 7mm; }
.info-grid { display: flex; flex-wrap: wrap; gap: 4mm 10mm; }
.info-grid .item { min-width: 45mm; }
.info-grid .item.wide { flex: 1 1 100%; min-width: 100%; }
.info-label { font-size: 7.5pt; letter-spacing: 0.5pt; text-transform: uppercase; color: var(--muted); margin-bottom: 0.5mm; }
.info-value { font-size: 10.5pt; font-weight: 600; }
.note { font-size: 9.5pt; color: var(--muted); margin: 0 0 7mm; }
.section-title { font-size: 8pt; letter-spacing: 0.5pt; text-transform: uppercase; color: var(--accent); font-weight: 700; margin: 0 0 2mm; }
table.fees { width: 100%; border-collapse: collapse; margin-bottom: 9mm; }
table.fees th { text-align: left; font-size: 7.5pt; letter-spacing: 0.5pt; text-transform: uppercase; color: var(--muted); border-bottom: 1pt solid var(--line); padding: 2mm 2mm 1.5mm 0; }
table.fees td { padding: 2.5mm 2mm 2.5mm 0; border-bottom: 0.75pt solid var(--line); vertical-align: top; }
table.fees td.amount, table.fees th.amount { text-align: right; }
table.fees tr.empty td { color: var(--muted); font-style: italic; }
table.fees tr.total td { border-bottom: none; border-top: 1.5pt solid var(--accent); font-weight: 700; padding-top: 3mm; }
.signatures { display: flex; gap: 8mm; margin-top: 4mm; }
.sign-box { flex: 1; }
.sign-box .info-label { margin-bottom: 2mm; }
.sign-box .box { border: 0.75pt solid var(--line); border-radius: 2mm; min-height: 24mm; }
.collected-line { margin-top: 8mm; font-size: 9.5pt; }
.collected-line .fill { display: inline-block; border-bottom: 0.75pt solid var(--line); min-width: 90mm; }
</style>
</head>
<body>
<div class="header">
<div>
<div class="club-name">{{ club.official_name }}</div>
{% if home_location %}
<div class="club-address">{{ home_location.address }}</div>
<div class="club-address">{{ home_location.zip_code }} {{ home_location.city }}</div>
{% endif %}
</div>
<div class="doc-title">
<div class="kicker">{% trans "Referee payment form" %}</div>
<div class="title">{{ event.start|date:"j F Y" }}</div>
</div>
</div>
<div class="info-card">
<div class="info-grid">
<div class="item wide">
<div class="info-label">{% trans "Game" %}</div>
<div class="info-value">
{% for team in event.teams.all %}{{ team.short_name }}{% if not forloop.last %}, {% endif %}{% endfor %}
{% if event.opponent %}{% trans "vs" %} {{ event.opponent }}{% endif %}
{% if event.external_game_id %}<span style="color: var(--muted); font-weight: 400;">({% trans "ID" %}: {{ event.external_game_id }})</span>{% endif %}
</div>
</div>
<div class="item">
<div class="info-label">{% trans "Date" %}</div>
<div class="info-value">{{ event.start|date:"j F Y" }}</div>
</div>
<div class="item">
<div class="info-label">{% trans "Time" %}</div>
<div class="info-value">{{ event.start|date:"H:i" }}</div>
</div>
<div class="item wide">
<div class="info-label">{% trans "Referee names" %}</div>
<div class="info-value">
{% for referee in referees %}{{ referee.display_name }}{% if not forloop.last %}, {% endif %}{% empty %}—{% endfor %}
</div>
</div>
</div>
</div>
<p class="note">{% blocktrans with name=club.name %}{{ name }} pays the fee to the referee(s) for officiating the game mentioned above.{% endblocktrans %}</p>
<div class="section-title">{% trans "Referees and fee, plus any kilometers" %}</div>
<table class="fees">
<thead>
<tr>
<th>{% trans "Referee" %}</th>
<th class="amount">{% trans "Fee" %}</th>
<th class="amount">{% trans "Kilometers" %}</th>
<th class="amount">{% trans "Rate" %}</th>
<th class="amount">{% trans "Total" %}</th>
</tr>
</thead>
<tbody>
{% for referee in referees %}
<tr>
<td>{{ referee.display_name }}</td>
<td class="amount">€{{ referee.fee|floatformat:2 }}</td>
<td class="amount">{% if referee.km %}{{ referee.km }} km{% else %}—{% endif %}</td>
<td class="amount">{% if referee.km %}€{{ referee.km_rate }}{% else %}—{% endif %}</td>
<td class="amount">€{{ referee.total_payable|floatformat:2 }}</td>
</tr>
{% empty %}
<tr class="empty"><td colspan="5">{% trans "No referees assigned yet." %}</td></tr>
{% endfor %}
{% if referees %}
<tr class="total">
<td colspan="4">{% trans "Total to pay" %}</td>
<td class="amount">€{{ grand_total|floatformat:2 }}</td>
</tr>
{% endif %}
</tbody>
</table>
<div class="signatures">
<div class="sign-box">
<div class="info-label">{% trans "Referee signature" %}</div>
<div class="box"></div>
</div>
</div>
<div class="collected-line">
{% trans "Collected by team manager" %}: <span class="fill">&nbsp;</span>
</div>
</body>
</html>