Replace isScanned with state (valid/invalid/expired) and firstScannedAt on BilletOrder

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-03-21 14:15:32 +01:00
parent 2b48d2081f
commit 2efb5f176a
4 changed files with 75 additions and 21 deletions

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20260321200000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Replace isScanned with state and firstScannedAt on billet_order';
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE billet_order ADD COLUMN IF NOT EXISTS state VARCHAR(20) DEFAULT 'valid' NOT NULL");
$this->addSql('ALTER TABLE billet_order ADD COLUMN IF NOT EXISTS first_scanned_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL');
$this->addSql('ALTER TABLE billet_order DROP COLUMN IF EXISTS is_scanned');
$this->addSql('ALTER TABLE billet_order DROP COLUMN IF EXISTS scanned_at');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE billet_order ADD COLUMN IF NOT EXISTS is_scanned BOOLEAN DEFAULT false NOT NULL');
$this->addSql('ALTER TABLE billet_order ADD COLUMN IF NOT EXISTS scanned_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL');
$this->addSql('ALTER TABLE billet_order DROP COLUMN IF EXISTS state');
$this->addSql('ALTER TABLE billet_order DROP COLUMN IF EXISTS first_scanned_at');
}
}

View File

@@ -30,11 +30,15 @@ class BilletOrder
#[ORM\Column] #[ORM\Column]
private int $unitPriceHT = 0; private int $unitPriceHT = 0;
#[ORM\Column] public const STATE_VALID = 'valid';
private bool $isScanned = false; public const STATE_INVALID = 'invalid';
public const STATE_EXPIRED = 'expired';
#[ORM\Column(length: 20)]
private string $state = self::STATE_VALID;
#[ORM\Column(nullable: true)] #[ORM\Column(nullable: true)]
private ?\DateTimeImmutable $scannedAt = null; private ?\DateTimeImmutable $firstScannedAt = null;
#[ORM\Column] #[ORM\Column]
private \DateTimeImmutable $createdAt; private \DateTimeImmutable $createdAt;
@@ -108,26 +112,31 @@ class BilletOrder
return $this->unitPriceHT / 100; return $this->unitPriceHT / 100;
} }
public function isScanned(): bool public function getState(): string
{ {
return $this->isScanned; return $this->state;
} }
public function setIsScanned(bool $isScanned): static public function setState(string $state): static
{ {
$this->isScanned = $isScanned; $this->state = $state;
return $this; return $this;
} }
public function getScannedAt(): ?\DateTimeImmutable public function isValid(): bool
{ {
return $this->scannedAt; return self::STATE_VALID === $this->state;
} }
public function setScannedAt(?\DateTimeImmutable $scannedAt): static public function getFirstScannedAt(): ?\DateTimeImmutable
{ {
$this->scannedAt = $scannedAt; return $this->firstScannedAt;
}
public function setFirstScannedAt(?\DateTimeImmutable $firstScannedAt): static
{
$this->firstScannedAt = $firstScannedAt;
return $this; return $this;
} }

View File

@@ -41,8 +41,12 @@
<p class="text-xs font-mono text-gray-400">{{ ticket.reference }}</p> <p class="text-xs font-mono text-gray-400">{{ ticket.reference }}</p>
</div> </div>
<span class="font-black text-sm text-indigo-600">{{ ticket.unitPriceHTDecimal|number_format(2, ',', ' ') }} &euro;</span> <span class="font-black text-sm text-indigo-600">{{ ticket.unitPriceHTDecimal|number_format(2, ',', ' ') }} &euro;</span>
{% if ticket.scanned %} {% if ticket.state == 'valid' %}
<span class="badge-yellow text-[10px] font-black uppercase">Scanne</span> <span class="badge-green text-[10px] font-black uppercase">Valide</span>
{% elseif ticket.state == 'expired' %}
<span class="badge-yellow text-[10px] font-black uppercase">Expire</span>
{% else %}
<span class="badge-red text-[10px] font-black uppercase">Invalide</span>
{% endif %} {% endif %}
<a href="{{ path('app_order_download_ticket', {reference: order.reference, ticketReference: ticket.reference}) }}" class="px-3 py-1 border-2 border-gray-900 bg-white text-xs font-black uppercase hover:bg-indigo-600 hover:text-white transition-all" target="_blank"> <a href="{{ path('app_order_download_ticket', {reference: order.reference, ticketReference: ticket.reference}) }}" class="px-3 py-1 border-2 border-gray-900 bg-white text-xs font-black uppercase hover:bg-indigo-600 hover:text-white transition-all" target="_blank">
Telecharger PDF Telecharger PDF

View File

@@ -19,8 +19,9 @@ class BilletOrderTest extends TestCase
self::assertNull($ticket->getBilletName()); self::assertNull($ticket->getBilletName());
self::assertSame(0, $ticket->getUnitPriceHT()); self::assertSame(0, $ticket->getUnitPriceHT());
self::assertSame(0.0, $ticket->getUnitPriceHTDecimal()); self::assertSame(0.0, $ticket->getUnitPriceHTDecimal());
self::assertFalse($ticket->isScanned()); self::assertSame(BilletOrder::STATE_VALID, $ticket->getState());
self::assertNull($ticket->getScannedAt()); self::assertTrue($ticket->isValid());
self::assertNull($ticket->getFirstScannedAt());
self::assertMatchesRegularExpression('/^ETICKET-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/', $ticket->getReference()); self::assertMatchesRegularExpression('/^ETICKET-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/', $ticket->getReference());
self::assertInstanceOf(\DateTimeImmutable::class, $ticket->getCreatedAt()); self::assertInstanceOf(\DateTimeImmutable::class, $ticket->getCreatedAt());
} }
@@ -64,22 +65,30 @@ class BilletOrderTest extends TestCase
self::assertSame($ticket, $result); self::assertSame($ticket, $result);
} }
public function testSetAndGetIsScanned(): void public function testSetAndGetState(): void
{ {
$ticket = new BilletOrder(); $ticket = new BilletOrder();
$result = $ticket->setIsScanned(true); $result = $ticket->setState(BilletOrder::STATE_INVALID);
self::assertTrue($ticket->isScanned()); self::assertSame(BilletOrder::STATE_INVALID, $ticket->getState());
self::assertFalse($ticket->isValid());
self::assertSame($ticket, $result); self::assertSame($ticket, $result);
$ticket->setState(BilletOrder::STATE_EXPIRED);
self::assertSame(BilletOrder::STATE_EXPIRED, $ticket->getState());
self::assertFalse($ticket->isValid());
$ticket->setState(BilletOrder::STATE_VALID);
self::assertTrue($ticket->isValid());
} }
public function testSetAndGetScannedAt(): void public function testSetAndGetFirstScannedAt(): void
{ {
$ticket = new BilletOrder(); $ticket = new BilletOrder();
$date = new \DateTimeImmutable(); $date = new \DateTimeImmutable();
$result = $ticket->setScannedAt($date); $result = $ticket->setFirstScannedAt($date);
self::assertSame($date, $ticket->getScannedAt()); self::assertSame($date, $ticket->getFirstScannedAt());
self::assertSame($ticket, $result); self::assertSame($ticket, $result);
} }