Skip to content

QA: Rezervace

Checklist pro testery rezervačního modulu (větev feature/reservations-rework). Cíl: ověřit nové chování PubTable, AlternateReservation a standing confirmation flow před merge na master. Referenční dokumentace: Rezervace.


Tabulka / ModelStavÚčel
pub_tables / PubTableNováStoly v podniku jako first-class entita (nahrazuje JSON capacity)
alternate_reservations / AlternateReservationNováSamostatná tabulka pro náhradníky (dříve příznak is_alternate=1 na TeamReservation)
standing_reservations.pub_table_idNový sloupecPreferovaný stůl pro autorezervaci
team_reservations.pub_table_idNový sloupecKonkrétně přiřazený stůl
team_reservations.standing_reservation_idNový sloupecOdkaz na nadřazenou autorezervaci
team_reservations.confirmed_atNový sloupecČas potvrzení standing rezervace
team_reservations.notification_72h_sent_atNový sloupecByl odeslán request o potvrzení
team_reservations.notification_48h_sent_atNový sloupecDošlo k auto-zrušení nepotvrzené rezervace
standing_reservations.consecutive_unconfirmed_countNový sloupecPočítač nepotvrzených rezervací (3 = trvalé zrušení)
  1. Kapacita = počet stolů, ne seats/smallseats. Nový výpočet kapacity v Event::getCapacity() a resolveTableLayout() na základě PubTable.
  2. Automatické přiřazení stolu při rezervaci (pokud má podnik PubTables).
  3. Overbooking stolu (max_capacity > optimal_capacity) vyžaduje potvrzení hráče.
  4. Náhradníci (AlternateReservation) – samostatná tabulka, bez přiřazeného stolu, promuje se na TeamReservation při uvolnění místa.
  5. Standing reservations confirmation flow – 72 h před eventem se žádá o potvrzení, 48 h před eventem se nepotvrzené ruší, 3× nepotvrzeno = trvalé zrušení autorezervace.
  6. Soft deletes na všech rezervačních entitách – deaktivace stolu nemaže historická data.
  7. Migrace z legacy capacity JSON – při nasazení se vygenerují PubTable záznamy (velké 6/8, malé 2/4).
CommandFrekvenceCo dělá
reservations:backfillhodinově (:01)Pro každou aktivní StandingReservation zajistí právě jednu budoucí TeamReservation
reservations:reminddenně 10:00Pošle remindery na zítřejší eventy (beze změny, ale ověřit)
reservations:require-confirmationhodinově72 h před eventem pošle týmům s autorezervací žádost o potvrzení
reservations:cancel-unconfirmedhodinově48 h před eventem zruší nepotvrzené standing rezervace

Reset DB a seed dat zajistí vývojář. Testovací data zahrnují:

  • Klasické rezervace na dokončené eventy
  • 3–6 rezervací na aktivní eventy, z toho 5+ jako náhradníci
  • 2–3 standing reservations per aktivní blueprint + generované TeamReservation
  • Rezervace na speciály (4–6, z toho 5+ jako náhradní)

  1. Admin → Podniky → detail → sekce Stoly.
  2. Klik na Přidat stoly, zadejte počet (např. 5).
  3. Očekávání:
    • Vzniklo 5 nových stolů s optimal_capacity = 4, max_capacity = 6, sort_order inkrementované.
    • Všechny mají is_active = true.
    • Stoly se zobrazí v tabulce seřazené podle sort_order.
  1. Klik na ikonu tužky u řádku stolu.
  2. Upravte poznámku, optimal/max kapacity, přepněte is_active.
  3. Očekávání: změna se okamžitě promítne do tabulky, neaktivní stůl se přeskočí při dalším přiřazení.
  1. Nastavte stůl, který má přiřazenou budoucí TeamReservation nebo aktivní StandingReservation.
  2. Klik na ikonu koše.
  3. Očekávání: notifikace „Nelze smazat stůl” a akce se zruší (action->halt()).
  1. Stůl bez budoucích rezervací/autorezervací.
  2. Klik na koš → potvrdit.
  3. Očekávání:
    • Soft delete (deleted_at není null).
    • Na historických TeamReservation zůstává pub_table_id – relation pubTable()->withTrashed() ji stále vrátí.
  • Ověřte, že po spuštění migrace 2026_03_23_182648_update_pub_tables_rename_name_to_note_and_seed_from_capacity mají podniky s capacity = [5,5,5] (tří-složkový JSON) vygenerovány odpovídající PubTable záznamy.
  • Velké (seats) → optimal=6, max=8, malé (smallseats) → optimal=2, max=4.

2.1 Happy path – ideální stůl existuje

