Skip to content

E2: Match & Stadium Management - Tasks

Epic: E2: Match & Stadium Management

Task Legend

Symbol Status
Not started
🟡 In progress
Complete
Blocked

E2-F1: Match CRUD API

Feature: E2-F1: Match CRUD API

Status Task Verification
Create Match database migration (id, home_team, away_team, competition, kick_off_time, venue_id, status, created_at, updated_at) Run migrations
Implement POST /admin/matches endpoint with required fields Returns 201 with match_id
Implement GET /admin/matches endpoint with pagination Returns list of matches
Implement GET /admin/matches/{id} endpoint Returns match details
Implement PUT /admin/matches/{id} endpoint Updates match details
Implement DELETE /admin/matches/{id} endpoint (soft delete) Match status becomes "deleted"
Add status validation (draft, published, cancelled, closed) Invalid status transition returns 400
Add RBAC check for Match Manager role Non-admin returns 403

E2-F2: Sales Phase Configuration

Feature: E2-F2: Sales Phase Configuration

Status Task Verification
Create SalesPhase database migration (id, match_id, phase_type, start_at, end_at, eligibility_rules_json, ticket_limit) Run migrations
Implement POST /admin/matches/{id}/sales-phases endpoint Creates phase
Implement GET /admin/matches/{id}/sales-phases endpoint Returns phases for match
Implement PUT /admin/matches/{id}/sales-phases/{phase_id} endpoint Updates phase
Add overlap validation (phases cannot have overlapping dates) Overlapping dates return 400
Add eligibility_rules_json schema validation Invalid JSON structure returns 400

E2-F3: Stadium Template Management

Feature: E2-F3: Stadium Template Management

Status Task Verification
Create Stadium database migration (id, name, city, country, type, total_capacity, is_template) Run migrations
Create Sector database migration (id, stadium_id, name, row_count, seats_per_row, technical_seats) Run migrations
Seed database with Croatian stadium templates (Maksimir, Poljud, Opus Arena, Rujevica, Varazdin) with pre-configured seat maps Query returns 5 templates with complete seat data
Implement GET /admin/stadiums/templates endpoint Returns list of templates
Implement GET /admin/stadiums/{id}/sectors endpoint Returns sectors for stadium
Implement POST /admin/stadiums/{stadiumId}/sectors endpoint Creates sector in template
Implement PUT /admin/stadiums/{stadiumId}/sectors/{sectorId} endpoint Updates sector
Implement DELETE /admin/stadiums/{stadiumId}/sectors/{sectorId} endpoint Deletes sector (if no seat map)

E2-F4: Match Stadium Configuration

Feature: E2-F4: Match Stadium Configuration

Status Task Verification
Create MatchStadiumConfig database migration (id, match_id, stadium_id, config_version) Run migrations
Create MatchSector database migration (match_id, sector_id, status, price_category_id) Run migrations
Implement POST /admin/matches/{id}/stadium-config endpoint to copy template Creates match-specific config
Implement PUT /admin/matches/{id}/sectors/{sector_id} endpoint to set status (active/closed) Sector status updated
Implement price category assignment to sectors All seats in sector inherit price
Lock configuration after save (increment config_version) Version incremented

E2-F5: Away Match Stadium Creation

Feature: E2-F5: Away Match Stadium Creation

Status Task Verification
Implement POST /admin/stadiums endpoint for creating away stadium Creates stadium with type=AWAY
Add fields for name, city, country, address All fields saved
Add non_numbered_seats flag for away stadiums Flag persists
Implement sector creation for away stadium Sectors created with capacity

E2-F6: Match Update Notifications

Feature: E2-F6: Match Update Notifications

Status Task Verification
Add notification_preference param to PUT /admin/matches/{id} (notify/silent) Param accepted
If notify: trigger email to all ticket holders Emails queued
If notify: trigger push notification to all ticket holders Pushes queued
Log all changes to audit trail regardless of notification preference Audit record created

E2-F7: Match Cancellation Workflow

Feature: E2-F7: Match Cancellation Workflow

