Skip to content

Migration de l'entité SponsorTicket de Ting vers Doctrine - #2416

Open
Korbeil wants to merge 2 commits into
afup:masterfrom
Korbeil:afup-migrate-ting-sponsorticket-entity
Open

Korbeil wants to merge 2 commits into
afup:masterfrom
Korbeil:afup-migrate-ting-sponsorticket-entity

Conversation

@Korbeil

@Korbeil Korbeil commented Sep 21, 2026

Copy link
Copy Markdown
Member

Description

Migre l'entité des tokens sponsors (billetterie des sponsors) de l'ORM maison Ting vers Doctrine ORM, conformément à l'ADR-002 (entité à propriétés publiques, repository Doctrine) et à l'ADR-001 (vocabulaire métier en français : societe, inscription). Aucun changement de schéma de base de données.

Changes

  • Billetterie sponsors : mêmes fonctionnalités (gestion des tokens, invitations, scan des QR Codes), désormais lues et écrites via Doctrine ORM
  • Le champ « société » du formulaire des tokens est renommé societe (libellé inchangé)
  • Ajout d'un test d'intégration sur le repository des tokens sponsors

How to test

  1. Dans le BO, ouvrir un évènement → Tokens sponsors : créer, modifier et supprimer un token
  2. Vérifier l'envoi du mail et l'usage des invitations depuis la billetterie sponsors côté public
  3. make test-integration pour le nouveau test du repository

@Korbeil
Korbeil marked this pull request as ready for review September 22, 2026 06:15
@vgreb

vgreb commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator

Attention ⚠️ Les propriétés de l'entité sont maintenant typées et non nullables. Si on soumet un champ texte vide ou rempli d'espaces, le composant Form le convertit en null. L'écriture de ce null dans la propriété lève une exception, et on obtient une 500 au lieu du message de NotBlank.

#[ORM\Column(name: 'company', nullable: false)]
public string $societe;
#[ORM\Column(length: 64, nullable: false)]
#[Assert\NotBlank]
#[Assert\Length(min: 30, max: 64)]
public string $token;
#[ORM\Column(name: 'contact_email', nullable: false)]
#[Assert\NotBlank]
#[Assert\Email]
public string $contactEmail;
#[ORM\Column(name: 'max_invitations', nullable: false)]
#[Assert\NotBlank]
#[Assert\Range(min: 1, max: 20)]
public int $maxInvitations = 0;

Un 'empty_data' => '' sur les champs texte de SponsorTokenType devrait suffire, en rétablissent le #[Assert\NotBlank] perdu sur la propriété SponsorTicker::$societe.

@Korbeil
Korbeil marked this pull request as draft September 30, 2026 20:26
Korbeil added a commit to Korbeil/afup-web that referenced this pull request Sep 30, 2026
…nsors

Sur une soumission vide ou remplie d'espaces, le composant Form convertit
la valeur en null, ce qui provoquait une erreur de typage sur les
propriétés non nullables de SponsorTicket au lieu d'un message de
validation.

- empty_data => '' sur les champs texte de SponsorTokenType
- empty_data => '0' sur maxInvitations (même classe de bug, Champ integer)
- #Assert\NotBlank rétabli sur SponsorTicket::$societe (perdu dans la
  migration Ting)

Retour de la review afup#2416 (vgreb).
@Korbeil

Korbeil commented Sep 30, 2026

Copy link
Copy Markdown
Member Author

Merci pour la review, corrigé dans 493be7d :

  • 'empty_data' => '' sur les trois champs texte de SponsorTokenType (societe, contactEmail, token).
  • Même classe de bug sur maxInvitations (IntegerType) : une soumission vide arrivait aussi en null sur la propriété int (500 au lieu d'un message de validation). J'y ai mis 'empty_data' => '0', la contrainte Range(min: 1, max: 20) produisant alors l'erreur attendue.
  • #[Assert\NotBlank] rétabli sur SponsorTicket::$societe.

La checkbox qrCodesScannerAvailable est saine (empty_data recalculé côté CheckboxType). Vérifié par un cas de soumission vide/espaces sur chaque champ : erreurs NotBlank/Range attendues, plus d'exception ; phpstan et test d'intégration du repository OK.


public function addTicketToSponsor(SponsorTicket $sponsorTicket, Ticket $ticket): void
{
$invoice = $this->invoiceFactory->createInvoiceFromSponsorTicket($sponsorTicket);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Le save() Doctrine ne fait pas partie de la transaction Ting : Doctrine (PDO) et Ting (mysqli) ouvrent chacun leur propre connexion, et flush() commite immédiatement. Pour que le compteur ne puisse que surestimer en cas d'échec (le quota est contrôlé via getPendingInvitations()), on réserve l'invitation avant la transaction et on la libère si la transaction échoue.

        $invoice = $this->invoiceFactory->createInvoiceFromSponsorTicket($sponsorTicket);
        $isNewTicket = $ticket->getId() === null;

        if ($isNewTicket) {
            // Réservée hors transaction Ting (connexion Doctrine distincte) :
            // en cas d'échec entre les deux écritures, le compteur surestime, sans risque de dépasser le quota
            $sponsorTicket->usedInvitations++;
            $this->sponsorTicketRepository->save($sponsorTicket);
        }

        try {
            $this->invoiceRepository->startTransaction();
            $this->invoiceRepository->save($invoice);
            $this->ticketRepository->save($ticket);
            $this->invoiceRepository->commit();
        } catch (Exception) {
            $this->invoiceRepository->rollback();

            if ($isNewTicket) {
                $sponsorTicket->usedInvitations--;
                $this->sponsorTicketRepository->save($sponsorTicket);
            }
        }

@@ -50,7 +50,7 @@
try {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Même problème : la décrémentation est commitée par Doctrine avant le commit() Ting. Si ce dernier échoue, le ticket existe toujours alors que l'invitation a été rendue. On ne libère l'invitation qu'une fois la suppression commitée.

        try {
            $this->ticketRepository->startTransaction();
            $this->ticketRepository->delete($ticket);
            $this->ticketRepository->commit();
        } catch (Exception) {
            $this->ticketRepository->rollback();

            return;
        }

        // Libérée seulement après le commit Ting (connexion Doctrine distincte) :
        // si cette écriture échoue, le compteur surestime, sans risque de dépasser le quota
        $sponsorTicket->usedInvitations--;
        $this->sponsorTicketRepository->save($sponsorTicket);

@Korbeil
Korbeil marked this pull request as ready for review October 1, 2026 06:35
…nsors

Sur une soumission vide ou remplie d'espaces, le composant Form convertit
la valeur en null, ce qui provoquait une erreur de typage sur les
propriétés non nullables de SponsorTicket au lieu d'un message de
validation.

- empty_data => '' sur les champs texte de SponsorTokenType
- empty_data => '0' sur maxInvitations (même classe de bug, Champ integer)
- #Assert\NotBlank rétabli sur SponsorTicket::$societe (perdu dans la
  migration Ting)

Retour de la review afup#2416 (vgreb).
@Korbeil
Korbeil force-pushed the afup-migrate-ting-sponsorticket-entity branch from 493be7d to 5564c37 Compare October 5, 2026 19:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants