Payout system:
- Create Payout entity (stripePayoutId, status, amount, currency, destination, arrivalDate)
- Webhook handles payout.created/updated/paid/failed/canceled with email notification
- Payout list in /mon-compte virements tab with status badges
- PDF attestation on paid payouts with email attachment
PDF attestation:
- dompdf with DejaVu Sans font, yellow-orange gradient background
- Orange centered title bar, E-Cosplay logo, emitter/beneficiary info blocks
- QR code linking to /attestation/check/{payoutId} for authenticity verification
- Public verification page: shows payout details if valid, error if altered
- Legal disclaimer and CGV reference
- Button visible only when status is paid, opens in new tab
Sub-accounts:
- Add parentOrganizer (self-referencing ManyToOne) and subAccountPermissions (JSON) to User
- Permissions: scanner (validate tickets), events (CRUD), tickets (free invitations)
- Create sub-account with random password, send email with credentials
- Edit page with name/email/permissions checkboxes
- Delete with confirmation
- hasPermission() helper method
Account improvements:
- Block entire page for unapproved organizers with validation pending message
- Display stripeStatus in Stripe Connect banners
- Remove test payout button
Webhook v2 Connect events:
- v2.core.account.created/updated/closed → update stripeStatus
- capability_status_updated → sync charges/payouts enabled from capabilities
- PayoutPdfService for reusable PDF generation
Migrations: stripeStatus, Payout table, sub-account fields, drop pdfPath
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
62 lines
4.4 KiB
Twig
62 lines
4.4 KiB
Twig
{% extends 'base.html.twig' %}
|
|
|
|
{% block title %}Modifier sous-compte - E-Ticket{% endblock %}
|
|
|
|
{% block body %}
|
|
<div style="max-width:36rem;margin:0 auto;padding:3rem 1rem;">
|
|
<h1 class="text-3xl font-black uppercase tracking-tighter italic" style="border-bottom:4px solid #111827;display:inline-block;margin-bottom:0.5rem;">Modifier le sous-compte</h1>
|
|
<p class="font-bold text-gray-500 italic" style="margin-bottom:2rem;">{{ subAccount.firstName }} {{ subAccount.lastName }}</p>
|
|
|
|
<div style="border:4px solid #111827;box-shadow:6px 6px 0 rgba(0,0,0,1);background:white;padding:1.5rem;">
|
|
<form method="post" action="{{ path('app_account_edit_subaccount', {id: subAccount.id}) }}" style="display:flex;flex-direction:column;gap:1.5rem;">
|
|
<div style="display:flex;flex-wrap:wrap;gap:1.5rem;">
|
|
<div style="flex:1;min-width:150px;">
|
|
<label for="edit_sub_last" style="font-size:10px;letter-spacing:0.1em;display:block;margin-bottom:0.5rem;" class="font-black uppercase text-gray-400">Nom</label>
|
|
<input type="text" id="edit_sub_last" name="last_name" value="{{ subAccount.lastName }}" required
|
|
style="width:100%;padding:0.75rem 1rem;border:3px solid #111827;font-weight:700;outline:none;">
|
|
</div>
|
|
<div style="flex:1;min-width:150px;">
|
|
<label for="edit_sub_first" style="font-size:10px;letter-spacing:0.1em;display:block;margin-bottom:0.5rem;" class="font-black uppercase text-gray-400">Prenom</label>
|
|
<input type="text" id="edit_sub_first" name="first_name" value="{{ subAccount.firstName }}" required
|
|
style="width:100%;padding:0.75rem 1rem;border:3px solid #111827;font-weight:700;outline:none;">
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="edit_sub_email" style="font-size:10px;letter-spacing:0.1em;display:block;margin-bottom:0.5rem;" class="font-black uppercase text-gray-400">Email</label>
|
|
<input type="email" id="edit_sub_email" name="email" value="{{ subAccount.email }}" required
|
|
style="width:100%;padding:0.75rem 1rem;border:3px solid #111827;font-weight:700;outline:none;">
|
|
</div>
|
|
|
|
<div>
|
|
<p style="font-size:10px;letter-spacing:0.1em;margin-bottom:0.5rem;" class="font-black uppercase text-gray-400">Permissions</p>
|
|
<div style="display:flex;flex-direction:column;gap:0.75rem;">
|
|
<label style="display:flex;align-items:center;gap:0.5rem;cursor:pointer;" class="text-sm font-bold">
|
|
<input type="checkbox" name="permissions[]" value="scanner" {{ subAccount.hasPermission('scanner') ? 'checked' : '' }} style="width:1.25rem;height:1.25rem;"> Scanner (valider les billets)
|
|
</label>
|
|
<label style="display:flex;align-items:center;gap:0.5rem;cursor:pointer;" class="text-sm font-bold">
|
|
<input type="checkbox" name="permissions[]" value="events" {{ subAccount.hasPermission('events') ? 'checked' : '' }} style="width:1.25rem;height:1.25rem;"> Evenements (creer, modifier, supprimer)
|
|
</label>
|
|
<label style="display:flex;align-items:center;gap:0.5rem;cursor:pointer;" class="text-sm font-bold">
|
|
<input type="checkbox" name="permissions[]" value="tickets" {{ subAccount.hasPermission('tickets') ? 'checked' : '' }} style="width:1.25rem;height:1.25rem;"> Billets (invitations gratuites)
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="display:flex;gap:0.75rem;">
|
|
<button type="submit"
|
|
style="padding:0.75rem 1.5rem;border:3px solid #111827;box-shadow:4px 4px 0 rgba(0,0,0,1);background:#fabf04;cursor:pointer;"
|
|
class="font-black uppercase text-sm tracking-widest hover:bg-green-500 hover:text-black transition-all">
|
|
Enregistrer
|
|
</button>
|
|
<a href="{{ path('app_account', {tab: 'subaccounts'}) }}"
|
|
style="padding:0.75rem 1.5rem;border:3px solid #111827;display:inline-flex;align-items:center;"
|
|
class="font-black uppercase text-sm tracking-widest bg-white hover:bg-gray-100 transition-all">
|
|
Annuler
|
|
</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|