Files
gepafin-rendicontazione-api/app/templates_jinja/verbale_istruttoria.html
BFLOWS a3f863ecdb feat(verbale): logo Gepafin SVG ufficiale nel verbale PDF
Carlo ha fornito il logo Gepafin ufficiale (SVG vettoriale, colore #4A644E verde
istituzionale, 118x50 viewbox). Sostituito il placeholder testuale
<span class="hdr__logo">GEPAFIN</span> con il logo reale.

Implementazione:
- app/static/gepafin-logo.svg — logo copiato dal repo FE Gepafin (8.7KB SVG)
- verbale.py: aggiunto STATIC_DIR + LOGO_FILE_URL con path file:// assoluto
  (weasyprint risolve file:// localmente senza HTTP fetch)
- template passa logo_path nel contesto Jinja
- verbale_istruttoria.html: CSS .hdr__logo-img (altezza 38pt) sostituisce
  .hdr__logo testuale

Verificato: PDF da 23KB -> 32KB (incluso SVG), XObject embedded OK,
header PDF-1.7 valido.

Nel PDF il logo appare in alto a destra dell'header accanto al sottotitolo
'Finanziaria regionale dell\'Umbria'.
2026-04-18 22:15:24 +02:00

583 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="utf-8">
<title>Verbale di istruttoria — Pratica {{ practice.application_id }}</title>
<style>
@page {
size: A4;
margin: 22mm 18mm 20mm 18mm;
@top-left {
content: "GEPAFIN S.p.A.";
font-family: "DejaVu Sans", Helvetica, sans-serif;
font-size: 8pt;
color: #4a5568;
letter-spacing: 0.5px;
}
@top-right {
content: "Verbale di istruttoria — Pratica {{ practice.application_id }}";
font-family: "DejaVu Sans", Helvetica, sans-serif;
font-size: 8pt;
color: #4a5568;
}
@bottom-center {
content: "Pagina " counter(page) " di " counter(pages);
font-family: "DejaVu Sans", Helvetica, sans-serif;
font-size: 8pt;
color: #718096;
}
@bottom-right {
content: "Generato: {{ generated_at }}";
font-family: "DejaVu Sans", Helvetica, sans-serif;
font-size: 7pt;
color: #a0aec0;
}
}
html { font-family: "DejaVu Sans", Helvetica, sans-serif; font-size: 10pt; color: #1a202c; }
body { margin: 0; }
h1 { font-size: 18pt; margin: 0 0 4pt 0; color: #1a365d; letter-spacing: -0.3px; }
h2 { font-size: 12pt; margin: 18pt 0 6pt 0; padding: 4pt 0 4pt 8pt;
border-left: 3pt solid #2b6cb0; color: #1a365d; page-break-after: avoid; }
h3 { font-size: 10pt; margin: 12pt 0 4pt 0; color: #2d3748; page-break-after: avoid; }
p { margin: 4pt 0; line-height: 1.4; }
small { color: #4a5568; }
/* Intestazione GEPAFIN */
.hdr {
border-bottom: 2pt solid #1a365d;
padding-bottom: 10pt;
margin-bottom: 14pt;
}
.hdr__logo-img {
height: 38pt; width: auto;
display: inline-block;
}
.hdr__subtitle {
font-size: 9pt; color: #4a5568; margin-top: 2pt; letter-spacing: 0.5px;
}
.hdr__right {
float: right; text-align: right; font-size: 9pt; color: #4a5568;
}
/* Badge esito */
.badge {
display: inline-block; padding: 3pt 8pt; border-radius: 4pt;
font-size: 10pt; font-weight: 700; letter-spacing: 0.4pt;
}
.badge--approved { background: #c6f6d5; color: #22543d; border: 0.5pt solid #68d391; }
.badge--rejected { background: #fed7d7; color: #742a2a; border: 0.5pt solid #fc8181; }
.badge--review { background: #fefcbf; color: #744210; border: 0.5pt solid #ecc94b; }
.badge--amendment { background: #feebc8; color: #7b341e; border: 0.5pt solid #f6ad55; }
/* Grid dati pratica */
.meta-grid {
display: table; width: 100%; border-collapse: collapse;
margin: 8pt 0 4pt 0; background: #f7fafc; border: 0.5pt solid #e2e8f0;
}
.meta-grid .row { display: table-row; }
.meta-grid .cell { display: table-cell; padding: 5pt 8pt;
border-bottom: 0.3pt solid #edf2f7; vertical-align: top; }
.meta-grid .cell.label { width: 32%; font-weight: 700; color: #4a5568; font-size: 9pt; background: #edf2f7; }
.meta-grid .cell.val { font-size: 10pt; }
/* Tabelle fatture / ULA / doc */
table.data {
width: 100%; border-collapse: collapse; margin: 4pt 0 8pt 0;
font-size: 8.5pt; border: 0.5pt solid #cbd5e0;
}
table.data th {
background: #2d3748; color: white; padding: 4pt 6pt;
font-weight: 600; text-align: left; border-right: 0.3pt solid #4a5568;
}
table.data td {
padding: 4pt 6pt; border-bottom: 0.3pt solid #e2e8f0;
border-right: 0.3pt solid #edf2f7; vertical-align: top;
}
table.data td.num { text-align: right; font-variant-numeric: tabular-nums; }
table.data tr.subheader td {
background: #ebf4ff; color: #2a4365; font-weight: 700;
border-top: 0.5pt solid #4299e1; border-bottom: 0.5pt solid #4299e1;
padding: 5pt 6pt; font-size: 9pt;
}
table.data tr.totals td {
background: #f7fafc; font-weight: 700; border-top: 0.8pt solid #2d3748;
}
table.data tr.rejected td {
background: #fff5f5; color: #742a2a;
}
table.data tr.partial td {
background: #fffaf0;
}
/* Stato tag inline */
.status-inline {
display: inline-block; padding: 1pt 5pt; border-radius: 3pt;
font-size: 7.5pt; font-weight: 700; letter-spacing: 0.3pt;
}
.status-AMMESSA, .status-VALIDO { background: #c6f6d5; color: #22543d; }
.status-PARZIALE { background: #feebc8; color: #7b341e; }
.status-RESPINTA, .status-NON_VALIDO, .status-SCADUTO { background: #fed7d7; color: #742a2a; }
.status-PENDING { background: #edf2f7; color: #4a5568; }
.note-box {
margin: 4pt 0 6pt 0;
padding: 6pt 8pt;
background: #fffaf0;
border-left: 2pt solid #ed8936;
font-size: 9pt; font-style: italic;
}
.totals-summary {
display: table; width: 100%; margin: 10pt 0;
background: #f7fafc; border: 0.5pt solid #cbd5e0;
}
.totals-summary .row { display: table-row; }
.totals-summary .cell {
display: table-cell; padding: 8pt 10pt; width: 25%;
border-right: 0.3pt solid #e2e8f0; vertical-align: middle;
}
.totals-summary .cell:last-child { border-right: none; }
.totals-summary .lbl { font-size: 8pt; color: #4a5568; text-transform: uppercase; letter-spacing: 0.3pt; }
.totals-summary .val { font-size: 14pt; font-weight: 700; color: #1a365d; margin-top: 2pt; }
.totals-summary .val.final { color: #2b6cb0; }
.totals-summary .val.residuo { color: #c53030; }
.amend-box {
border: 0.5pt solid #f6ad55; border-left: 2pt solid #ed8936;
background: #fffaf0; padding: 6pt 10pt; margin: 4pt 0 6pt 0;
}
.amend-box .head { font-size: 9pt; color: #744210; margin-bottom: 4pt; }
.amend-box .req, .amend-box .resp { font-size: 9pt; margin: 3pt 0; white-space: pre-wrap; }
.amend-box .resp { padding: 4pt 6pt; background: white; border-radius: 2pt; }
/* Firma */
.sign-block {
margin-top: 18pt;
display: table; width: 100%;
}
.sign-block .col { display: table-cell; width: 50%; padding: 6pt 10pt; vertical-align: top; }
.sign-block .lbl { font-size: 9pt; color: #4a5568; font-weight: 700; letter-spacing: 0.3pt; }
.sign-block .val { font-size: 10pt; margin-top: 2pt; }
.sign-block .sig-line {
margin-top: 30pt;
border-top: 0.5pt solid #2d3748;
font-size: 8pt;
color: #4a5568;
padding-top: 2pt;
}
.ok { color: #22543d; font-weight: 700; }
.ko { color: #c53030; font-weight: 700; }
.text-secondary { color: #718096; }
/* Clearfix per header float */
.clearfix::after { content: ""; display: table; clear: both; }
</style>
</head>
<body>
<div class="hdr clearfix">
<div class="hdr__right">
<div><strong>Verbale di istruttoria</strong></div>
<div>Rendicontazione bando</div>
<div>Pratica n. {{ practice.application_id }}{% if max_tranches_snapshot > 1 or practice.sequence_number > 1 %} — Tranche {{ practice.sequence_number }}/{{ max_tranches_snapshot }}{% endif %}</div>
{% if practice.period_label %}<div><small>{{ practice.period_label }}</small></div>{% endif %}
</div>
<div>
<img src="{{ logo_path }}" alt="Gepafin" class="hdr__logo-img" />
<div class="hdr__subtitle">Finanziaria regionale dell'Umbria</div>
</div>
</div>
<h1>Verbale di istruttoria — Rendicontazione</h1>
<p>
{% if practice.status == 'APPROVED' %}
<span class="badge badge--approved">ESITO: APPROVATA</span>
{% elif practice.status == 'REJECTED' %}
<span class="badge badge--rejected">ESITO: RESPINTA</span>
{% elif practice.status == 'AWAITING_AMENDMENT' %}
<span class="badge badge--amendment">SOCCORSO ISTRUTTORIO IN CORSO</span>
{% else %}
<span class="badge badge--review">IN ISTRUTTORIA</span>
{% endif %}
</p>
<h2>Dati pratica</h2>
<div class="meta-grid">
<div class="row">
<div class="cell label">Bando</div>
<div class="cell val">{{ practice.schema_snapshot.template_label or ('Bando #' ~ practice.call_id) }}</div>
</div>
<div class="row">
<div class="cell label">Numero applicazione</div>
<div class="cell val">#{{ practice.application_id }}</div>
</div>
<div class="row">
<div class="cell label">Beneficiario</div>
<div class="cell val">
{{ company.company_name or '(non disponibile)' }}
{% if company.vat_number %} · P.IVA {{ company.vat_number }}{% endif %}
</div>
</div>
<div class="row">
<div class="cell label">Regime IVA</div>
<div class="cell val">{{ practice.iva_regime or '—' }}</div>
</div>
<div class="row">
<div class="cell label">Importo erogato</div>
<div class="cell val">{{ practice.amount_erogato|euro }}</div>
</div>
<div class="row">
<div class="cell label">Periodo rendicontazione</div>
<div class="cell val">
{% set period = practice.schema_snapshot.get('period') or {} %}
{% if period.get('start_date') and period.get('end_date') %}
{{ period.start_date|datefmt }} — {{ period.end_date|datefmt }}
{% else %}—{% endif %}
</div>
</div>
{% if max_tranches_snapshot > 1 %}
<div class="row">
<div class="cell label">Tranche / fase</div>
<div class="cell val">
<strong>Tranche {{ practice.sequence_number }} di {{ max_tranches_snapshot }}</strong>
{% if practice.period_label %} — {{ practice.period_label }}{% endif %}
</div>
</div>
{% endif %}
<div class="row">
<div class="cell label">Data presentazione</div>
<div class="cell val">{{ practice.submitted_at|datetimefmt if practice.submitted_at else '—' }}</div>
</div>
<div class="row">
<div class="cell label">Data istruttoria</div>
<div class="cell val">{{ practice.reviewed_at|datetimefmt if practice.reviewed_at else generated_at }}</div>
</div>
</div>
{# ============ FATTURE ============ #}
<h2>Verifica fatture</h2>
{% if practice.invoices %}
{% set use_taxable = totals.use_taxable_only is not sameas false %}
<table class="data">
<thead>
<tr>
<th style="width:9%"></th>
<th style="width:9%">Data</th>
<th style="width:24%">Fornitore / Descrizione</th>
<th style="width:12%" class="num">{{ 'Imponibile' if use_taxable else 'Totale' }} dichiarato</th>
<th style="width:12%" class="num">{{ 'Imponibile' if use_taxable else 'Totale' }} ammesso</th>
<th style="width:10%">Stato</th>
<th style="width:24%">Motivazione istruttore</th>
</tr>
</thead>
<tbody>
{% for cat_code, items in invoices_by_cat.items() %}
{% set cat_label = categories_map.get(cat_code, cat_code) %}
{% set cat_decl = per_cat_declared.get(cat_code, 0) %}
{% set cat_verif = per_cat_verified.get(cat_code, 0) %}
<tr class="subheader">
<td colspan="3"><strong>{{ cat_code }}</strong> — {{ cat_label }}</td>
<td class="num">{{ cat_decl|euro }}</td>
<td class="num">{{ cat_verif|euro }}</td>
<td colspan="2"><small>{{ items|length }} fatture</small></td>
</tr>
{% for inv in items %}
{% set cls = 'rejected' if inv.verification_status == 'RESPINTA' else ('partial' if inv.verification_status == 'PARZIALE' else '') %}
{% set declared = inv.taxable if use_taxable else inv.total %}
{% if inv.verification_status == 'PENDING' %}
{% set verified_val = None %}
{% elif inv.verification_status == 'RESPINTA' %}
{% set verified_val = 0 %}
{% elif use_taxable %}
{% set verified_val = inv.taxable_verified if inv.taxable_verified is not none else declared %}
{% else %}
{% set verified_val = inv.total_verified if inv.total_verified is not none else declared %}
{% endif %}
<tr class="{{ cls }}">
<td>{{ inv.invoice_number }}</td>
<td>{{ inv.invoice_date|datefmt }}</td>
<td>
<strong>{{ inv.supplier_name }}</strong><br>
<small>{{ inv.description|truncate(80) }}</small>
</td>
<td class="num">{{ declared|euro }}</td>
<td class="num">{{ verified_val|euro if verified_val is not none else '—' }}</td>
<td><span class="status-inline status-{{ inv.verification_status }}">{{ inv.verification_status }}</span></td>
<td>{{ inv.verification_notes or '—' }}</td>
</tr>
{% endfor %}
{% endfor %}
<tr class="totals">
<td colspan="3"><strong>Totale complessivo</strong></td>
<td class="num">{{ totals.grand_total_declared|euro }}</td>
<td class="num">{{ totals.grand_total_verified|euro }}</td>
<td colspan="2"></td>
</tr>
</tbody>
</table>
{% else %}
<p class="text-secondary">Nessuna fattura rendicontata.</p>
{% endif %}
{# ============ ULA ============ #}
{% if ula_section_enabled and practice.ula_employees %}
<h2>Verifica dipendenti ULA</h2>
<p><small>Soglia incremento richiesta: <strong>≥ {{ '%.2f'|format(ula_threshold) }}</strong> · FTE dichiarato: <strong>{{ '%.2f'|format(ula_fte_decl) }}</strong> · FTE ammesso: <strong class="{{ 'ok' if ula_ok else 'ko' }}">{{ '%.2f'|format(ula_fte_verif) }}</strong></small></p>
<table class="data">
<thead>
<tr>
<th style="width:16%">CF</th>
<th style="width:18%">Dipendente</th>
<th style="width:14%">Contratto</th>
<th style="width:16%">Periodo</th>
<th style="width:8%" class="num">FTE dich.</th>
<th style="width:8%" class="num">FTE amm.</th>
<th style="width:8%">Stato</th>
<th style="width:12%">Note</th>
</tr>
</thead>
<tbody>
{% for emp in practice.ula_employees %}
{% set cls = 'rejected' if emp.verification_status == 'RESPINTA' else ('partial' if emp.verification_status == 'PARZIALE' else '') %}
{% set fte_verif = emp.fte_pct_verified if emp.fte_pct_verified is not none else emp.fte_pct %}
<tr class="{{ cls }}">
<td>{{ emp.codice_fiscale }}</td>
<td>{{ emp.full_name }}</td>
<td>{{ contract_labels.get(emp.contract_type, emp.contract_type) }}</td>
<td>{{ emp.period_start_date|datefmt }}<br><small>→ {{ emp.period_end_date|datefmt }}</small></td>
<td class="num">{{ '%.2f'|format(emp.fte_pct|float) }}</td>
<td class="num">
{% if emp.verification_status == 'PENDING' %}—
{% elif emp.verification_status == 'RESPINTA' %}0.00
{% else %}{{ '%.2f'|format(fte_verif|float) }}
{% endif %}
</td>
<td><span class="status-inline status-{{ emp.verification_status }}">{{ emp.verification_status }}</span></td>
<td>{{ emp.verification_notes|default('—') }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{# ============ DOCUMENTI ============ #}
<h2>Verifica documenti</h2>
{% if docs_required %}
<table class="data">
<thead>
<tr>
<th style="width:25%">Documento</th>
<th style="width:20%">File allegato</th>
<th style="width:12%">Esito</th>
<th style="width:43%">Note istruttore</th>
</tr>
</thead>
<tbody>
{% for dr in docs_required %}
{% set doc = docs_by_code.get(dr.code, {}) %}
{% set stat = doc.verification_status or 'PENDING' %}
{% set cls = 'rejected' if stat in ('NON_VALIDO', 'SCADUTO') else '' %}
<tr class="{{ cls }}">
<td><strong>{{ dr.label }}</strong><br><small>{{ dr.code }}</small></td>
<td>
{% if doc.filename %}<i>{{ doc.filename }}</i>
{% else %}<span class="ko">non caricato</span>
{% endif %}
</td>
<td><span class="status-inline status-{{ stat }}">{{ stat }}</span></td>
<td>{{ doc.verification_notes|default('—') }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-secondary">Nessun documento richiesto dallo schema del bando.</p>
{% endif %}
{# ============ CONTROLLI AGGIUNTIVI ============ #}
{% if custom_checks_merged %}
<h2>Controlli aggiuntivi (dichiarazioni beneficiario)</h2>
<table class="data">
<thead>
<tr>
<th style="width:32%">Controllo</th>
<th style="width:12%">Obbligatorio</th>
<th style="width:10%">Dichiarato</th>
<th style="width:11%">Doc. allegato</th>
<th style="width:11%">Validazione</th>
<th style="width:24%">Note istruttore</th>
</tr>
</thead>
<tbody>
{% for cc in custom_checks_merged %}
{% set stat = cc.verification_status or 'PENDING' %}
{% set cls = 'rejected' if stat == 'NON_VALIDO' else '' %}
{% set missing = cc.required and not cc.beneficiary_declared %}
<tr class="{{ 'rejected' if missing else cls }}">
<td>
<strong>{{ cc.label or cc.code }}</strong>
{% if cc.description %}<br><small>{{ cc.description|truncate(180) }}</small>{% endif %}
</td>
<td>{% if cc.required %}<span class="ko"></span>{% else %}<small class="text-secondary">opzionale</small>{% endif %}</td>
<td>{% if cc.beneficiary_declared %}<span class="ok"></span>{% else %}<span class="ko">NO</span>{% endif %}</td>
<td>
{% if cc.requires_document %}
{% if cc.has_document %}<span class="ok"></span>{% else %}<span class="ko">NO</span>{% endif %}
{% else %}<small class="text-secondary">non richiesto</small>{% endif %}
</td>
<td><span class="status-inline status-{{ stat }}">{{ stat }}</span></td>
<td>{{ cc.verification_notes or '—' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{# ============ SOCCORSI ============ #}
{% if amendments %}
<h2>Soccorso istruttorio</h2>
{% for a in amendments %}
<div class="amend-box">
<div class="head">
<strong>{{ a.status|amendstatus }}</strong>
· deadline {{ a.deadline|datefmt }}
· aperto il {{ a.created_at|datetimefmt }}
{% if a.closed_at %} · chiuso il {{ a.closed_at|datetimefmt }}{% endif %}
</div>
<div class="req"><strong>Richiesta istruttore:</strong><br>{{ a.request_text }}</div>
{% if a.response_text %}
<div class="resp"><strong>Risposta beneficiario</strong> ({{ a.response_at|datetimefmt }}):<br>{{ a.response_text }}</div>
{% endif %}
</div>
{% endfor %}
{% endif %}
{# ============ STORICO TRANCHES PRECEDENTI ============ #}
{% if previous_tranches %}
<h2>Storico tranches precedenti</h2>
<table class="data">
<thead>
<tr>
<th style="width:8%">#</th>
<th style="width:35%">Periodo / fase</th>
<th style="width:17%">Data approvazione</th>
<th style="width:20%" class="num">Importo ammesso</th>
<th style="width:20%" class="num">Cumulativo</th>
</tr>
</thead>
<tbody>
{% for t in previous_tranches %}
<tr>
<td><strong>T{{ t.sequence_number }}</strong></td>
<td>{{ t.period_label or '—' }}</td>
<td>{{ t.reviewed_at|datefmt }}</td>
<td class="num">{{ t.approved_remission|euro }}</td>
<td class="num"><strong>{{ t.cumulative|euro }}</strong></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{# ============ 5 VOCI UFFICIALI CECILIA ============ #}
<h2>Riepilogo finanziario (cap tranche {{ practice.sequence_number }})</h2>
<div class="totals-summary">
<div class="row">
<div class="cell" style="width:50%">
<div class="lbl">(1) Importo massimo ammissibile (cap globale)</div>
<div class="val">{{ totals.max_remission_global|euro }}</div>
{% if totals.already_approved_previous_tranches > 0 %}
<div class="lbl" style="margin-top:4pt">già approvato nelle tranche precedenti</div>
<div style="font-size:10pt; font-weight:700; color:#744210"> {{ totals.already_approved_previous_tranches|euro }}</div>
<div class="lbl" style="margin-top:4pt">max. disponibile per questa tranche</div>
<div style="font-size:11pt; font-weight:700; color:#2b6cb0">= {{ totals.max_remission_this_tranche|euro }}</div>
{% endif %}
</div>
<div class="cell" style="width:50%">
<div class="lbl">(4) Importo finanziamento erogato</div>
<div class="val">{{ totals.amount_erogato|euro }}</div>
<div class="lbl" style="margin-top:6pt">tranches complessive</div>
<div style="font-size:10pt">{{ totals.tranches_count }} / {{ totals.tranches_max }}</div>
</div>
</div>
</div>
<div class="totals-summary" style="margin-top:8pt">
<div class="row">
<div class="cell" style="width:33%">
<div class="lbl">(2) Richiesto pre-controllo (ammissibile)</div>
<div class="val">{{ totals.pre_check_admissible|euro }}</div>
<div class="lbl" style="margin-top:4pt">dichiarato tranche</div>
<div style="font-size:9pt">{{ totals.grand_total_declared|euro }}</div>
</div>
<div class="cell" style="width:33%">
<div class="lbl">(3) Ammesso post-controllo istruttore</div>
<div class="val final">{{ totals.remission_due|euro }}</div>
{% if totals.any_verified %}
<div class="lbl" style="margin-top:4pt">verificato tranche</div>
<div style="font-size:9pt">{{ totals.grand_total_verified|euro }}</div>
{% else %}
<div class="lbl" style="margin-top:4pt"><em>in attesa di verifica istruttore</em></div>
{% endif %}
</div>
<div class="cell" style="width:34%; background:#fff5f5">
<div class="lbl">(5) Residuo da restituire</div>
<div class="val residuo">{{ totals.residuo_da_restituire|euro }}</div>
<div class="lbl" style="margin-top:4pt">= erogato approvato precedente ammesso tranche</div>
</div>
</div>
</div>
{% if practice.status == 'APPROVED' %}
<div class="totals-summary" style="margin-top:8pt; background:#f0fff4; border-color:#68d391">
<div class="row">
<div class="cell" style="width:100%">
<div class="lbl">REMISSIONE APPROVATA PER QUESTA TRANCHE</div>
<div class="val" style="color:#22543d; font-size:18pt">{{ practice.approved_remission|euro }}</div>
</div>
</div>
</div>
{% endif %}
{# ============ CHECKLIST + NOTE ============ #}
{% set checklist = practice.instructor_checklist or {} %}
{% if checklist %}
<h3>Checklist finale istruttore</h3>
<ul style="font-size:9pt; margin: 4pt 0 6pt 14pt;">
<li>Documentazione completa e coerente: {{ 'SÌ' if checklist.get('domanda_completa') else 'NO' }}</li>
<li>Incremento ULA &gt; 1 verificato: {{ 'SÌ' if checklist.get('ula_ok') else 'NO' }}</li>
<li>Importo erogato entro il range bando: {{ 'SÌ' if checklist.get('erogato_in_range') else 'NO' }}</li>
</ul>
{% endif %}
{% if practice.instructor_final_notes %}
<h3>Note sintetiche di istruttoria</h3>
<div class="note-box">{{ practice.instructor_final_notes }}</div>
{% endif %}
{% if practice.rejection_reason %}
<h3>Motivazione del rigetto</h3>
<div class="note-box" style="background:#fff5f5; border-left-color:#fc8181;">{{ practice.rejection_reason }}</div>
{% endif %}
{# ============ FIRMA ============ #}
<div class="sign-block">
<div class="col">
<div class="lbl">LUOGO E DATA</div>
<div class="val">Perugia, {{ generated_at }}</div>
<div class="sig-line">&nbsp;</div>
</div>
<div class="col">
<div class="lbl">ISTRUTTORE</div>
<div class="val">{{ instructor_name or '(firma)' }}</div>
<div class="sig-line">Firma</div>
</div>
</div>
</body>
</html>