# CC Soccer D11 - Session Handoff
**Date:** March 21, 2026
**Session:** Migration prep + full D7→D11 migration run
**Branch:** `main`

---

## Current State

### Migration is DONE locally ✅

A clean, verified D11 database has been produced from the D7 production data and exported to `d11-production-ready.sql.gz` (1.35 MB compressed) in the project root.

**Final migration numbers:**
- Seasons: 42 created (inactive history stubs, 5-year lookback)
- Users: 1,732 created, 234 skipped (Drupal admin/test users), 0 errors
- Credits: 583 created (from D7 userpoints balances)
- Registrations: 5,076 created, 0 errors

**Key files in project root (all gitignored):**
- `d7-production-20260321.sql.gz` — D7 production dump (pulled March 21, 2026)
- `d11-production-ready.sql.gz` — Final migrated D11 DB ready for server upload
- `ccsoccer-d11-migrated-200users.sql.gz` — Old test run, can be deleted

---

## What Changed This Session

### Update hook 9061 — fixed and verified
- First version used wrong removal method (`uninstallFieldStorageDefinition`) — broke the site
- Fixed to use `FieldConfig::delete()` + `FieldStorageConfig::delete()` (correct for config-based fields)
- Manually cleaned up orphaned config with `php:eval` after the failed run
- Removed four dead user fields: `field_has_jersey`, `field_jersey_size`, `field_credits_balance`, `field_notification_prefs`
- Also cleaned stale references from `ccsoccer.module` form alters

