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