SIRET/RNA verification: - Create SiretService with API gouv lookup + JOAFE RNA lookup + cache pool (24h) - Verification page: declared info vs API data side by side - Display NAF code + label (from naf.json), nature juridique code + label - Association/Entreprise/EI badges, ESS badge, RNA, coordonnees lat/long - JOAFE section: objet, regime, domaine, dates, lieu, PDF download link - Tranche effectif with readable labels - Refresh cache button - Page restricted to non-approved organizers only Organizer approval flow: - Approval form with offer (free/basic/custom) and commission rate (default 3%) - Add commissionRate field to User entity + migration - Rejection form with required reason textarea, sent in email - Edit page for approved organizers: all fields modifiable - Modify button in approved organizers table Registration flow pages: - Post-registration success page with email verification message - Organizer gets additional 48h staff review notice - Post-email-verification page: confirmed for buyers, 48h notice for organizers Dashboard: - Simplified Meilisearch sync to single button Tests: SiretServiceTest (9), AdminControllerTest (31), RegistrationControllerTest updated, UserTest updated Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
262 lines
18 KiB
Twig
262 lines
18 KiB
Twig
{% extends 'admin/base.html.twig' %}
|
|
|
|
{% block title %}Verification Entreprise / Association - {{ siret }}{% endblock %}
|
|
|
|
{% block body %}
|
|
<div style="margin-bottom:2rem;">
|
|
<h1 class="text-3xl font-black uppercase tracking-tighter italic" style="border-bottom:4px solid #111827;display:inline-block;margin-bottom:0.5rem;">Verification Entreprise / Association</h1>
|
|
<p class="font-bold text-gray-500 italic">{{ user.firstName }} {{ user.lastName }} — {{ siret }}</p>
|
|
</div>
|
|
|
|
<div style="display:flex;flex-wrap:wrap;gap:1.5rem;">
|
|
<div style="flex:1;min-width:300px;">
|
|
<div style="border:4px solid #111827;box-shadow:6px 6px 0 rgba(0,0,0,1);background:white;padding:1.5rem;">
|
|
<h2 class="text-sm font-black uppercase tracking-widest" style="margin-bottom:1rem;">Informations declarees</h2>
|
|
<table style="width:100%;border-collapse:collapse;">
|
|
<tbody>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Nom</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">{{ user.firstName }} {{ user.lastName }}</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Raison sociale</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">{{ user.companyName }}</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">SIRET</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm font-mono">{{ user.siret }}</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Adresse</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">{{ user.address }}, {{ user.postalCode }} {{ user.city }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Telephone</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">{{ user.phone }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="flex:1;min-width:300px;">
|
|
<div style="border:4px solid #111827;box-shadow:6px 6px 0 rgba(0,0,0,1);background:white;padding:1.5rem;">
|
|
<h2 class="text-sm font-black uppercase tracking-widest" style="margin-bottom:1rem;">Donnees API Gouvernement</h2>
|
|
|
|
{% if data %}
|
|
<div style="margin-bottom:1rem;display:flex;gap:0.5rem;flex-wrap:wrap;">
|
|
{% if data.complements is defined and data.complements.est_association is defined and data.complements.est_association %}
|
|
<span style="background:#dbeafe;border:2px solid #111827;padding:0.15rem 0.75rem;" class="text-xs font-black uppercase">Association</span>
|
|
{% elseif data.complements is defined and data.complements.est_entrepreneur_individuel is defined and data.complements.est_entrepreneur_individuel %}
|
|
<span style="background:#fef3c7;border:2px solid #111827;padding:0.15rem 0.75rem;" class="text-xs font-black uppercase">Entrepreneur individuel</span>
|
|
{% else %}
|
|
<span style="background:#e0e7ff;border:2px solid #111827;padding:0.15rem 0.75rem;" class="text-xs font-black uppercase">Entreprise</span>
|
|
{% endif %}
|
|
{% if data.complements is defined and data.complements.est_ess is defined and data.complements.est_ess %}
|
|
<span style="background:#d1fae5;border:2px solid #111827;padding:0.15rem 0.75rem;" class="text-xs font-black uppercase">ESS</span>
|
|
{% endif %}
|
|
</div>
|
|
<table style="width:100%;border-collapse:collapse;">
|
|
<tbody>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Denomination</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">{{ data.nom_complet ?? '—' }}</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">SIREN</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm font-mono">{{ data.siren ?? '—' }}</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Nature juridique</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">
|
|
{% if data.nature_juridique is defined and data.nature_juridique %}
|
|
<span class="font-mono">{{ data.nature_juridique }}</span> — {{ data.libelle_nature_juridique ?? '' }}
|
|
{% else %}
|
|
—
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Siege</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">
|
|
{% if data.siege is defined and data.siege %}
|
|
{{ data.siege.adresse ?? '' }}, {{ data.siege.code_postal ?? '' }} {{ data.siege.libelle_commune ?? '' }}
|
|
{% else %}
|
|
—
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Activite (NAF)</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">
|
|
{% if data.activite_principale is defined and data.activite_principale %}
|
|
<span class="font-mono">{{ data.activite_principale }}</span> — {{ data.libelle_activite_principale ?? '' }}
|
|
{% else %}
|
|
—
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Tranche effectif</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">
|
|
{% set te = data.tranche_effectif_salarie ?? 'NN' %}
|
|
{% set te_labels = {
|
|
'NN': 'Non renseigne',
|
|
'00': '0 salarie',
|
|
'01': '1 ou 2 salaries',
|
|
'02': '3 a 5 salaries',
|
|
'03': '6 a 9 salaries',
|
|
'11': '10 a 19 salaries',
|
|
'12': '20 a 49 salaries',
|
|
'21': '50 a 99 salaries',
|
|
'22': '100 a 199 salaries',
|
|
'31': '200 a 249 salaries',
|
|
'32': '250 a 499 salaries',
|
|
'41': '500 a 999 salaries',
|
|
'42': '1 000 a 1 999 salaries',
|
|
'51': '2 000 a 4 999 salaries',
|
|
'52': '5 000 a 9 999 salaries',
|
|
'53': '10 000 salaries et plus'
|
|
} %}
|
|
{{ te_labels[te] ?? te }}
|
|
</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Date creation</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">{{ data.date_creation ?? '—' }}</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Statut</td>
|
|
<td style="padding:0.5rem 0;">
|
|
{% if data.etat_administratif is defined and data.etat_administratif == 'A' %}
|
|
<span style="background:#d1fae5;border:2px solid #111827;padding:0.15rem 0.5rem;" class="text-xs font-black uppercase">Active</span>
|
|
{% else %}
|
|
<span style="background:#fee2e2;border:2px solid #111827;padding:0.15rem 0.5rem;" class="text-xs font-black uppercase">Fermee</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% if data.complements is defined and data.complements.est_association is defined and data.complements.est_association %}
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">RNA</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm font-mono">{{ data.complements.identifiant_association ?? '—' }}</td>
|
|
</tr>
|
|
{% endif %}
|
|
<tr>
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Coordonnees</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">
|
|
{% if data.siege is defined and data.siege.latitude is defined and data.siege.longitude is defined and data.siege.latitude and data.siege.longitude %}
|
|
<span class="font-mono">{{ data.siege.latitude }}, {{ data.siege.longitude }}</span>
|
|
{% else %}
|
|
—
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
{% else %}
|
|
<div style="padding:2rem;text-align:center;">
|
|
<span style="background:#fee2e2;border:2px solid #111827;padding:0.25rem 0.75rem;" class="text-xs font-black uppercase">SIRET non trouve</span>
|
|
<p class="text-gray-400 text-sm font-bold" style="margin-top:0.5rem;">Aucun resultat retourne par l'API pour ce SIRET.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if rnaData %}
|
|
<div style="margin-top:1.5rem;">
|
|
<div style="border:4px solid #111827;box-shadow:6px 6px 0 rgba(0,0,0,1);background:white;padding:1.5rem;">
|
|
<h2 class="text-sm font-black uppercase tracking-widest" style="margin-bottom:1rem;">Journal Officiel (JOAFE)</h2>
|
|
<table style="width:100%;border-collapse:collapse;">
|
|
<tbody>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;width:180px;" class="font-bold text-sm text-gray-400">RNA</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm font-mono">{{ rnaData.numero_rna ?? '—' }}</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Regime</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">{{ rnaData.association_type_libelle ?? '—' }}</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;vertical-align:top;" class="font-bold text-sm text-gray-400">Objet</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">{{ rnaData.objet ?? '—' }}</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Domaine d'activite</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">{{ rnaData.domaine_activite_libelle_categorise ?? '—' }}</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Date declaration</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">{{ rnaData.datedeclaration ?? '—' }}</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Date parution JOAFE</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">{{ rnaData.dateparution ?? '—' }}</td>
|
|
</tr>
|
|
<tr style="border-bottom:1px solid #e5e7eb;">
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">Lieu declaration</td>
|
|
<td style="padding:0.5rem 0;" class="text-sm">{{ rnaData.lieupref ?? '—' }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:0.5rem 0;" class="font-bold text-sm text-gray-400">PDF JOAFE</td>
|
|
<td style="padding:0.5rem 0;">
|
|
{% if rnaData.url_pdf is defined and rnaData.url_pdf %}
|
|
<a href="{{ rnaData.url_pdf }}" target="_blank" style="border:2px solid #111827;padding:0.15rem 0.5rem;background:#fabf04;display:inline-block;" class="text-xs font-black uppercase hover:bg-indigo-600 hover:text-black transition-all">Telecharger</a>
|
|
{% else %}
|
|
—
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div style="margin-top:2rem;display:flex;gap:0.75rem;flex-wrap:wrap;">
|
|
<form method="post" action="{{ path('app_admin_siret_refresh', {id: user.id}) }}" style="display:inline;">
|
|
<button type="submit" style="padding:0.5rem 1rem;border:3px solid #111827;background:white;cursor:pointer;" class="font-black uppercase text-xs tracking-widest hover:bg-indigo-600 hover:text-black transition-all">Rafraichir SIRET</button>
|
|
</form>
|
|
<a href="{{ path('app_admin_organizers') }}" style="padding:0.5rem 1rem;border:3px solid #111827;background:white;display:inline-block;" class="font-black uppercase text-xs tracking-widest hover:bg-gray-100 transition-all">Retour</a>
|
|
</div>
|
|
|
|
<div style="display:flex;flex-wrap:wrap;gap:1.5rem;margin-top:2rem;">
|
|
<div style="flex:1;min-width:300px;">
|
|
<div style="border:4px solid #111827;box-shadow:6px 6px 0 rgba(0,0,0,1);background:white;padding:1.5rem;">
|
|
<h2 class="text-sm font-black uppercase tracking-widest" style="margin-bottom:1rem;color:#16a34a;">Approuver l'organisateur</h2>
|
|
<form method="post" action="{{ path('app_admin_approve_organizer', {id: user.id}) }}" style="display:flex;flex-direction:column;gap:1rem;">
|
|
<div>
|
|
<label for="approve_offer" style="font-size:10px;letter-spacing:0.1em;display:block;margin-bottom:0.5rem;" class="font-black uppercase text-gray-400">Offre</label>
|
|
<select id="approve_offer" name="offer" style="width:100%;padding:0.5rem 0.75rem;border:3px solid #111827;font-weight:700;outline:none;cursor:pointer;">
|
|
<option value="free">Gratuit</option>
|
|
<option value="basic">Basique</option>
|
|
<option value="custom">Sur-mesure</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="approve_commission" style="font-size:10px;letter-spacing:0.1em;display:block;margin-bottom:0.5rem;" class="font-black uppercase text-gray-400">Taux de commission (%)</label>
|
|
<input type="number" id="approve_commission" name="commission_rate" value="3" step="0.1" min="0" max="100"
|
|
style="width:100%;padding:0.5rem 0.75rem;border:3px solid #111827;font-weight:700;outline:none;">
|
|
</div>
|
|
<button type="submit" style="padding:0.5rem 1rem;border:3px solid #111827;box-shadow:4px 4px 0 rgba(0,0,0,1);background:#fabf04;cursor:pointer;" class="font-black uppercase text-xs tracking-widest hover:bg-green-500 hover:text-black transition-all">Approuver l'organisateur</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="flex:1;min-width:300px;">
|
|
<div style="border:4px solid #991b1b;box-shadow:6px 6px 0 rgba(0,0,0,1);background:white;padding:1.5rem;">
|
|
<h2 class="text-sm font-black uppercase tracking-widest" style="margin-bottom:1rem;color:#991b1b;">Refuser la demande</h2>
|
|
<form method="post" action="{{ path('app_admin_reject_organizer', {id: user.id}) }}" data-confirm="Etes-vous sur de vouloir refuser et supprimer le compte de {{ user.firstName }} {{ user.lastName }} ? Cette action est irreversible." style="display:flex;flex-direction:column;gap:1rem;">
|
|
<div>
|
|
<label for="reject_reason" style="font-size:10px;letter-spacing:0.1em;display:block;margin-bottom:0.5rem;" class="font-black uppercase text-gray-400">Motif du refus</label>
|
|
<textarea id="reject_reason" name="reason" required rows="4"
|
|
style="width:100%;padding:0.75rem 1rem;border:3px solid #111827;font-weight:700;outline:none;resize:vertical;"
|
|
placeholder="Expliquez la raison du refus..."></textarea>
|
|
</div>
|
|
<button type="submit" style="padding:0.5rem 1rem;border:3px solid #991b1b;box-shadow:4px 4px 0 rgba(0,0,0,1);background:#dc2626;color:white;cursor:pointer;" class="font-black uppercase text-xs tracking-widest hover:bg-red-800 transition-all">Refuser et supprimer le compte</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|