### MigrateCommands.php — multiple updates
- **Credits:** Completely rewrote Step 3. Was querying `season_credit` nodes (wrong). Now queries D7 `userpoints` table — the actual source of truth for credit balances. 100 points = $1.00.
- **Credit expiry:** 1 year from migration date. D7 userpoints had no expiry concept.
- **User filter:** Added `AND u.mail NOT LIKE '%@applynx.com'` to exclude dev/test accounts (Scott Sarmento's dev accounts were in the 5-year window with fake credit balances)
- **Limit default:** Changed from 200 to 0 (no limit). Pass `--limit=N` for test runs.
- **`--dev` flag removed:** Always uses random per-user passwords now. Use masquerade for local dev access.
- **Notification preference:** Added mapping from D7 `field_message_preference` → D11 `field_notification_preference` (D7 value `sms` → D11 value `text`)
- **Phone:** Added `LEFT JOIN {sms_user}` to pull phone numbers into `field_phone`
- **`updateCreditBalances()` removed:** `field_credits_balance` is gone; `CreditManagerService` calculates live

### ccsoccer.module — form alter cleanup
- Removed `field_credits_balance`, `field_has_jersey`, `field_jersey_size` from `$hidden_fields` and `$admin_only_fields` arrays in form alters

### DEPLOYMENT_GUIDE.md — updated to reflect reality
- SSH section rewritten with actual setup (existing `id_rsa` key, `ccsoccer` alias, IP address)
- Server directory structure updated with real paths (`ccsoccer_site`, `slofriendly_site`, `test_ccsoccer_site`)
- Instructions for adding Andrew to SSH when needed

### MIGRATION_STEPS.md — D7 dump timestamp recorded
- Pull timestamp: March 21, 2026 (afternoon Pacific)
- Post-launch checklist: verify credits/registrations issued after this date

### settings.local.php — D7 connection updated
- Changed from `n6ac4b5_ccsoccer_test` to `n6ac4b5_ccsoccer` (production dump)

---

## Key Discoveries This Session

### Credits were in userpoints, not season_credit nodes
D7 uses the `userpoints` contrib module for credit balances. `season_credit` nodes are admin-facing transaction records only. The `userpoints` table has one row per user with their current balance. This was discovered by tracing the D7 season > players view back to `userpoints_get_current_points()` in `league_manager_registration.inc`.

Previous migration was only migrating 8 active season_credit nodes. Correct number is 583 userpoints balances totaling ~$13,600.

### D7 user count breakdown
- 3,578 total users (including blocked)
- 2,493 active (status=1)
- 1,968 active + logged in within 5 years (before applynx exclusion)
- 1,732 final migrated count (after applynx exclusion)

---

## Team handling — deferred refactor (discuss with Andrew first)
Current behavior: all term Teams created eagerly on season save. Desired: Teams only created when Roster Builder opens, empty ones deleted on save.

**Do not implement until:** migration complete, Andrew's passkey merged, Andrew consulted (he built the roster builder).

**Three changes when ready:**
1. `ccsoccer.module` `ccsoccer_season_insert` — remove `createTeamsForSeason()` call
2. `TeamBalancerService::getRosterState()` — lazy creation if no teams exist yet
3. `RosterBuilderController::save()` — delete Team entities with zero players after save

---

## Next Steps

### 1. Push current code changes to main
All the session's code changes are sitting unpushed locally:
- `ccsoccer.install` (update hook 9061 fixed)
- `ccsoccer.module` (form alter cleanup)
- `MigrateCommands.php` (credits, phone, notification pref, filters, limit)
- `DEPLOYMENT_GUIDE.md`, `MIGRATION_STEPS.md` (doc updates)

**Andrew's sequence after pulling:**
```bash
git pull
ddev drush updb -y   # runs 9061, drops four user fields
ddev drush cr
```
No `drush cim` needed — no config YAML changes.

### 2. Decide: migrate subdomain vs point TEST at prod DB ✅ DB IS READY
`n6ac4b5_d11prod` is live on the server with 1,732 users imported and verified.

Options:
- **Option A:** Create `migrate.ccsoccer.com` subdomain pointing at `n6ac4b5_d11prod` — clean separation, TEST unaffected
- **Option B:** Point `test.ccsoccer.com` directly at `n6ac4b5_d11prod` — simpler, but disrupts Andrew's test environment

Recommendation: Option A. Set up migrate subdomain when ready.

**Before pointing any site at `n6ac4b5_d11prod`:** verify leagues and tournaments exist in the DB (they should be there from config import + manual creation in local dev, but confirm).

### 3. Configure site settings.local.php
Point at `n6ac4b5_d11prod` using `n6ac4b5_ccsoccer_user` (already has All Privileges granted).
Set trusted_host_patterns, configure Authorize.net sandbox.

**Import notes:**
- Original SQL had `DEFINER=\`db\`@\`%\`` from DDEV — caused permission error on InMotion
- Fixed with: `sed 's/DEFINER=[^*]*\*/\*/' d11-production-ready.sql > d11-production-ready-clean.sql`
- Always use the clean version for future imports to this server

### 5. Post-migration launch checklist (at go-live)
- [ ] Manually assign board member roles + permanent_override: Haley, Layne, Julie, Myk, Kyle
- [ ] Verify credit balances for a handful of known users against D7
- [ ] Send password reset email to all migrated users (they all have random passwords)
- [ ] Check for any credits/registrations issued in D7 after March 21, 2026 dump date
- [ ] All security hardening items from prior handoffs
- [ ] Delete `ccsoccer-d11-migrated-200users.sql.gz` from repo root (old test file)

---

## Migration Command Usage (final)
```bash
# Full production migration (no limit, random passwords):
ddev drush ccsoccer:migrate-d7

# Test run (50 users):
ddev drush ccsoccer:migrate-d7 --limit=50

# Dry run:
ddev drush ccsoccer:migrate-d7 --dry-run

# Reset and re-run sequence:
ddev drush si -y
ddev drush entity:delete shortcut_set default -y
ddev drush config:set system.site uuid $(grep uuid config/sync/system.site.yml | awk '{print $2}') -y
ddev drush cim -y
ddev drush updb -y
ddev drush cr
ddev drush ccsoccer:migrate-d7
```

---

## Files Changed This Session
- `web/modules/custom/ccsoccer/ccsoccer.install` — update hook 9061 fixed
- `web/modules/custom/ccsoccer/ccsoccer.module` — form alter cleanup
- `web/modules/custom/ccsoccer/src/Drush/Commands/MigrateCommands.php` — major credits rewrite + filters + phone + notification pref
- `web/sites/default/settings.local.php` — D7 DB connection updated (not in git)
- `DEPLOYMENT_GUIDE.md` — SSH section + server paths updated
- `MIGRATION_STEPS.md` — D7 dump timestamp added

---

## Server Quick Reference
```bash
# SSH in
ssh ccsoccer

# Deploy to test
cd ~/public_html/test_ccsoccer_site
git pull
PATH=/opt/cpanel/ea-php83/root/usr/bin:$PATH /opt/cpanel/ea-php83/root/usr/bin/php vendor/drush/drush/drush.php -r web updb -y
PATH=/opt/cpanel/ea-php83/root/usr/bin:$PATH /opt/cpanel/ea-php83/root/usr/bin/php vendor/drush/drush/drush.php -r web cr

# If new PHP classes added:
PATH=/opt/cpanel/ea-php83/root/usr/bin:$PATH /opt/cpanel/ea-php83/root/usr/bin/php /opt/cpanel/composer/bin/composer dump-autoload --ignore-platform-req=ext-intl

# If composer.lock changed:
PATH=/opt/cpanel/ea-php83/root/usr/bin:$PATH /opt/cpanel/ea-php83/root/usr/bin/php /opt/cpanel/composer/bin/composer install --ignore-platform-req=ext-intl
```

## Test Server .htaccess (IP Whitelist)
Not in git — protected via `skip-worktree`. If ever lost:
```apache
Require ip 68.249.41.9 35.151.50.130 99.8.107.54 97.84.70.141
<IfModule mod_headers.c>
  Header set X-Robots-Tag "noindex, nofollow, noarchive"
</IfModule>
```
```bash
git update-index --skip-worktree web/.htaccess
```

## Git Workflow
- Always `git pull` before `git push` — Andrew may have pushed changes
- `main` is the primary branch
- `settings.local.php` is NOT in git — never commit it
- `*.sql.gz` files are gitignored — never commit DB dumps