Status Task Verification
Implement POST /admin/matches/{id}/cancel endpoint Requires approval for published matches
Add Super Admin approval check for published match cancellation Non-super-admin returns 403
Trigger batch refund job for all paid tickets Refund records created
Send cancellation notification to all ticket holders Emails/push sent
Invalidate all QR codes (set ticket status to CANCELLED) Tickets marked cancelled
Generate cancellation report (tickets, refunds, notifications) Report accessible

E2-F8: Access Control Data Export

Feature: E2-F8: Access Control Data Export

Status Task Verification
Implement GET /admin/matches/{id}/access-control-export endpoint Returns Excel file
Export only barcode column (no PII) Excel has barcode only
Include extra ~1000 codes for unsold seats and late changes Export count > sold tickets
File naming: MatchID_TicketExport_YYYYMMDD_HHMM.xlsx Filename matches pattern

E2-F9: Match Closure with Attendance

Feature: E2-F9: Match Closure & Attendance

Status Task Verification
Implement POST /admin/matches/{id}/close endpoint Changes match status to CLOSED
Accept attendance file upload (Excel with barcodes) File parsed correctly
Match barcodes to tickets and update status to ATTENDED Matched tickets have ATTENDED status
Make match closure and attendance update atomic (transaction) Partial failure rolls back both
If no attendance data: require reason, mark all tickets ATTENDED All tickets updated
Trigger loyalty points calculation after closure Job triggered

E2-F10: Sector Seat Map Configuration

Feature: E2-F10: Sector Seat Map Configuration

Database Migrations

Status Task Verification
Create sector_seat_maps table (id, sector_id, seat_map_json JSONB, total_seats, configuration_status, created_at, updated_at) Run migrations
Create seat_type enum (STANDARD, TECHNICAL, ACCESSIBILITY, COMPANION) Enum created
Create seats table (id, sector_id, row_identifier, seat_number, seat_type, created_at) Run migrations
Add UNIQUE constraint on (sector_id, row_identifier, seat_number) Constraint validated
Create traversal_dir enum (TOP_TO_BOTTOM, BOTTOM_TO_TOP) Enum created
Create row_strategy enum (CENTER_OUTWARD, LEFT_TO_RIGHT, RIGHT_TO_LEFT) Enum created
Create sector_snake_configs table (id, sector_id, best_row_index, traversal_direction, within_row_strategy, subsector_grouping_json, created_at) Run migrations

API Endpoints

Status Task Verification
Implement GET /admin/sectors/{sectorId}/seat-map endpoint Returns seat map JSON for canvas
Implement POST /admin/sectors/{sectorId}/seat-map endpoint Creates seat map from canvas JSON
Implement PUT /admin/sectors/{sectorId}/seat-map endpoint Updates existing seat map
Implement DELETE /admin/sectors/{sectorId}/seat-map endpoint Deletes seat map (if no tickets sold)
Implement POST /admin/sectors/{sectorId}/seats/generate endpoint Generates Seat records from JSON
Implement seat generation logic with preview option Preview returns seat list without saving
Support LTR numbering direction Seats numbered 1, 2, 3...
Support RTL numbering direction Seats numbered ...3, 2, 1
Implement POST /admin/sectors/{sectorId}/seats/classify endpoint Bulk classifies seats
Implement PUT /admin/seats/{seatId}/type endpoint Updates single seat type
Implement GET /admin/sectors/{sectorId}/snake-config endpoint Returns snake configuration
Implement PUT /admin/sectors/{sectorId}/snake-config endpoint Updates snake configuration
Implement GET /admin/matches/{matchId}/sectors/status-summary endpoint Returns sector status aggregates

Business Logic

Status Task Verification
Validate seat_map_json schema on save Invalid JSON returns 400
Calculate total_seats from seat_map_json Count matches JSON entries
Prevent seat map update if seats already generated with tickets sold Returns 409 if locked
Auto-number seats based on row_labels and numbering_direction Numbers calculated correctly
Skip gap positions (missing entries) during seat generation Gaps create number jumps

Last Updated: February 2026