- Add proper <thead> with <th> headers to tables in email templates: order_cancelled_orga, order_notification_orga, order_refunded, organizer_invitation, payment_failed, scan_force_notification - Add proper <thead> with <th> headers to tables in PDF templates: attestation_ventes, billet, export_recap, invoice - Fix testInfraPageWithSnapshotData: provide complete server data (os, uptime, cpu, ram, disk, services, ssl) required by the template Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
192 lines
9.2 KiB
Twig
192 lines
9.2 KiB
Twig
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Facture {{ order.orderNumber }}</title>
|
|
<style>
|
|
@page { size: A4; margin: 0; }
|
|
body {
|
|
font-family: Helvetica, Arial, sans-serif;
|
|
font-size: 11px;
|
|
color: #111;
|
|
margin: 0;
|
|
padding: 0;
|
|
width: 210mm;
|
|
}
|
|
.page { padding: 40px; }
|
|
.header-table { width: 100%; border-collapse: collapse; margin-bottom: 30px; }
|
|
.header-table td { vertical-align: top; }
|
|
.logo { width: 120px; }
|
|
.logo img { max-width: 100px; max-height: 60px; }
|
|
.title { font-size: 28px; font-weight: bold; text-transform: uppercase; text-align: right; letter-spacing: 2px; }
|
|
.subtitle { font-size: 10px; color: #666; text-align: right; margin-top: 4px; }
|
|
|
|
.info-table { width: 100%; border-collapse: collapse; margin-bottom: 24px; }
|
|
.info-table td { vertical-align: top; padding: 0; }
|
|
.info-block { padding: 16px; border: 1px solid #eee; background: #fafafa; }
|
|
.info-title { font-size: 8px; font-weight: bold; text-transform: uppercase; letter-spacing: 1px; color: #999; margin-bottom: 6px; }
|
|
.info-text { font-size: 10px; font-weight: bold; line-height: 1.6; }
|
|
|
|
.items-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
|
|
.items-table th { background: #111827; color: #fff; padding: 10px 12px; font-size: 9px; font-weight: bold; text-transform: uppercase; letter-spacing: 1px; text-align: left; }
|
|
.items-table th:nth-child(2) { text-align: center; }
|
|
.items-table th:nth-child(3), .items-table th:nth-child(4) { text-align: right; }
|
|
.items-table td { padding: 10px 12px; border-bottom: 1px solid #eee; font-size: 11px; }
|
|
.items-table td:nth-child(2) { text-align: center; }
|
|
.items-table td:nth-child(3), .items-table td:nth-child(4) { text-align: right; }
|
|
|
|
.total-table { width: 300px; margin-left: auto; border-collapse: collapse; margin-bottom: 30px; }
|
|
.total-table td { padding: 6px 12px; font-size: 11px; }
|
|
.total-table .total-row td { border-top: 3px solid #111827; font-size: 14px; font-weight: bold; padding-top: 10px; }
|
|
|
|
.payment-block { padding: 14px 16px; border: 1px solid #eee; background: #fafafa; margin-bottom: 24px; }
|
|
|
|
.footer { padding: 20px 0; border-top: 2px solid #111827; margin-top: 30px; }
|
|
.footer-text { font-size: 10px; color: #666; line-height: 1.8; text-align: center; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="page">
|
|
<!-- HEADER -->
|
|
<table class="header-table">
|
|
<thead>
|
|
<tr>
|
|
<th style="padding: 0; font-size: 0; line-height: 0; height: 0; border: none; text-align: left; width: 120px;">Logo</th>
|
|
<th style="padding: 0; font-size: 0; line-height: 0; height: 0; border: none; text-align: right;">Facture</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="logo">
|
|
{% if logoBase64 %}
|
|
<img src="{{ logoBase64 }}" alt="E-Ticket">
|
|
{% endif %}
|
|
<div style="font-size: 12px; font-weight: bold; margin-top: 4px;">E-Ticket</div>
|
|
<div style="font-size: 8px; color: #666;">by E-Cosplay</div>
|
|
</td>
|
|
<td>
|
|
<div class="title">Facture</div>
|
|
<div class="subtitle">N° {{ order.orderNumber }}</div>
|
|
<div class="subtitle">Date : {{ order.paidAt ? order.paidAt|date('d/m/Y') : order.createdAt|date('d/m/Y') }}</div>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- INFOS -->
|
|
<table class="info-table">
|
|
<thead>
|
|
<tr>
|
|
<th style="padding: 0; font-size: 0; line-height: 0; height: 0; border: none; text-align: left; width: 50%;">Vendeur</th>
|
|
<th style="padding: 0; font-size: 0; line-height: 0; height: 0; border: none; text-align: left; width: 50%;">Acheteur</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td style="width: 50%; padding-right: 8px;">
|
|
<div class="info-block">
|
|
<div class="info-title">Vendeur / Organisateur</div>
|
|
<div class="info-text">
|
|
{{ organizer.companyName ?? (organizer.firstName ~ ' ' ~ organizer.lastName) }}<br>
|
|
{% if organizer.siret %}SIRET : {{ organizer.siret }}<br>{% endif %}
|
|
{% if organizer.address %}{{ organizer.address }}<br>{% endif %}
|
|
{% if organizer.postalCode or organizer.city %}{{ organizer.postalCode }} {{ organizer.city }}<br>{% endif %}
|
|
{% if organizer.email %}{{ organizer.email }}<br>{% endif %}
|
|
{% if organizer.phone %}{{ organizer.phone }}{% endif %}
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td style="width: 50%; padding-left: 8px;">
|
|
<div class="info-block">
|
|
<div class="info-title">Acheteur</div>
|
|
<div class="info-text">
|
|
{% if order.invitation %}
|
|
{{ organizer.companyName ?? (organizer.firstName ~ ' ' ~ organizer.lastName) }}<br>
|
|
{% if organizer.email %}{{ organizer.email }}{% endif %}
|
|
{% else %}
|
|
{{ order.firstName }} {{ order.lastName }}<br>
|
|
{{ order.email }}
|
|
{% endif %}
|
|
</div>
|
|
<div class="info-title" style="margin-top: 10px;">Evenement</div>
|
|
<div class="info-text">
|
|
{{ order.event.title }}<br>
|
|
{{ order.event.startAt|date('d/m/Y') }} — {{ order.event.startAt|date('H:i') }} a {{ order.event.endAt|date('H:i') }}<br>
|
|
{{ order.event.address }}, {{ order.event.zipcode }} {{ order.event.city }}
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- ITEMS -->
|
|
<table class="items-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Description</th>
|
|
<th>Quantite</th>
|
|
<th>Prix unitaire HT</th>
|
|
<th>Total HT</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in order.items %}
|
|
<tr>
|
|
<td style="font-weight: bold;">{{ item.billetName }}</td>
|
|
<td>{{ item.quantity }}</td>
|
|
<td>{{ item.unitPriceHTDecimal|number_format(2, ',', ' ') }} €</td>
|
|
<td style="font-weight: bold;">{{ item.lineTotalHTDecimal|number_format(2, ',', ' ') }} €</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- TOTALS -->
|
|
<table class="total-table">
|
|
<thead>
|
|
<tr>
|
|
<th style="padding: 0; font-size: 0; line-height: 0; height: 0; border: none; text-align: left;">Libelle</th>
|
|
<th style="padding: 0; font-size: 0; line-height: 0; height: 0; border: none; text-align: right;">Montant</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Total HT</td>
|
|
<td style="text-align: right; font-weight: bold;">{{ order.totalHTDecimal|number_format(2, ',', ' ') }} €</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="color: #666;">TVA (0%)</td>
|
|
<td style="text-align: right; color: #666;">0,00 €</td>
|
|
</tr>
|
|
<tr class="total-row">
|
|
<td>Total TTC</td>
|
|
<td style="text-align: right;">{{ order.totalHTDecimal|number_format(2, ',', ' ') }} €</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- PAYMENT -->
|
|
{% if order.paymentMethod %}
|
|
<div class="payment-block">
|
|
<div class="info-title">Paiement</div>
|
|
<div style="font-size: 10px; font-weight: bold;">
|
|
{{ order.paymentMethod }}
|
|
{% if order.cardBrand and order.cardLast4 %} — {{ order.cardBrand|upper }} **** {{ order.cardLast4 }}{% endif %}
|
|
{% if order.paidAt %} — Paye le {{ order.paidAt|date('d/m/Y H:i') }}{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- FOOTER -->
|
|
<div class="footer">
|
|
<div class="footer-text">
|
|
Vente realisee via la plateforme E-Ticket, operee par l'association E-Cosplay<br>
|
|
42 rue de Saint-Quentin, 02800 Beautor, France — contact@e-cosplay.fr<br>
|
|
Cette facture est emise par l'organisateur {{ organizer.companyName ?? (organizer.firstName ~ ' ' ~ organizer.lastName) }}{% if organizer.siret %} — SIRET {{ organizer.siret }}{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|