# Public Self-Service Enrolment — Deployment

Adds a public link members can use to enrol themselves. No login required.
Submissions land as Pending — admin reviews and approves.

## Package contents

```
database/migrations/2026_05_26_180000_add_public_enrolment_to_schemes.php
app/Http/Controllers/PublicEnrolmentController.php
resources/views/public/enrol/form.blade.php
resources/views/public/enrol/done.blade.php
controller-methods.txt              → append snippets to existing controllers
routes-snippet.txt                  → add routes to web.php + corporate.php
public-enrol-card-snippet.blade.php → add to scheme detail page
approve-reject-snippet.blade.php    → for the members table
wipe-dummy-data.sql                 → RUN ONCE before going live
docs/DEPLOYMENT.md                  → this file
```

## Step 1 — Drop files into Laravel root

```bash
cd /home/clararos
unzip -o public-enrolment.zip -d /home/clararos/domains/go.crh.co.ke/public_html/

cd /home/clararos/domains/go.crh.co.ke/public_html
ls database/migrations/2026_05_26_180000*.php
ls app/Http/Controllers/PublicEnrolmentController.php
ls resources/views/public/enrol/form.blade.php
ls resources/views/public/enrol/done.blade.php
```

All four `ls` commands should print the file path.

## Step 2 — Wipe dummy data

⚠️ This deletes the seeded test members + premiums. Patients table is NOT
touched.

```bash
mysql -u clararos_hmis -p'PASSWORD' clararos_hmis < wipe-dummy-data.sql
```

Confirm the BEFORE/AFTER counts in the output — every "rows" should be 0
in the AFTER section, and the MKR scheme should still show up with
`referral_program_enabled = 1`.

## Step 3 — Run the migration

```bash
php artisan migrate
```

One new migration should run:
`add_public_enrolment_to_schemes`

Verify columns added:
```bash
mysql -u clararos_hmis -p'PASSWORD' clararos_hmis -e "
SHOW COLUMNS FROM corporate_accounts LIKE 'public_enrol%';
SHOW COLUMNS FROM corporate_members LIKE 'enrolment%';
SHOW COLUMNS FROM corporate_members LIKE 'submi%';
SHOW COLUMNS FROM corporate_members LIKE 'approv%';
"
```

Expected: 2 columns on corporate_accounts, 5+ columns on corporate_members.

## Step 4 — Append controller methods AND extend CorporateMember $fillable

### 4a. Extend the CorporateMember model's $fillable

```bash
nano app/Models/CorporateMember.php
```

Find the `$fillable = [...]` array and add these field names to it:

```php
'enrolment_source', 'submission_ip', 'submission_user_agent',
'submitted_at', 'approved_by', 'approved_at', 'rejected_reason',
```

The array now ends like:
```php
protected $fillable = [
    'corporate_account_id', 'patient_id', 'member_number',
    'member_type', 'principal_member_id', 'relationship_to_principal',
    'verification_method_1', 'verification_value_1',
    'verification_method_2', 'verification_value_2',
    'membership_start_date', 'membership_end_date',
    'status', 'last_paid_month', 'next_due_date',
    'created_by', 'updated_by',
    // New (self-enrolment):
    'enrolment_source', 'submission_ip', 'submission_user_agent',
    'submitted_at', 'approved_by', 'approved_at', 'rejected_reason',
];
```

Save. Verify:
```bash
php -l app/Models/CorporateMember.php
```

### 4b. Append controller methods

Open `controller-methods.txt`. It contains two snippets — one for
`CorporateAccountController` (slug toggle + rotate) and one for
`CorporateMemberController` (approve + reject).

Open each controller in nano and paste the method block just BEFORE the
final closing `}` of the class.

```bash
nano app/Http/Controllers/Corporate/CorporateAccountController.php
nano app/Http/Controllers/Corporate/CorporateMemberController.php
```

Verify:
```bash
grep -n "togglePublicEnrol\|rotatePublicEnrolSlug" app/Http/Controllers/Corporate/CorporateAccountController.php
grep -n "function approve\|function reject" app/Http/Controllers/Corporate/CorporateMemberController.php
php -l app/Http/Controllers/Corporate/CorporateAccountController.php
php -l app/Http/Controllers/Corporate/CorporateMemberController.php
```