Section titled “2.1 Happy path – ideální stůl existuje”
  1. Podnik s PubTable optimal=4, max=6 (volný).
  2. Web: vytvořit rezervaci se 4 hráči.
  3. Očekávání:
    • Rezervace vytvořena, pub_table_id vyplněno.
    • E-mail Potvrzení rezervace v Mailhogu.
    • Kapacita eventu klesla o 1 stůl.
  1. Všechny volné stoly mají optimal_capacity < počet hráčů, ale existuje stůl s max_capacity >= počet hráčů.
  2. Submit rezervace.
  3. Očekávání:
    • Stránka se vrátí s dialogem / zprávou confirm_overflow (obsahuje optimal/max/players).
    • Po submitu s confirmed=1 se stůl přiřadí (nejbližší podle |optimal − players|).
  1. Všechny volné stoly mají max_capacity < počet hráčů.
  2. Submit rezervace.
  3. Očekávání: flash danger zpráva „K dispozici jsou jen stoly s maximální kapacitou X hráčů…“.
  1. Všechny stoly obsazené (capacity = 0).
  2. Submit rezervace.
  3. Očekávání:
    • Vzniká záznam v alternate_reservations (ne v team_reservations).
    • Flash: „Váš tým jsme zaregistrovali jako náhradní…”.
    • E-mail ReservationAlternateConfirmation (ne Confirmation!).
    • pub_table_id je NULL.
  1. Tým vytvoří rezervaci, zruší ji (soft delete), a poté znovu vytvoří se stejným team_id a event_id.
  2. Očekávání:
    • Nevznikne duplicitní záznam, původní se obnoví (restore()).
    • Pokud má původní pub_table_id, který je obsazený jiným týmem, systém vybere nejbližší volný stůl.
    • Odejde nová e-mailová notifikace z restored() hooku.
  1. Anonymní: zkusit rezervaci se stejným team_name + stejný event.
  2. Očekávání: flash danger „Tento tým už má aktivní rezervaci”.
  3. Zkusit team_name existující v jiném týmu stejného podniku.
  4. Očekávání: flash danger s nabídkou přihlášení (HTML se strongem a ul).
  1. Podnik, kde nebyla spuštěna migrace stolů (nemá PubTable).
  2. Očekávání:
    • Kapacita se počítá ze sumy capacity minus existující rezervace.
    • Rezervace se vytvoří s pub_table_id = NULL.
    • Systém nevyžaduje potvrzení overflow (tento flow je jen pro PubTable).
  1. Podnik s online_reservations = false.
  2. GET /rezervace/{event} na blueprint event.
  3. Očekávání: abort(401) „Online rezervace pro tento podnik nejsou povoleny”. Speciály tímto nejsou blokovány.

3.1 Potvrzení pozvánky – dostupná kapacita

Section titled “3.1 Potvrzení pozvánky – dostupná kapacita”
  1. Event plný, tým je AlternateReservation, někdo zruší rezervaci.
  2. Očekávání:
    • Job InviteAlternateReservations odešle e-mail ReservationInvitation všem náhradníkům na daném eventu.
    • invited_at na alternate rezervaci se vyplní.
  3. Náhradník kliká na confirmUrl z e-mailu.
  4. Na stránce /rezervace/confirm/{token} potvrdí.
  5. Očekávání:
    • Vzniká TeamReservation (s pub_table_id pokud je PubTable dostupný).
    • AlternateReservation je soft-deleted.
    • Zpráva „Budeme se na váš těšit v…” + link na kvíz.

3.2 Potvrzení pozvánky – kapacita mezitím zmizla

Section titled “3.2 Potvrzení pozvánky – kapacita mezitím zmizla”
  1. Dva náhradníci, event s kapacitou = 1.
  2. První potvrdí → alokuje místo. Druhý kliká na svůj link.
  3. Očekávání:
    • Druhý vidí zprávu „Ajajaj… Někdo byl rychlejší…”.
    • AlternateReservation druhého zůstává beze změny (nepromuje se).
  1. Tým klikne na cancelUrl ze svého confirmation e-mailu (token od AlternateReservation).
  2. Očekávání:
    • AlternateReservation soft-delete.
    • Pokud je event ACTIVE: e-mail ReservationCancellation (observer deleted).
    • Jinak žádný e-mail.
  • Ověřte, že migrace 2026_03_23_150010_migrate_alternate_reservations_data přesunula všechny TeamReservation s legacy příznakem is_alternate=1 do alternate_reservations.
  • Po migraci by v team_reservations neměl být žádný záznam s is_alternate=1.

  1. Přihlášený uživatel zakládá rezervaci, zaškrtne autorezervace.
  2. Očekávání:
    • Vzniká TeamReservation i StandingReservation (active=true).
    • Notifikace StandingReservationCreatedNotification manažerovi týmu.
    • Token se vygeneruje v boot::creating.
  1. V admin panelu ověřte: aktivní StandingReservation pro blueprint bez budoucí rezervace.
  2. Vytvořte budoucí event pro tento blueprint (Admin → Blueprint → Přidat event).
  3. Vývojář spustí reservations:backfill (nebo počkejte do příštího hodinového cyklu).
  4. Očekávání:
    • V Admin → Blueprint → záložka Rezervace se zobrazí nová rezervace pro daný tým.
    • Rezervace má přiřazený stůl (preferovaný z autorezervace, nebo nejbližší volný).

