Add offer and commission rate to organizer invitation
- offer (free/basic/custom) and commissionRate fields on OrganizerInvitation - Admin form: select offer + commission rate input - Invitation list: show offer badge + rate - Email: gold banner with proposed offer and commission rate (hors Stripe) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
28
migrations/Version20260322110000.php
Normal file
28
migrations/Version20260322110000.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260322110000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add offer and commission_rate to organizer_invitation';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE organizer_invitation ADD COLUMN IF NOT EXISTS offer VARCHAR(20) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE organizer_invitation ADD COLUMN IF NOT EXISTS commission_rate DOUBLE PRECISION DEFAULT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE organizer_invitation DROP COLUMN IF EXISTS offer');
|
||||
$this->addSql('ALTER TABLE organizer_invitation DROP COLUMN IF EXISTS commission_rate');
|
||||
}
|
||||
}
|
||||
@@ -454,6 +454,8 @@ class AdminController extends AbstractController
|
||||
$lastName = trim($request->request->getString('last_name'));
|
||||
$email = trim($request->request->getString('email'));
|
||||
$message = trim($request->request->getString('message')) ?: null;
|
||||
$offer = $request->request->getString('offer', 'free');
|
||||
$commissionRate = (float) $request->request->getString('commission_rate', '3');
|
||||
|
||||
if ('' === $companyName || '' === $firstName || '' === $lastName || '' === $email) {
|
||||
$this->addFlash('error', 'Tous les champs obligatoires doivent etre remplis.');
|
||||
@@ -467,6 +469,8 @@ class AdminController extends AbstractController
|
||||
$invitation->setLastName($lastName);
|
||||
$invitation->setEmail($email);
|
||||
$invitation->setMessage($message);
|
||||
$invitation->setOffer($offer);
|
||||
$invitation->setCommissionRate($commissionRate);
|
||||
|
||||
$em->persist($invitation);
|
||||
$em->flush();
|
||||
|
||||
@@ -36,6 +36,12 @@ class OrganizerInvitation
|
||||
#[ORM\Column(length: 20)]
|
||||
private string $status = self::STATUS_SENT;
|
||||
|
||||
#[ORM\Column(length: 20, nullable: true)]
|
||||
private ?string $offer = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?float $commissionRate = null;
|
||||
|
||||
#[ORM\Column(length: 64, unique: true)]
|
||||
private string $token;
|
||||
|
||||
@@ -128,6 +134,30 @@ class OrganizerInvitation
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOffer(): ?string
|
||||
{
|
||||
return $this->offer;
|
||||
}
|
||||
|
||||
public function setOffer(?string $offer): static
|
||||
{
|
||||
$this->offer = $offer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCommissionRate(): ?float
|
||||
{
|
||||
return $this->commissionRate;
|
||||
}
|
||||
|
||||
public function setCommissionRate(?float $commissionRate): static
|
||||
{
|
||||
$this->commissionRate = $commissionRate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getToken(): string
|
||||
{
|
||||
return $this->token;
|
||||
|
||||
@@ -32,6 +32,20 @@
|
||||
<input type="text" id="inv_first_name" name="first_name" required class="admin-form-input" placeholder="Jean">
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<label for="inv_offer" class="block text-[10px] font-black uppercase tracking-widest text-gray-500 mb-1">Offre</label>
|
||||
<select id="inv_offer" name="offer" class="admin-form-input">
|
||||
<option value="free">Gratuit</option>
|
||||
<option value="basic">Basic</option>
|
||||
<option value="custom">Sur-mesure</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="inv_commission" class="block text-[10px] font-black uppercase tracking-widest text-gray-500 mb-1">Taux de commission E-Ticket (%)</label>
|
||||
<input type="number" id="inv_commission" name="commission_rate" step="0.1" min="0" max="100" value="3" class="admin-form-input" placeholder="3">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="inv_message" class="block text-[10px] font-black uppercase tracking-widest text-gray-500 mb-1">Message personnalise (optionnel)</label>
|
||||
<textarea id="inv_message" name="message" rows="3" class="admin-form-input" placeholder="Bonjour, nous vous invitons a rejoindre E-Ticket..."></textarea>
|
||||
@@ -47,6 +61,7 @@
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-white">Organisateur</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-white">Raison sociale</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-white">Email</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-white">Offre</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-white">Date</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-white text-right">Statut</th>
|
||||
</tr>
|
||||
@@ -59,6 +74,10 @@
|
||||
</td>
|
||||
<td class="text-sm text-gray-600">{{ inv.companyName }}</td>
|
||||
<td class="text-sm text-gray-600">{{ inv.email }}</td>
|
||||
<td class="text-sm text-gray-600">
|
||||
{% if inv.offer %}<span class="admin-badge-indigo text-xs font-black uppercase">{{ inv.offer }}</span>{% endif %}
|
||||
{% if inv.commissionRate is not null %}<span class="text-xs text-gray-400 ml-1">{{ inv.commissionRate }}%</span>{% endif %}
|
||||
</td>
|
||||
<td class="text-sm text-gray-400">{{ inv.createdAt|date('d/m/Y H:i') }}</td>
|
||||
<td class="text-right">
|
||||
{% if inv.status == 'sent' %}
|
||||
@@ -82,7 +101,7 @@
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5" class="!text-center !py-12 text-gray-400 font-bold text-sm">Aucune invitation envoyee.</td>
|
||||
<td colspan="6" class="!text-center !py-12 text-gray-400 font-bold text-sm">Aucune invitation envoyee.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@@ -7,6 +7,19 @@
|
||||
<p>Bonjour {{ invitation.firstName }},</p>
|
||||
<p>L'equipe <strong>E-Ticket by E-Cosplay</strong> vous invite a rejoindre la plateforme en tant qu'organisateur pour <strong>{{ invitation.companyName }}</strong>.</p>
|
||||
|
||||
{% if invitation.offer or invitation.commissionRate is not null %}
|
||||
<div style="padding: 16px; background: #fabf04; border: 3px solid #111827; margin: 20px 0;">
|
||||
<p style="margin: 0; font-size: 14px; font-weight: 900; color: #111827; text-transform: uppercase;">
|
||||
L'equipe E-Ticket vous propose l'offre
|
||||
{% if invitation.offer == 'free' %}Gratuit{% elseif invitation.offer == 'basic' %}Basic{% elseif invitation.offer == 'custom' %}Sur-mesure{% else %}{{ invitation.offer }}{% endif %}
|
||||
{% if invitation.commissionRate is not null %}
|
||||
avec un taux de commission de {{ invitation.commissionRate }}%
|
||||
{% endif %}
|
||||
</p>
|
||||
<p style="margin: 6px 0 0; font-size: 12px; font-weight: 700; color: #374151;">(hors frais de commission Stripe)</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if invitation.message %}
|
||||
<div style="padding: 16px; background: #f9fafb; border-left: 4px solid #fabf04; margin: 20px 0;">
|
||||
<p style="margin: 0; font-style: italic; color: #374151;">{{ invitation.message }}</p>
|
||||
|
||||
@@ -17,6 +17,8 @@ class OrganizerInvitationTest extends TestCase
|
||||
self::assertNull($inv->getLastName());
|
||||
self::assertNull($inv->getEmail());
|
||||
self::assertNull($inv->getMessage());
|
||||
self::assertNull($inv->getOffer());
|
||||
self::assertNull($inv->getCommissionRate());
|
||||
self::assertSame(OrganizerInvitation::STATUS_SENT, $inv->getStatus());
|
||||
self::assertSame(64, \strlen($inv->getToken()));
|
||||
self::assertNull($inv->getRespondedAt());
|
||||
@@ -56,6 +58,24 @@ class OrganizerInvitationTest extends TestCase
|
||||
self::assertNull($inv->getMessage());
|
||||
}
|
||||
|
||||
public function testSetAndGetOffer(): void
|
||||
{
|
||||
$inv = new OrganizerInvitation();
|
||||
$result = $inv->setOffer('basic');
|
||||
|
||||
self::assertSame('basic', $inv->getOffer());
|
||||
self::assertSame($inv, $result);
|
||||
}
|
||||
|
||||
public function testSetAndGetCommissionRate(): void
|
||||
{
|
||||
$inv = new OrganizerInvitation();
|
||||
$result = $inv->setCommissionRate(5.5);
|
||||
|
||||
self::assertSame(5.5, $inv->getCommissionRate());
|
||||
self::assertSame($inv, $result);
|
||||
}
|
||||
|
||||
public function testSetAndGetStatus(): void
|
||||
{
|
||||
$inv = new OrganizerInvitation();
|
||||
|
||||
Reference in New Issue
Block a user