# CC Soccer D11 - Session Handoff
**Date:** February 13, 2026
**Session:** D7→D11 Migration Test Run (200 Users)
**Branch:** main (not yet pushed — remember to `git pull` before `git push`)

---

## Summary

Built and executed the first D7→D11 migration test run. Created a custom drush command (`ccsoccer:migrate-d7`) that migrates seasons, users, credits, and registrations from the D7 test database into the D11 local environment. Successfully migrated 200 users with full field mapping, 36 historical seasons as inactive stubs, 64 credits, and 1,633 registrations.

---

## What Was Completed

### 1. D7 Database Connection

**File:** `web/sites/default/settings.local.php`
- Added `d7` database connection pointing to `n6ac4b5_ccsoccer_test`
- Required granting access: `ddev mysql -uroot -e "GRANT ALL ON n6ac4b5_ccsoccer_test.* TO 'db'@'%';"`

### 2. Migration Command

**File:** `src/Drush/Commands/MigrateCommands.php` (NEW)
- Command: `drush ccsoccer:migrate-d7`
- Aliases: `ccs-migrate`
- Options: `--limit=N`, `--step=seasons|users|credits|registrations|all`, `--dry-run`
- Idempotent: Skips existing seasons (by name) and users (by UID)

**Step 1 — Seasons:** Creates stub Season entities from D7 `commerce_product` (type `season_registration`)
- 5-year lookback window (product IDs created after Feb 2021)
- Skips TEST entries and SLO Friendly tournaments
- Maps league from title: "Coed"/"C-SLO"/"C-AG" → league 1, else → league 2
- Sets `active=FALSE`, `registration_visible=FALSE` (historical stubs)
- Real dates from `field_data_field_game_dates`, real prices from `commerce_price`

**Step 2 — Users:** Migrates users by most recent login
- Preserves D7 UIDs (90000+ range, no collision with D11 low UIDs)
- Sets known test password (`CCSoccer2026!`)
- Assigns `player` role
- Maps 10 profile fields (first/last name, gender, DOB, skill levels, goalie, visibility prefs, zip, discount)
- Sets auto-increment after migration to prevent UID collisions

**Step 3 — Credits:** Migrates `season_credit` nodes
- D7 `field_amount_amount` is int (cents) — maps directly to D11 Credits `amount`
- Calculates `status` as active or expired based on expiration date
- Updates `field_credits_balance` on user entities after migration

**Step 4 — Registrations:** Migrates `league_manager_reg_info` records
- Uses season mapping (D7 product_id → D11 season.id) built in step 1
- Tournament mapping hardcoded: D7 product_id 102 → tournament 1, 117 → tournament 2
- Sets `registration_type` = 'season' or 'tournament', `status` = 'paid'

### 3. Season Insert Hook Fix

**File:** `ccsoccer.module` — `ccsoccer_season_insert()`
- Added early return when `active = FALSE`
- Prevents auto-creation of Commerce products and teams for historical/inactive seasons
- First run created 684 unwanted teams + 36 commerce products; cleaned up with script

### 4. Test Run Results

| Entity | Count | Notes |
|--------|-------|-------|
| Seasons | 36 created | All inactive, real dates/prices |
| Users | 200 (10 + 190) | 0 errors |
| Credits | 64 | 2 users with active balances ($9 each) |
| Registrations | 1,633 | 0 errors |

---

## Flags / Open Items

### 🔴 Phone Numbers Missing
D7 has no `field_data_field_phone` table. Phone numbers are in the `sms_user` table (SMS module):
```sql
SELECT uid, number, status FROM sms_user;
```
Numbers in the test DB are cleansed (Australian fake numbers). For real migration, need to extract from `sms_user` and map to D11 `field_phone`. The `sms_user` table has `uid`, `number`, `status`, `gateway` (serialized with country code), and opt-out flag.

### 🟡 Registration Duplicate Protection
Current command doesn't check for existing registrations before creating. Running the command twice will duplicate registrations (not seasons or users — those have duplicate checks). For production migration this is fine (run once), but for repeated test runs, registrations will accumulate.

### 🟡 Migration Documentation
This session's migration plan should be written up as a formal MIGRATION_STEPS.md for Andrew and board validation. **Status: Created this session.**

### 🟢 Password Hashes
D7 uses `$S$D...` hashes (phpass). D11 uses different hashing. Test migration uses known password (`CCSoccer2026!`). Production migration options:
- Transfer D7 hashes and use a compatibility module
- Force password reset for all users on first login

---

## D7 Database Structure (Reference)

**Seasons:** `commerce_product` WHERE `type='season_registration'` (95 total, 36 in 5-year window)
- Dates: `field_data_field_game_dates` (value/value2)
- Price: `field_data_commerce_price` (commerce_price_amount in cents)
- Context: `field_data_field_registration_context` (league/tournament — only recent entries have this)

**Users:** `users` table + field tables
- Profile: `field_data_field_first_name`, `field_data_field_last_name`, `field_data_field_gender`
- League: `field_data_field_league_manager_dob`, `_obs_skill`, `_self_skill`, `_goalie`, `_show_email`, `_show_phone`
- Other: `field_data_field_billing_zip_code`, `field_data_field_message_preference`, `field_data_field_player_discount`
- NO `field_data_field_phone` — phone is in `sms_user` table

**Credits:** `node` WHERE `type='season_credit'`
- Amount: `field_data_field_amount` → column `field_amount_amount` (int, cents)
- Expiry: `field_data_field_expiration_date` → column `field_expiration_date_value` (datetime)

**Registrations:** `league_manager_reg_info` (8,707 records)
- Fields: id, uid, season_id (→ commerce_product.product_id), team_id, status, group_id
- All status = "paid"

---

## For Andrew

After pulling:
1. `ddev drush cr` — picks up new MigrateCommands.php
2. `ddev mysql -uroot -e "GRANT ALL ON n6ac4b5_ccsoccer_test.* TO 'db'@'%';"` — grant access to D7 test DB
3. `ddev drush ccsoccer:migrate-d7 --dry-run --limit=10` — verify everything looks right
4. `ddev drush ccsoccer:migrate-d7 --limit=200` — run for real

Note: The `ccsoccer_season_insert()` hook now skips Commerce product and team creation for `active=FALSE` seasons, so historical stubs won't generate junk data.

---

## Files Modified / Created

| File | Changes |
|------|---------|
| `web/sites/default/settings.local.php` | Added D7 database connection |
| `src/Drush/Commands/MigrateCommands.php` | **NEW** — Migration command |
| `ccsoccer.module` | `ccsoccer_season_insert()` — skip inactive seasons |
| `MIGRATION_STRATEGY.md` | Updated with test run learnings |
| `MIGRATION_STEPS.md` | **NEW** — Step-by-step migration runbook |

---

## Previous Session Archive

Archived to: `archive/SESSION_HANDOFF_2026_02_13.md`

---

**Session Status:** ✅ **COMPLETE** — Ready to push (git pull first!)