4.3 Auto-backfill – už má budoucí rezervaci

Section titled “4.3 Auto-backfill – už má budoucí rezervaci”
  1. Tým už má rezervaci na budoucí event blueprintu — ověřte v Admin → Blueprint → záložka Rezervace.
  2. Vývojář spustí reservations:backfill --blueprint_id=X.
  3. Očekávání: žádná nová rezervace pro tento tým nevznikne — počet řádků v tabulce zůstane stejný.

4.4 Confirmation request – 72 h před eventem

Section titled “4.4 Confirmation request – 72 h před eventem”

Příprava: Vývojář zajistí standing rezervaci na event začínající za 72 h (v rozmezí 70–74 h) bez potvrzení.

  1. Vývojář spustí reservations:require-confirmation.
  2. Ověřit: v Mailhogu dorazil e-mail StandingReservationConfirmationRequest pro daný tým.
  3. Vývojář spustí command podruhé.
  4. Ověřit: druhý e-mail nedorazil (command posílá jen jednou).
  1. Klik na confirm link v e-mailu (z kroku 4.4) → /rezervace/confirm-standing/{token}.
  2. Potvrdit.
  3. Očekávání:
    • Stránka zobrazí potvrzení a přesměruje na přehled rezervací.
    • Rezervace se zobrazí jako potvrzená v Admin → Blueprint → záložka Rezervace.

Příprava: Vývojář zajistí standing rezervaci na event začínající za 48 h (46–50 h), kde byl odeslán 72h reminder (scénář 4.4), ale hráč nepotvrdil.

  1. Vývojář spustí reservations:cancel-unconfirmed.
  2. Ověřit: v Mailhogu dorazil e-mail StandingReservationAutoCancel pro daný tým.
  3. Ověřit: rezervace zmizela z tabulky v Admin → Blueprint → záložka Rezervace.
  4. Ověřit: pokud byli náhradníci — dorazil jim e-mail ReservationInvitation (Mailhog).

4.7 Trvalé zrušení po 3 × nepotvrzení

Section titled “4.7 Trvalé zrušení po 3 × nepotvrzení”

Příprava: Vývojář připraví stav po 2 předchozích nepotvrzeních (opakování scénáře 4.6 na stejné autorezervaci).

  1. Vývojář spustí reservations:cancel-unconfirmed potřetí.
  2. Ověřit: v Mailhogu dorazil e-mail StandingReservationCancelledPermanent.
  3. Ověřit: autorezervace zmizela z Admin → Blueprint → záložka Standing rezervace.

Příprava: Vývojář připraví stav po 2 nepotvrzeních (stejná autorezervace jako 4.7, ale resetovaná).

  1. Hráč potvrdí standing rezervaci (scénář 4.5).
  2. Ověřit: autorezervace je stále aktivní v Admin → Blueprint → záložka Standing rezervace (trvalé zrušení nenastalo).
  1. Hráč klikne na revoke link autorezervace (StandingReservationController::revoke).
  2. Očekávání:
    • StandingReservation soft-delete.
    • Všechny TeamReservation s tímto team_id pro budoucí eventy téhož blueprintu se smažou.
    • Notifikace StandingReservationRevokedNotification manažerovi týmu.
    • Observer deleted odešle Cancellation e-maily za budoucí eventy (status=ACTIVE).
  1. Moderátor v panelu → Stálé rezervace → edit → přiřaď Stůl.
  2. Vývojář spustí reservations:backfill.
  3. Ověřit: v Admin → Blueprint → záložka Rezervace má nová rezervace přiřazen správný stůl (nebo nejbližší volný, pokud byl preferovaný obsazený).

Tyto scénáře vyžadují spuštění artisan příkazů. Vývojář spustí příkaz, tester ověří výsledek v UI / Mailhogu.

  1. Vývojář dispatchne EnsureExactlyOneFutureTeamReservationJob 3× za sebou.
  2. Ověřit: v Admin → Blueprint → záložka Rezervace vznikla právě jedna nová rezervace (ne tři).

5.2 Souběžný přístup (race condition)

