# Tournament Deposit Refunds - Implementation Handoff

**Date:** January 27, 2026  
**Status:** Ready to implement  
**Previous Session:** Compacted multiple times, starting fresh

---

## What We're Building

**Tournament Deposits Management Page** at `/admin/ccsoccer/reports/tournament-deposits`

**Purpose:** Allow admins to view and refund captain deposits for tournaments.

---

## Why This Chat Exists

Previous chat kept compacting due to context limits. We got the Jersey Report view committed cleanly (update hook 9045), but removed the unfinished deposit refund code to avoid shipping half-baked features.

Now we're implementing it properly from scratch.

---

## Current State

### ✅ Already Complete

1. **Jersey Report View** - Committed and deployed (update hook 9045)
   - Creates `ccsoccer_jersey_report_view` with pre-joined tables
   - Andrew can run `ddev drush updb` to create it

2. **Registration Entity** - Clean, no deposit refund fields
   - Located: `/web/modules/custom/ccsoccer/src/Entity/Registration.php`
   - Ready to add new fields

3. **Tournament Deposits Page Placeholder** - Exists but not functional
   - Route: `/admin/ccsoccer/reports/tournament-deposits`
   - Controller: `TournamentDepositsController.php`
   - Currently just shows "placeholder" message

---

## What We Need to Build

### Phase 1: Add Fields to Registration Entity

**Two new fields needed on Registration entity:**

```php
// Field 1: Deposit refund status
$fields['deposit_refund_status'] = BaseFieldDefinition::create('list_string')
  ->setLabel(t('Deposit Refund Status'))
  ->setDescription(t('Status of deposit refund (tournament captains only)'))
  ->setSettings([
    'allowed_values' => [
      'pending' => 'Pending',
      'refunded' => 'Refunded',
      'forfeited' => 'Forfeited',
      'not_applicable' => 'Not Applicable',
    ],
  ])
  ->setDefaultValue('not_applicable')
  ->setDisplayOptions('view', ['label' => 'inline', 'weight' => 50]);

// Field 2: Deposit refund date
$fields['deposit_refund_date'] = BaseFieldDefinition::create('timestamp')
  ->setLabel(t('Deposit Refund Date'))
  ->setDescription(t('When deposit was refunded'))
  ->setDisplayOptions('view', ['label' => 'inline', 'weight' => 51]);
```

**Update Hook Needed:**
- Will be `ccsoccer_update_9046()`
- Must call field storage update (not entity updates in D10+)

**Important Notes:**
- These fields only apply to tournament registrations where `is_captain = TRUE`
- Default value is 'not_applicable' (for all non-captain registrations)
- `deposit_refund_date` is NULL until refund processed

---

### Phase 2: Build Tournament Deposits Query

**Query Requirements:**

Get all tournament captain registrations with:
- Captain name (user first/last name)
- Tournament name
- Order details (order number, total price)
- Deposit amount ($50 from order line items)
- Registration date (order completed timestamp)
- Current refund status
- Refund date (if refunded)

**Tables to Join:**
- `ccsoccer_registration` (base)
- `users_field_data` (captain info)
- `user__field_first_name` and `user__field_last_name` (captain name)
- `tournament` (tournament details)
- `commerce_order` (order details)
- `commerce_order_item` (line items to find deposit)
- `commerce_product_variation_field_data` (to identify deposit product)

**Filters:**
- `r.registration_type = 'tournament'`
- `r.is_captain = TRUE`
- `r.tournament_deposit_paid = TRUE`

**Optional:** Consider creating a view similar to Jersey Report for performance.

---

### Phase 3: Build Controller Logic

**File:** `/web/modules/custom/ccsoccer/src/Controller/TournamentDepositsController.php`

**Method to Update:** `public function tournamentDeposits()`

**Logic Flow:**
1. Get tournament filter from URL query (?tournament=ID)
2. Build query to get captain registrations
3. For each registration:
   - Display captain name, tournament, order details
   - Show current refund status
   - If status is 'pending', show "Refund" button
   - If status is 'refunded', show refund date
4. Render as table with filters

---

### Phase 4: Build Refund Form

**New Form Class:** `TournamentDepositRefundForm.php`

**Location:** `/web/modules/custom/ccsoccer/src/Form/TournamentDepositRefundForm.php`

**Purpose:** Confirmation form for processing refund

**Form Elements:**
- Hidden field: registration_id
- Hidden field: order_id
- Display: Captain name, tournament, deposit amount
- Confirmation text: "Are you sure you want to refund this $50 deposit?"
- Submit button: "Confirm Refund"
- Cancel link

**Submit Handler:**
1. Load registration entity
2. Load commerce order
3. Process refund via Commerce API:
   ```php
   $order_storage = \Drupal::entityTypeManager()->getStorage('commerce_order');
   $order = $order_storage->load($order_id);
   
   // Create refund via Commerce Payment API
   // (exact API calls to be determined - Commerce has refund methods)
   ```
4. Update registration fields:
   - `deposit_refund_status = 'refunded'`
   - `deposit_refund_date = REQUEST_TIME`
5. Save registration
6. Show success message
7. Redirect back to Tournament Deposits page

---

### Phase 5: Add Route for Refund Form

**File:** `/web/modules/custom/ccsoccer/ccsoccer.routing.yml`

**New Route:**
```yaml
ccsoccer.tournament_deposit_refund:
  path: '/admin/ccsoccer/tournament-deposit/{registration}/refund'
  defaults:
    _form: '\Drupal\ccsoccer\Form\TournamentDepositRefundForm'
    _title: 'Refund Tournament Deposit'
  requirements:
    _permission: 'administer ccsoccer'
    registration: \d+
```

