# CC Soccer D11 - Session Handoff
**Date:** January 10, 2026  
**Last Updated:** Session End

---

## 🔧 COMPLETED THIS SESSION (Tournament Build-Out)

### Tournament Registration Flow - Phases 1 & 2 Complete

The tournament registration system has been fully implemented through Phase 2. Here's what was built:

#### Phase 1: Foundation - Order Completion & Team Creation

**1. Tournament Entity Fields** (`Tournament.php`)
- Added `max_roster_size` field (integer, default 16, required)
- Added `getMaxRosterSize()` helper method
- Added `division` field for tournament grouping

**2. Team Entity Enhancements** (`Team.php`)
- Added `max_roster_size` field (integer, optional - for admin override)
- Added helper methods:
  - `getMaxRosterSize()` - inherits from tournament if not set
  - `getPlayerCount()` - count players on roster
  - `getRosterSpotsRemaining()` - calculate open spots
  - `isFull()` - check if at capacity
  - `isTournamentTeam()` - check if belongs to tournament
  - `isSeasonTeam()` - check if belongs to season

**3. TournamentTeamManager Service** (`src/Service/TournamentTeamManager.php`)
New service with comprehensive methods:
- `createTeam($tournament_id, $captain_id, $team_name)` - Create team when captain pays deposit
- `addPlayerToTeam($team, $player_id, $force)` - Add player to roster
- `removePlayerFromTeam($team, $player_id)` - Remove player from roster
- `isPlayerOnTeam($team, $player_id)` - Check if player on team
- `getTeamByCaptain($tournament_id, $captain_id)` - Find captain's team
- `getPlayerTeam($tournament_id, $player_id)` - Find player's team
- `getPlayerRegistration($tournament_id, $player_id)` - Find player's registration
- `getTeamsForTournament($tournament_id)` - Get all teams
- `getTeamsWithOpenSpots($tournament_id)` - Get teams with capacity
- `getUnassignedPlayers($tournament_id)` - Get free agents
- `getCCSoccerPoolPlayers($tournament_id)` - Get pool players
- `assignPlayerToTeam($registration_id, $team_id, $force)` - Full assignment flow
- `unassignPlayerFromTeam($registration_id)` - Full unassignment flow
- `getTournamentStats($tournament_id)` - Get roster statistics

**4. Tournament Payment Processing** (`ccsoccer.module`)
Added `_ccsoccer_process_tournament_registration()` function handling 5 registration scenarios:
- `'create'` - Captain creating a new team (sets `is_captain=TRUE`, `tournament_deposit_paid=TRUE`, creates team)
- `'join'` - Player joining an existing team (adds to team roster)
- `'token_accept'` - Player accepting a team invitation via token link
- `'ccsoccer_pool'` - Player opts into CCSoccer pool for team assignment
- `'none'` - Free agent registration (no team)

#### Phase 2: Capacity Checks & Validation

**1. TournamentTeamPane Updates** (`src/Plugin/Commerce/CheckoutPane/TournamentTeamPane.php`)
- `getExistingTeams()` - Now filters out full teams, shows player count in dropdown: "Team Name (5/16 players)"
- `validatePaneForm()` - Added capacity check when selecting "Join Team" and when accepting token invitation