Section titled “5.2 Souběžný přístup (race condition)”
  1. Vývojář simuluje dva souběžné requesty na rezervaci stejného týmu na stejný event.
  2. Ověřit: v Admin → Event → záložka Rezervace existuje právě jedna rezervace daného týmu (ne dvě).
  1. Vývojář zajistí event zítra s rezervací bez odeslaného reminderu.
  2. Vývojář spustí reservations:remind.
  3. Ověřit: v Mailhogu dorazil e-mail ReservationReminder pro daný tým.
  4. Vývojář spustí command podruhé.
  5. Ověřit: druhý e-mail nedorazil.

6.1 Admin – detail eventu → záložka Rezervace

Section titled “6.1 Admin – detail eventu → záložka Rezervace”
  • Ověřte, že:
    • Filament tabulka ReservationList zobrazuje aktivní TeamReservation.
    • Oddělená tabulka Náhradníci (AlternateReservationList) pro daný event.
    • Oddělená tabulka Autorezervace (StandingTable) pro daný blueprint.
    • Sloupec Přiřazený stůl zobrazuje Stůl {sort_order} + poznámku, nebo při NULL.

6.2 Moderátor – panel → záložka Rezervace

Section titled “6.2 Moderátor – panel → záložka Rezervace”
  • Ověřte zobrazení ReservationsTable + StandingReservationsTable pod sebou.
  • Edit standing rezervace → přiřazení stolu přes select (jen aktivní stoly podniku, formát Stůl {n} – {note} ({opt}/{max})).
  • Delete standing → confirm modal „Opravdu zrušit?” + danger color + e-mail týmu.
  • StandingTable (admin): filtr Podle kvízu – nabídka jen blueprintů s existujícími standing rezervacemi.
  • AlternateReservationList: řazení podle invited_at pro rychlé ověření, zda už byli pozváni.

7 · E-mailové šablony – kontrola obsahu

Section titled “7 · E-mailové šablony – kontrola obsahu”

Všechny šablony najdete v resources/views/emails/. Ověřte:

ŠablonaTriggerMusí obsahovat
reservation_confirmationnová TeamReservationtým, event, cancel URL
reservation_cancellationsoft delete TeamReservation / AlternateReservation na aktivní eventtým, event
reservation_invitationInviteAlternateReservations jobevent, confirm URL
reservation_alternate_confirmationnová AlternateReservationtým, event, cancel URL
reservation_reminderreservations:remindevent, link
standing_reservation_confirmation_requestreservations:require-confirmationevent, 72 h varování, confirm link
standing_reservation_auto_cancelreservations:cancel-unconfirmedtým, event, důvod (48 h nepotvrzeno)
standing_reservation_cancelled_permanent3× po sobě nepotvrzenodůvod, info o trvalém zrušení

Po změnách rezervací ověřit, že nic dalšího nepraskne:

  • Speciály – vytvoření rezervace, overbooking, náhradníci (Special\ReservationsTable).
  • Anonymní rezervace – cookie anon_reservation_{eventId} na 30 dní; po přihlášení se rezervace onboardne na hráčský profil.
  • Výpis rezervací v profilu hráče – neselže na smazaném eventu (fix z 2026-03-30).
  • Kapacita se počítá bez náhradníků (fix z 2026-03-11).
  • Po zrušení rezervace tlačítko Zpět → správný kvíz / speciál (fix).
  • Po přihlášení-odhlášení-přihlášení: obnovení předchozí soft-deleted rezervace (ne chyba 500, ne unique constraint violation).
  • Blueprint bez budoucího eventu → backfill se hlásí bez erroru, jen zaloguje a odejde.
  • Disable event → QuizzEvent cancel → notifikace EventCanceled všem rezervovaným týmům.

Co ověřitKde v adminu
Stoly podnikuAdmin → Podniky → detail → záložka Stoly
Standing rezervace s nepotvrzenímiAdmin → Standing rezervace → filtr “Nepotvrzené”
Náhradníci na eventuAdmin → Event → detail → záložka Náhradníci
Odeslané e-mailyMailhog: http://159.89.0.170:8025/

Před release pustit:

Terminal window
docker-compose exec fpm php artisan test tests/Feature/Reservations/ --compact
docker-compose exec fpm php artisan test tests/Feature/GeneratingReservationFromStandingTest.php --compact
docker-compose exec fpm php artisan test tests/Feature/ReservationControllerTest.php --compact

Pokryté scénáře:

  • AlternateReservationTest – overbooking, promote, duplicates
  • PubTableTest – seed migrace, kapacita z PubTable
  • StandingConfirmationTest – confirm / auto-cancel / permanent delete / commandy
  • TableAssignmentTest – výběr stolu při backfill jobu