---

## Technical Considerations

### Commerce Refund Integration

**Unknown:** Exact Commerce API for processing refunds
- Need to research Commerce Payment documentation
- May need to load payment gateway
- May need to create refund transaction
- Consider if manual refund (outside Drupal) vs API refund

**Two Approaches:**

**Option A: Full API Integration**
- Trigger actual refund via Authorize.net API
- More complex, more automated
- Risk: API failures need handling

**Option B: Manual Refund Tracking**
- Admin processes refund manually (Authorize.net dashboard)
- Drupal just tracks status
- Simpler, safer
- Risk: Status can get out of sync

**Recommendation:** Start with Option B for MVP, add Option A later if needed.

---

### Field Storage Updates (Important!)

In Drupal 10+, entity updates work differently:

**DO NOT USE:** `drush entity:updates` (removed in D10+)

**USE INSTEAD:** Field storage definition update in update hook:

```php
function ccsoccer_update_9046() {
  $entity_type = 'ccsoccer_registration';
  
  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
  
  // Get field storage definitions from code
  $field_storage_definitions = \Drupal::service('entity_field.manager')
    ->getFieldStorageDefinitions($entity_type);
    
  // Install new fields
  if (isset($field_storage_definitions['deposit_refund_status'])) {
    $definition_update_manager->installFieldStorageDefinition(
      'deposit_refund_status',
      $entity_type,
      'ccsoccer',
      $field_storage_definitions['deposit_refund_status']
    );
  }
  
  if (isset($field_storage_definitions['deposit_refund_date'])) {
    $definition_update_manager->installFieldStorageDefinition(
      'deposit_refund_date',
      $entity_type,
      'ccsoccer',
      $field_storage_definitions['deposit_refund_date']
    );
  }
  
  return t('Added deposit refund fields to Registration entity.');
}
```

**Testing:** After running update hook, check schema:
```bash
ddev drush sqlq "DESCRIBE ccsoccer_registration"
```

Should see `deposit_refund_status` and `deposit_refund_date` columns.

---

## File Locations

**Files to Modify:**
- `/web/modules/custom/ccsoccer/src/Entity/Registration.php` - Add field definitions
- `/web/modules/custom/ccsoccer/ccsoccer.install` - Add update hook 9046
- `/web/modules/custom/ccsoccer/src/Controller/TournamentDepositsController.php` - Build query and render logic

**Files to Create:**
- `/web/modules/custom/ccsoccer/src/Form/TournamentDepositRefundForm.php` - Refund confirmation form
- `/web/modules/custom/ccsoccer/ccsoccer.routing.yml` - Add refund form route (if not already exists)

---

## Testing Checklist

### After Field Addition (Update Hook 9046)
- [ ] Run `ddev drush updb`
- [ ] Check fields exist: `ddev drush sqlq "DESCRIBE ccsoccer_registration"`
- [ ] Load a tournament captain registration in UI
- [ ] Verify fields display correctly
- [ ] Check default value is 'not_applicable'

### After Query Implementation
- [ ] Visit `/admin/ccsoccer/reports/tournament-deposits`
- [ ] Verify captain registrations display
- [ ] Check all data columns are accurate
- [ ] Test tournament filter dropdown
- [ ] Verify only captains with paid deposits show

### After Refund Form
- [ ] Click "Refund" button for a captain
- [ ] Verify confirmation form displays correctly
- [ ] Submit refund
- [ ] Check registration updated (status = 'refunded', date set)
- [ ] Verify success message shows
- [ ] Confirm table now shows "Refunded" status with date
- [ ] Test canceling refund (no changes)

---

## Questions to Resolve

1. **Commerce Refund API:** What's the exact method to process refunds in Commerce?
   - Check: `commerce_payment` module documentation
   - Check: Authorize.net gateway refund methods

2. **Manual vs Automated Refunds:** 
   - Start with manual (admin tracks status only)?
   - Or build full API integration immediately?

3. **Partial Refunds:** 
   - Deposits are $50 flat
   - Do we ever refund partial amounts?
   - Probably not for MVP

4. **Audit Trail:**
   - Should we track who processed the refund?
   - Add `refunded_by` field (entity_reference to user)?
   - For MVP: probably not needed, can add later

5. **Notifications:**
   - Email captain when deposit refunded?
   - For MVP: probably not needed, can add later

---

## Implementation Order (Recommended)

1. **Add fields to Registration.php** - Quick, foundational
2. **Create update hook 9046** - Deploy field storage
3. **Test field deployment** - Make sure schema updates correctly
4. **Build query in controller** - Get data displaying
5. **Create basic table UI** - Show captains and current status
6. **Add tournament filter** - Make it usable
7. **Build refund form** - Start with status tracking only
8. **Test manual workflow** - Admin refunds via Authorize.net, updates status in Drupal
9. **Add Commerce API integration** - If time permits and API is clear

---

## Success Criteria

**MVP Complete When:**
- ✅ Fields exist on Registration entity
- ✅ Tournament Deposits page shows all captain registrations
- ✅ Can filter by tournament
- ✅ Can see deposit amount, order details, current status
- ✅ Can update refund status to "refunded"
- ✅ Refund date is recorded
- ✅ Table updates to show refunded status

**Nice to Have (Future):**
- Automated refund via Commerce API
- Email notifications to captains
- Bulk refund operations
- Export to CSV
- Audit trail (who refunded, when)

---

## Next Steps

1. Start with adding fields to Registration.php
2. Create update hook 9046
3. Run `ddev drush updb` to test field deployment
4. Build query and UI iteratively

Ready to code when you are!