**2. GroupController Updates** (`src/Controller/GroupController.php`)
- `acceptTeamInvitation()` - Added capacity check before accepting invitation (shows error if full, doesn't auto-decline)
- `invite()` - Uses `$team->getMaxRosterSize()`, prevents sending invitations to full teams
- `manage()` - Uses `$team->getMaxRosterSize()` for max group size display

**Capacity Checking Flow:**
| Scenario | Where Checked | Behavior |
|----------|---------------|----------|
| Join Team dropdown | `getExistingTeams()` | Full teams hidden from list |
| Join Team validation | `validatePaneForm()` | Error if team filled while on page |
| Token invitation acceptance | `validatePaneForm()` | Error if team filled while on page |
| Accept invitation (post-registration) | `acceptTeamInvitation()` | Error if team full, redirects back |
| Send invitation | `invite()` | Error if team already full |

#### Database Updates
- **Update 9027**: Added `max_roster_size` to Tournament and Team entities
- **Update 9029**: Added `registration_visible` field to Tournament entity
- **Update 9030**: Added `ccsoccer_pool` field to Registration entity

---

## 📊 PROJECT STATUS

**Overall Completion:** ~88%

### ✅ COMPLETE Features (100%)

| Feature | Status | Notes |
|---------|--------|-------|
| Core Entities | ✅ | All 10 entities working |
| Registration Flow | ✅ | Season + tournament checkout |
| Group Management | ✅ | Unified interface for seasons AND tournaments |
| Roster Builder | ✅ | Drag-drop + algorithm (seasons) |
| Schedule Builder | ✅ | Drag-drop + generator (seasons) |
| Notifications | ✅ | Email/SMS with test mode + privileged verify + multi-season |
| Game Status | ✅ | Banner + admin form + auto-reset + credits |
| Credits System | ✅ | Entity + service methods |
| Season Publishing | ✅ | Visibility flags + league inheritance |
| Override System | ✅ | Logic + admin UI complete |
| Content Pages | ✅ | Home, teams, schedule, my-schedule, my-teams |
| Jersey Purchase | ✅ | Cart display fixed, waiver skip for jersey-only |
| Masquerade | ✅ | Footer block with dashboard links |
| Mobile Menu | ✅ | Dropdowns work on iOS |
| Floating Schedule Nav | ✅ | Arrows accessible when scrolled |
| Cancelled Game Display | ✅ | Visual overlay on all schedule views |
| Board/Director Dashboards | ✅ | Role-based access with appropriate footer links |
| **Tournament Entity** | ✅ | Full entity with all fields |
| **Tournament Team Pane** | ✅ | Checkout pane with team selection/creation |
| **Tournament Payment Processing** | ✅ | Team creation, roster joining, token flow |
| **Tournament Capacity System** | ✅ | Roster limits enforced throughout |
| **TournamentTeamManager** | ✅ | Complete service for team/player management |

### ⏳ NEEDS TESTING

- Tournament captain creating team flow (end-to-end)
- Tournament player joining team flow
- Tournament invitation acceptance (both token and post-registration)
- CCSoccer pool player assignment (admin workflow)
- Full season registration workflow end-to-end
- Mobile registration flow

### ❌ TODO (Tournament Phases 3-5)

**Phase 3: Admin Tournament Management**
- Tournament Teams Admin View - List of registered teams with rosters
- Registration counts and deposit tracking
- Tournament Roster Builder - Move unassigned/pool players to teams
- CCSoccer Pool Team Creation (admin creates "CCSoccer Team 1", etc.)

**Phase 4: Tournament Public Display**
- `/tournament/{id}` - Public tournament info page
- `/tournament/{id}/teams` - List of registered teams

**Phase 5: Tournament Schedule**
- Tournament Schedule Builder (different algorithm - bracket/round-robin/pool play)
- Single-day event schedule generation

**Reports (deferred):**
- City Payment Report (revenue share with rainout exclusion)
- Insurance Report (player roster)
- Tournament Team Report (deposits and formation)
- Jersey Report (sizes/distribution)

**Migration (January-February):**
- Board decision on user pruning cutoff (2yr vs 3yr vs 5yr)
- Write migration scripts (users, credits, payment methods)
- Test migration with D7 data subset

---

## 🏗️ ARCHITECTURE HIGHLIGHTS

### Tournament Registration Flow
```
Captain Flow:
1. Captain goes to registration page
2. Captain selects "Create a new team (become captain)"
3. Captain enters team name, acknowledges deposit
4. Deposit product added to cart
5. Captain completes checkout (pays deposit)
6. Team is CREATED when order completes (via _ccsoccer_process_tournament_registration)
7. Captain can now invite players from their group management page (/my-group/{registration})

Player Flow:
1. Player receives invitation (or sees team on registration page)
2. Player goes to registration page
3. Player selects "Join Team" and picks their team
4. Player completes checkout (pays registration fee)
5. Player is added to team roster when order completes
```

### Tournament vs Season Key Differences
| Aspect | Season | Tournament |
|--------|--------|------------|
| Parent Entity | League (inherits settings) | Standalone (no parent) |
| Team Formation | Admin creates teams, assigns players | Captain creates team, invites players |
| Capacity | max_players (individual spots) | max_teams (team spots) |
| Registration Type | Individual pays, assigned to team later | Individual pays, chooses/creates team at checkout |
| Balancing | TeamBalancerService distributes evenly | No balancing - teams self-form |
| Schedule | ScheduleGeneratorService creates weekly games | Different format (bracket, round robin, pool play) |
| Groups | "Group" = keep-together constraint for roster builder | "Team" = the actual playing unit |
| Deposit | No deposit | Captain pays deposit |
| Team Name Source | Taxonomy terms per league | Captain chooses at creation |

### Multi-Season Notification System
- Season field has `cardinality: -1` (unlimited selections)
- Default value callback pre-selects seasons from last 2 years
- JavaScript reads all `selectedOptions` from multi-select
- API accepts `season_ids[]` array parameter
- Service queries with `->condition('season', $season_ids, 'IN')`
- Recipients de-duplicated (player in multiple seasons gets one notification)

### Notification Verification System
- All bulk sends (`sendBulk()`) trigger immediate delivery to privileged users
- Privileged roles: `administrator`, `board_member`, `slofriendly`
- Subject prefixed with `[VERIFY]` so recipients know it's their preview copy
- Regular recipients queued for cron processing

### Footer Dashboard Links (MasqueradeBlock)
- Admin → Admin Dashboard (has `administer ccsoccer`)
- Board Member → Board Tools (has `view reports`)
- Tournament Director (slofriendly) → Both Board Tools AND Director Tools (has both permissions)

### SMS Test Mode
- Automatically enabled in DDEV environment
- Detects `IS_DDEV_PROJECT` env var
- All SMS redirected to configured test number
- Adds "[DEV - was: {original}]" prefix

---

## 📁 KEY FILES

### Module Structure
```
web/modules/custom/ccsoccer/
├── ccsoccer.module           # Main hooks (theme, page_attachments, tournament registration processing)
├── ccsoccer.install          # Update hooks through 9030
├── ccsoccer.libraries.yml    # 15 CSS/JS libraries
├── ccsoccer.routing.yml      # All routes including dashboards
├── ccsoccer.services.yml     # Service definitions
├── css/
│   ├── content-pages.css
│   ├── masquerade.css
│   ├── menu-styling.css
│   ├── notification-form-cleanup.css
│   └── season-view.css
├── js/
│   ├── menu-fix.js
│   ├── notification-confirm.js
│   └── schedule-navigation.js
├── src/
│   ├── Controller/
│   │   ├── AdminController.php
│   │   ├── ContentController.php
│   │   ├── GroupController.php          # Unified for season groups AND tournament teams
│   │   ├── NotificationController.php
│   │   ├── RegistrationController.php
│   │   ├── SeasonController.php
│   │   └── TournamentController.php     # Basic - needs expansion for Phase 4
│   ├── Entity/
│   │   ├── Tournament.php               # max_roster_size, division, etc.
│   │   ├── Team.php                     # max_roster_size, capacity methods
│   │   ├── Registration.php             # is_captain, ccsoccer_pool, etc.
│   │   └── Invitation.php               # Unified for seasons and tournaments
│   ├── Plugin/
│   │   ├── Block/MasqueradeBlock.php
│   │   └── Commerce/CheckoutPane/
│   │       └── TournamentTeamPane.php   # Team selection during checkout
│   └── Service/
│       ├── NotificationService.php
│       ├── TeamBalancerService.php      # Season roster balancing
│       └── TournamentTeamManager.php    # Tournament team/player management
└── templates/
    ├── ccsoccer-group-manage.html.twig  # Unified template (has is_tournament variable)
    ├── ccsoccer-my-registrations.html.twig
    └── ccsoccer-masquerade-block.html.twig
```

---

## 🔧 DEVELOPER SETUP

### After Pulling Latest Code:
```bash
git pull origin main
ddev drush updb -y
ddev drush cr
```

### Current Update Hooks:
- 9027: Tournament and Team max_roster_size fields
- 9029: Tournament registration_visible field
- 9030: Registration ccsoccer_pool field

### Mobile Testing:
```bash
ddev config --bind-all-interfaces
ddev restart
# Access via http://[your-ip].ddev.site

# Disable after testing
ddev config --bind-all-interfaces=false
ddev restart
```

---

## 🚀 NEXT STEPS

### Immediate (Phase 3 - Admin Tournament Management):
1. **Tournament Teams Admin View**
   - Route: `/admin/ccsoccer/tournament/{tournament}/teams`
   - Show all teams with rosters, player counts, captain info
   - Deposit status tracking

2. **Tournament Roster Builder** (for free agents/pool players)
   - Route: `/admin/ccsoccer/tournament/{tournament}/roster`
   - Show unassigned players and CCSoccer pool players
   - Interface to assign them to teams

3. **CCSoccer Pool Team Creation**
   - Admin can create teams without captains (or admin as captain)
   - These become "CCSoccer Team 1", "CCSoccer Team 2", etc.

### Short-Term (Phase 4 - Public Display):
4. Tournament public info page
5. Tournament teams list page

### Later (Phase 5 - Schedule):
6. Tournament schedule builder (bracket/pool play generation)

### Pre-Launch:
- Full end-to-end testing of tournament flows
- Performance testing with real data volumes
- Security audit

---

## 📝 NOTES FOR NEXT SESSION

### Tournament System Ready For Testing:
- Captain can create team and pay deposit → Team entity created
- Player can join existing team → Added to roster
- Player can accept invitation via token → Added to roster
- Player can opt into CCSoccer pool → Flagged for admin assignment
- Capacity enforced at all touchpoints (checkout, invitation acceptance, sending invites)

### Key Test Scenarios:
1. **Captain creates team**: Register for tournament → Select "Create a new team" → Enter team name → Acknowledge deposit → Complete checkout → Verify team created with captain as only player
2. **Player joins team**: Register for tournament → Select "Join an existing team" → Pick team from dropdown (shows player counts) → Complete checkout → Verify added to team roster
3. **Player accepts token invite**: Captain sends invite → Player clicks invite link → Token auto-selects team → Complete checkout → Verify added to team
4. **Capacity limits**: Fill team to max → Verify team disappears from dropdown, cannot accept invites

### GroupController is Unified:
The same `/my-group/{registration}` route and template work for both:
- Season groups (keep-together constraints)
- Tournament teams (actual playing units)

The template uses `is_tournament` variable to adjust UI accordingly.

---

**Git Commit This Session:**
```
Tournament Registration System - Phases 1 & 2 Complete

Phase 1: Foundation
- Add max_roster_size field to Tournament entity (default 16)
- Add max_roster_size field to Team entity with inheritance from tournament
- Add Team helper methods: getPlayerCount(), getRosterSpotsRemaining(), isFull()
- Create TournamentTeamManager service with 14 methods for team/player management
- Implement _ccsoccer_process_tournament_registration() handling 5 scenarios:
  create, join, token_accept, ccsoccer_pool, none
- Add ccsoccer_pool field to Registration entity (update 9030)

Phase 2: Capacity Checks
- TournamentTeamPane.getExistingTeams() filters out full teams, shows roster counts
- TournamentTeamPane.validatePaneForm() validates capacity on join and token accept
- GroupController.acceptTeamInvitation() checks capacity before accepting
- GroupController.invite() prevents inviting to full teams
- GroupController.manage() uses team's max roster size for display

Tournament registration flow now complete through checkout:
- Captain pays deposit → Team created with captain as player
- Player joins team → Added to team roster
- Player accepts invite → Added via token flow
- CCSoccer pool → Flagged for admin assignment

Files modified:
- web/modules/custom/ccsoccer/src/Entity/Tournament.php
- web/modules/custom/ccsoccer/src/Entity/Team.php
- web/modules/custom/ccsoccer/src/Entity/Registration.php
- web/modules/custom/ccsoccer/src/Service/TournamentTeamManager.php (new)
- web/modules/custom/ccsoccer/src/Plugin/Commerce/CheckoutPane/TournamentTeamPane.php
- web/modules/custom/ccsoccer/src/Controller/GroupController.php
- web/modules/custom/ccsoccer/ccsoccer.module
- web/modules/custom/ccsoccer/ccsoccer.install
```

---

**End of Session Handoff**