Expected: 2+ matches each, no syntax errors.

## Step 5 — Add routes

### 5a. Public routes (in `routes/web.php`)

At the top, add:
```php
use App\Http\Controllers\PublicEnrolmentController;
```

Then at the top of the file (above any auth-required route groups), add:
```php
Route::get('/enrol/{slug}',          [PublicEnrolmentController::class, 'show'])->name('public.enrol.show');
Route::post('/enrol/{slug}',         [PublicEnrolmentController::class, 'store'])->name('public.enrol.store');
Route::get('/enrol/{slug}/done/{ref}', [PublicEnrolmentController::class, 'done'])->name('public.enrol.done');
```

### 5b. Admin routes (in `routes/corporate.php`)

Inside the existing route group, add:
```php
Route::post('/corporate/accounts/{account}/toggle-public-enrol',
    [CorporateAccountController::class, 'togglePublicEnrol'])->name('accounts.toggle-public-enrol');
Route::post('/corporate/accounts/{account}/rotate-public-enrol-slug',
    [CorporateAccountController::class, 'rotatePublicEnrolSlug'])->name('accounts.rotate-public-enrol-slug');
Route::post('/corporate/members/{member}/approve',
    [CorporateMemberController::class, 'approve'])->name('members.approve');
Route::post('/corporate/members/{member}/reject',
    [CorporateMemberController::class, 'reject'])->name('members.reject');
```

Verify:
```bash
php artisan route:list --path=enrol
php artisan route:list --path=corporate --columns=name | grep -E "approve|reject|public-enrol"
```

Expected: 3 public routes + 4 admin routes.

## Step 6 — Add the Public Enrolment card to the scheme page

Open `resources/views/corporate/accounts/show.blade.php`. Find the
"Scheme Configuration" block (around line 49). Just after it closes,
paste the contents of `public-enrol-card-snippet.blade.php`.

Optional: also wire approve/reject buttons into the members table using
`approve-reject-snippet.blade.php`. The simplest path is to find where
each member row is rendered and add the conditional block right
alongside the existing "Edit" / "Delete" buttons.

## Step 7 — Clear caches and turn ON for MKR

```bash
php artisan view:clear
php artisan route:clear
php artisan config:clear
```

In the browser:
1. Log in as Hospital Admin
2. Corporate / SACCO → Schemes & Members → click MKR SACCO
3. Find the new "Public Self-Enrolment Link" card
4. Click "Turn on public enrolment" — a URL appears
5. Click "Open ↗" to verify the form loads (in a new tab, no login needed)

## Step 8 — Smoke test the form

In the new tab (still as a logged-out anonymous visitor):
1. Try submitting with no fields filled — should fail with consent + required errors
2. Fill with valid data:
   - Member number: `MKR-001`
   - First/Middle/Last name
   - DOB at least 18 years ago
   - Phone: 0712345678
   - NOK details
   - At least one dependant under 18
   - Tick consent
3. Submit — should land on the "Enrolment Received" success page with reference

Back in admin:
4. Schemes & Members → MKR SACCO → members table now shows the new
   principal with "SELF" badge and "Pending" status
5. Click Approve → status flips to Active

## What's NOT included (yet)

- **SMS OTP verification** — to be layered on later. Today, status=Pending
  + your manual SACCO cross-check is the verification step.
- **Bulk SMS broadcast of the link** — share manually for now (WhatsApp,
  printed QR sheets at SACCO office).
- **Captcha** — relying on honeypot + IP rate limit + OTP-later. Add
  reCAPTCHA v3 if you start seeing abuse.

## Security notes

- The slug is unguessable (16 chars random) — soft access control
- Rate limit: max 10 submissions per IP per hour
- Honeypot field catches dumb bots silently
- Mandatory consent checkbox (server-validated, not just client-side)
- All submissions logged with IP + UA + timestamp in `corporate_audit_logs`
- If a link leaks, click "Rotate link" — old URL stops working immediately

## Removal

```bash
php artisan migrate:rollback --step=1
```
Then remove the 4 controller methods, 7 routes, and 2 view snippets.
