- PDF generation with WeasyPrint: deviz and factura templates (A4, branding)
- GET /orders/{id}/pdf/deviz returns PDF with order lines and totals
- Client portal (public, no auth): GET /p/{token}, POST /p/{token}/accept|reject
- SMS service (SMSAPI.ro) - skips in dev when no token configured
- Invoice service: create from validated order, auto-number (F-YYYY-NNNN)
- GET /invoices/{id}/pdf returns factura PDF
- Order status_client field for client accept/reject tracking
- Alembic migration for status_client
- 19 passing tests (auth + sync + orders + pdf + portal + invoices)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
68 lines
2.4 KiB
HTML
68 lines
2.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ro"><head>
|
|
<meta charset="UTF-8">
|
|
<style>
|
|
@page { size: A4; margin: 2cm; }
|
|
body { font-family: DejaVu Sans, sans-serif; font-size: 11pt; color: #111; }
|
|
.header { display: flex; justify-content: space-between; margin-bottom: 24px; }
|
|
h2 { margin: 0; font-size: 16pt; }
|
|
h3 { font-size: 11pt; margin: 16px 0 6px; color: #374151; }
|
|
table { width: 100%; border-collapse: collapse; }
|
|
th { background: #f3f4f6; padding: 6px 8px; text-align: left; font-size: 10pt; }
|
|
td { padding: 5px 8px; border-bottom: 1px solid #e5e7eb; font-size: 10pt; }
|
|
.totals { margin-top: 20px; text-align: right; }
|
|
.totals div { margin-bottom: 4px; }
|
|
.total-final { font-weight: bold; font-size: 13pt; border-top: 2px solid #111; padding-top: 6px; }
|
|
</style>
|
|
</head><body>
|
|
<div class="header">
|
|
<div>
|
|
<strong>{{ tenant.nume }}</strong><br>
|
|
{% if tenant.cui %}CUI: {{ tenant.cui }}<br>{% endif %}
|
|
{% if tenant.adresa %}{{ tenant.adresa }}<br>{% endif %}
|
|
{% if tenant.telefon %}Tel: {{ tenant.telefon }}{% endif %}
|
|
</div>
|
|
<div style="text-align:right">
|
|
<h2>DEVIZ Nr. {{ order.nr_comanda or order.id[:8]|upper }}</h2>
|
|
<div>Data: {{ order.data_comanda }}</div>
|
|
<div>Auto: <strong>{{ order.nr_auto }}</strong></div>
|
|
<div>{{ order.marca_denumire or '' }} {{ order.model_denumire or '' }}</div>
|
|
<div>Client: {{ order.client_nume or '' }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if manopera %}
|
|
<h3>Operatii manopera</h3>
|
|
<table>
|
|
<tr><th>Descriere</th><th>Ore</th><th>Pret/ora (RON)</th><th>Total (RON)</th></tr>
|
|
{% for l in manopera %}
|
|
<tr>
|
|
<td>{{ l.descriere }}</td><td>{{ l.ore }}</td>
|
|
<td>{{ "%.2f"|format(l.pret_ora or 0) }}</td>
|
|
<td>{{ "%.2f"|format(l.total or 0) }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</table>
|
|
{% endif %}
|
|
|
|
{% if materiale %}
|
|
<h3>Materiale</h3>
|
|
<table>
|
|
<tr><th>Descriere</th><th>UM</th><th>Cant.</th><th>Pret unit. (RON)</th><th>Total (RON)</th></tr>
|
|
{% for l in materiale %}
|
|
<tr>
|
|
<td>{{ l.descriere }}</td><td>{{ l.um }}</td><td>{{ l.cantitate }}</td>
|
|
<td>{{ "%.2f"|format(l.pret_unitar or 0) }}</td>
|
|
<td>{{ "%.2f"|format(l.total or 0) }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</table>
|
|
{% endif %}
|
|
|
|
<div class="totals">
|
|
<div>Manopera: {{ "%.2f"|format(order.total_manopera or 0) }} RON</div>
|
|
<div>Materiale: {{ "%.2f"|format(order.total_materiale or 0) }} RON</div>
|
|
<div class="total-final">TOTAL: {{ "%.2f"|format(order.total_general or 0) }} RON</div>
|
|
</div>
|
|
</body></html>
|