src/Entity/SharedEventCode.php line 20
<?phpdeclare(strict_types=1);namespace App\Entity;use App\Entity\User\EmailB2B;use App\Repository\SharedEventCodeRepository;use Doctrine\DBAL\Types\Types;use Doctrine\ORM\Mapping as ORM;use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;use Symfony\Component\Validator\Constraints as Assert;/*** Code événement partagé : même code pour tous les participants, période de validité, tarification paramétrable.*/#[ORM\Entity(repositoryClass: SharedEventCodeRepository::class)]#[ORM\Table(name: 'app_shared_event_code')]#[UniqueEntity('code')]class SharedEventCode{/** Fuseau métier pour les bornes début/fin (saisie BO + comparaison API). */public const VALIDITY_TIMEZONE = 'Europe/Paris';public const PRICING_OFFERED = 'offered';public const PRICING_NORMAL = 'normal';public const PRICING_SPECIAL = 'special';public const FRAME_10X15 = '10x15';public const FRAME_15X10 = '15x10';#[ORM\Id]#[ORM\GeneratedValue]#[ORM\Column]private ?int $id = null;#[ORM\Column(length: 50)]#[Assert\NotBlank]#[Assert\Length(min: 1, max: 10)]#[Assert\Regex(pattern: '/^[A-Za-z0-9]+$/')]private ?string $code = null;#[ORM\ManyToOne]#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]private ?EmailB2B $emailB2B = null;#[ORM\Column(length: 255)]#[Assert\NotBlank]private ?string $name = null;#[ORM\Column(length: 255)]#[Assert\NotBlank]private ?string $nameInApp = null;#[ORM\Column(length: 255, nullable: true)]private ?string $companyName = null;#[ORM\Column(type: Types::DATETIME_MUTABLE)]#[Assert\NotNull]private ?\DateTimeInterface $startAt = null;#[ORM\Column(type: Types::DATETIME_MUTABLE)]#[Assert\NotNull]private ?\DateTimeInterface $endAt = null;#[ORM\Column(length: 8)]#[Assert\Choice(choices: [self::FRAME_10X15, self::FRAME_15X10])]private string $frameFormat = self::FRAME_10X15;#[ORM\Column]private int $size = 12;#[ORM\Column(length: 255)]private string $country = 'FR';#[ORM\Column(length: 255)]private string $filenameLogo = '';#[ORM\Column(length: 255, nullable: true)]private ?string $filenameTemplate = null;#[ORM\Column(length: 255, nullable: true)]private ?string $filenameTemplateVertical = null;#[ORM\Column(length: 255, nullable: true)]private ?string $filenameAddonPhoto = null;#[ORM\Column(length: 20)]#[Assert\Choice(choices: [self::PRICING_OFFERED, self::PRICING_NORMAL, self::PRICING_SPECIAL])]private string $pricingType = self::PRICING_OFFERED;/** Prix produit TTC en centimes (hors port), utilisé si pricingType = special */#[ORM\Column(nullable: true)]private ?int $specialPriceCents = null;/** Première fois qu’un participant a utilisé le code (rattachement B2B ou commande). */#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]private ?\DateTimeImmutable $firstUsedAt = null;public function getId(): ?int{return $this->id;}public function getCode(): ?string{return $this->code;}public function setCode(string $code): self{$this->code = strtoupper(trim($code));return $this;}public function getEmailB2B(): ?EmailB2B{return $this->emailB2B;}public function setEmailB2B(?EmailB2B $emailB2B): self{$this->emailB2B = $emailB2B;return $this;}public function getName(): ?string{return $this->name;}public function setName(string $name): self{$this->name = $name;return $this;}public function getNameInApp(): ?string{return $this->nameInApp;}public function setNameInApp(string $nameInApp): self{$this->nameInApp = $nameInApp;return $this;}public function getCompanyName(): ?string{return $this->companyName;}public function setCompanyName(?string $companyName): self{$this->companyName = $companyName;return $this;}public function getStartAt(): ?\DateTimeInterface{return $this->startAt;}public function setStartAt(\DateTimeInterface $startAt): self{$this->startAt = $startAt;return $this;}public function getEndAt(): ?\DateTimeInterface{return $this->endAt;}public function setEndAt(\DateTimeInterface $endAt): self{$this->endAt = $endAt;return $this;}public function getFrameFormat(): string{return $this->frameFormat;}public function setFrameFormat(string $frameFormat): self{$this->frameFormat = $frameFormat;return $this;}public function getSize(): int{return $this->size;}public function setSize(int $size): self{$this->size = $size;return $this;}public function getCountry(): string{return $this->country;}public function setCountry(string $country): self{$this->country = $country;return $this;}public function getFilenameLogo(): string{return $this->filenameLogo;}public function setFilenameLogo(string $filenameLogo): self{$this->filenameLogo = $filenameLogo;return $this;}public function getFilenameTemplate(): ?string{return $this->filenameTemplate;}public function setFilenameTemplate(?string $filenameTemplate): self{$this->filenameTemplate = $filenameTemplate;return $this;}public function getFilenameTemplateVertical(): ?string{return $this->filenameTemplateVertical;}public function setFilenameTemplateVertical(?string $filenameTemplateVertical): self{$this->filenameTemplateVertical = $filenameTemplateVertical;return $this;}public function getFilenameAddonPhoto(): ?string{return $this->filenameAddonPhoto;}public function setFilenameAddonPhoto(?string $filenameAddonPhoto): self{$this->filenameAddonPhoto = $filenameAddonPhoto;return $this;}public function getPricingType(): string{return $this->pricingType;}public function setPricingType(string $pricingType): self{$this->pricingType = $pricingType;return $this;}public function getSpecialPriceCents(): ?int{return $this->specialPriceCents;}public function setSpecialPriceCents(?int $specialPriceCents): self{$this->specialPriceCents = $specialPriceCents;return $this;}/*** Les dates en base (DATETIME) et le formulaire admin sont interprétées comme heure locale France.* Compare à l’instant courant dans ce même fuseau, pour éviter les décalages si PHP/serveur est en UTC.*/public function isValidAt(?\DateTimeInterface $now = null): bool{$start = $this->startAt;$end = $this->endAt;if ($start === null || $end === null) {return false;}$tz = new \DateTimeZone(self::VALIDITY_TIMEZONE);$startParis = new \DateTimeImmutable($start->format('Y-m-d H:i:s'), $tz);$endParis = new \DateTimeImmutable($end->format('Y-m-d H:i:s'), $tz);$nowParis = $now === null? new \DateTimeImmutable('now', $tz): \DateTimeImmutable::createFromInterface($now)->setTimezone($tz);return $startParis <= $nowParis && $nowParis <= $endParis;}public function getFirstUsedAt(): ?\DateTimeImmutable{return $this->firstUsedAt;}public function setFirstUsedAt(?\DateTimeImmutable $firstUsedAt): self{$this->firstUsedAt = $firstUsedAt;return $this;}/** Enregistre la première utilisation (idempotent). */public function markFirstUseIfUnset(): void{if ($this->firstUsedAt !== null) {return;}$this->firstUsedAt = new \DateTimeImmutable();}public function isUsed(): bool{return $this->firstUsedAt !== null;}}