Sync Testing Guide - Local Development

Date: 2025-12-29 Changes Made: Added configurable ForeningLet write protection


What Changed

Files Modified:

  1. config/fanafdelingen.php
  2. Added foreninglet_writes_enabled flag

  3. .env.example

  4. Added FORENINGLET_WRITES_ENABLED documentation

  5. app/Actions/Fanafdelingen/CreateMemberInForeningLet.php

  6. Updated environment check to respect config flag

  7. app/Actions/Fanafdelingen/CancelMembershipInForeningLet.php

  8. Updated environment check to respect config flag

  9. app/Actions/Fanafdelingen/ConvertToCombiMember.php

  10. Updated environment check to respect config flag

  11. app/Actions/Fanafdelingen/ConvertToRegularMember.php

  12. Updated environment check to respect config flag

How It Works

Logic:

if (!App::environment('production') && !config('fanafdelingen.flags.foreninglet_writes_enabled', false)) {
    return; // Block the write
}

Behavior by Environment:

Environment Flag Value Result
Production Any ✅ Writes ALWAYS allowed
Local false (default) 🚫 Writes blocked (SAFE)
Local true ✅ Writes allowed (USE WITH CAUTION)
Staging false (default) 🚫 Writes blocked (SAFE)
Staging true ✅ Writes allowed

Safe Operations (Always Work)

These operations only READ from ForeningLet - safe in any environment:

✅ Sync Members from ForeningLet

php artisan tinker
\App\Actions\Fanafdelingen\SyncMembersFromForeningLet::dispatch();

✅ Sync Activities from ForeningLet

php artisan tinker
\App\Actions\Fanafdelingen\SyncActivitiesFromForeningLet::dispatch();

✅ Sync from Brøndby Support

php artisan tinker
\App\Actions\BrondbySupport\SyncMembersFromBrondbySupport::dispatch();

✅ Full Sync (Orchestrated)

php artisan tinker
\App\Actions\SyncAllSystemsInBatch::dispatch();

These are SAFE - they: - Read from external APIs - Write to your LOCAL database only - Never modify ForeningLet data


Blocked Operations (Protected)

These operations WRITE to ForeningLet - blocked by default in local:

🚫 Create Member (blocked in local by default)

# This will be blocked in local unless flag is enabled
\App\Actions\Fanafdelingen\CreateMemberInForeningLet::dispatch([...]);

🚫 Cancel Membership (blocked in local by default)

# This will be blocked in local unless flag is enabled
\App\Actions\Fanafdelingen\CancelMembershipInForeningLet::dispatch($memberId);

🚫 Convert to Combi (blocked in local by default)

# This will be blocked in local unless flag is enabled
\App\Actions\Fanafdelingen\ConvertToCombiMember::dispatch($member);

🚫 Convert to Regular (blocked in local by default)

# This will be blocked in local unless flag is enabled
\App\Actions\Fanafdelingen\ConvertToRegularMember::dispatch($member);

Testing in Local Environment

Default Behavior (Safe - Read Only)

Your .env file (or no flag set):

# FORENINGLET_WRITES_ENABLED not set (defaults to false)

What you can do: - ✅ Sync members from ForeningLet - ✅ Sync activities from ForeningLet - ✅ Sync from Brøndby Support - ✅ View all data in Filament admin - ✅ Test linking logic (updates local DB only) - ✅ Test bulk actions (they'll silently skip writes) - 🚫 Cannot create members in ForeningLet - 🚫 Cannot update members in ForeningLet - 🚫 Cannot cancel memberships in ForeningLet

Enable Writes (Advanced - Use with Caution)

If you need to test the full flow including writes:

  1. Update your .env file:
FORENINGLET_WRITES_ENABLED=true
  1. Clear config cache:
php artisan config:clear
  1. Now writes will work:
php artisan tinker
\App\Actions\Fanafdelingen\CreateMemberInForeningLet::dispatch([
    'first_name' => 'Test',
    'last_name' => 'User',
    'email' => 'test@example.com',
    // ... other fields
]);

⚠️ WARNING: This will actually create/modify data in ForeningLet!

  1. When done, disable again:
# In .env
FORENINGLET_WRITES_ENABLED=false
php artisan config:clear

Step 1: Verify Local is Safe (First Time Setup)

# Check your .env file
cat .env | grep FORENINGLET_WRITES_ENABLED

# If it's not there or set to false, you're safe
# If it's set to true, change it to false!

# Clear any cached config
php artisan config:clear

Step 2: Run Safe Sync Operations

php artisan tinker

# Sync activities (safe - read only)
>>> \App\Actions\Fanafdelingen\SyncActivitiesFromForeningLet::run();

# Sync members (safe - read only)
>>> \App\Actions\Fanafdelingen\SyncMembersFromForeningLet::run();

# Sync BS members (safe - read only)
>>> \App\Actions\BrondbySupport\SyncMembersFromBrondbySupport::run();

# Check how many members were synced
>>> \App\Models\Member::count();
>>> \App\Models\BrondbySupportMember::count();
>>> \App\Models\Activity::count();

Step 3: Test in Filament Admin

# Start the dev server
php artisan serve

# Or if using Herd
composer run dev

Open: http://localhost:8000/admin (or your Herd URL)

What you can test safely: - Browse members - Browse BS members - Check linking status - View sync logs - Click bulk actions (they'll be blocked, but UI works)

What will be blocked: - "Opret medlemskab af FA" bulk action - "Opsig medlemskab i FA" bulk action - "Konvertér til kombimedlemmer" bulk action - These will execute but silently skip the ForeningLet API calls

Step 4: Test Linking Logic (Safe)

The linking logic only updates your LOCAL database:

php artisan tinker

# Get an unconnected BS member
>>> $bsMember = \App\Models\BrondbySupportMember::unconnectedMembers()->first();

# Test the linking action (only updates local DB)
>>> \App\Actions\BrondbySupport\ConnectBrondbySupportMemberWithFanafdelingenMember::run($bsMember);

# Check if it worked
>>> $bsMember->refresh();
>>> $bsMember->membershipById; // Should show linked member

This is SAFE because it only sets the bs_member_id column in your local database.


Production Deployment

Ensure Flag is Set Correctly

In production .env:

# You can either:
# 1. Not set it (production always allows writes anyway)
# 2. Set it to true (explicit, but redundant)
FORENINGLET_WRITES_ENABLED=true

# Or just leave it out entirely - production ignores the flag

The check in production:

if (!App::environment('production') && !config('...')) {
    // This condition is FALSE in production
    // Because !App::environment('production') = false
    // So writes always work in production
}

After Deployment

Clear config cache:

php artisan config:cache

Test one write operation to verify:

php artisan tinker

# This should work in production
>>> \App\Actions\Fanafdelingen\ConvertToCombiMember::dispatch($someMember);

Troubleshooting

"Nothing happens when I run sync in local"

Check: 1. Are you getting any errors? 2. Check your database - are members being created? 3. Look at sync logs:

php artisan tinker
>>> \App\Models\SyncLog::latest()->get();

"I want to test a write action but it's blocked"

Option 1: Don't test the actual write The write actions are simple - they just send HTTP requests to ForeningLet API. The logic you want to test is probably the linking/sync logic, which is safe.

Option 2: Enable writes temporarily

# In .env
FORENINGLET_WRITES_ENABLED=true

# Clear cache
php artisan config:clear

# Test your action

# REMEMBER TO DISABLE AFTER
FORENINGLET_WRITES_ENABLED=false
php artisan config:clear

"How do I know if writes are blocked?"

Currently, the actions fail silently. You can add logging:

# Check sync logs for any failures
php artisan tinker
>>> \App\Models\SyncLog::where('status', 'error')->latest()->get();

Or add temporary logging to the action:

if (!App::environment('production') && !config('fanafdelingen.flags.foreninglet_writes_enabled', false)) {
    \Log::info('ForeningLet write blocked', ['action' => 'CreateMember']);
    return;
}

Then check: tail -f storage/logs/laravel.log


Quick Reference

Environment Check Commands

# What environment am I in?
php artisan tinker
>>> app()->environment();

# Is the flag enabled?
>>> config('fanafdelingen.flags.foreninglet_writes_enabled');

# Will writes be blocked?
>>> !app()->environment('production') && !config('fanafdelingen.flags.foreninglet_writes_enabled');
// true = writes blocked
// false = writes allowed

Safe Commands (Run Anytime)

# Sync everything (read only)
php artisan tinker
>>> \App\Actions\SyncAllSystemsInBatch::dispatch();

# Check what was synced
>>> \App\Models\Member::count();
>>> \App\Models\BrondbySupportMember::count();
>>> \App\Models\Activity::count();

# View recent sync logs
>>> \App\Models\SyncLog::latest()->take(10)->get();

Verify Protection is Working

php artisan tinker

# This should be blocked in local (unless flag enabled)
>>> \App\Actions\Fanafdelingen\CreateMemberInForeningLet::dispatch([
    'first_name' => 'Test',
    'last_name' => 'User',
    'email' => 'willnotbecreated@test.com',
    'is_member' => 0,
    'enrollment' => now()->toDateString(),
    'field2' => 99999,
    'activity_ids' => [60123],
    'receive_newsletter' => 1,
    'note' => 'Test creation',
], null);

# Check ForeningLet - this user should NOT exist (unless you enabled writes)

Summary

Your local environment is SAFE by default - Syncs work (read-only) - Writes are blocked - You can test everything except actual ForeningLet modifications

Production is always enabled - Flag is ignored in production - All operations work normally

You can enable writes if needed - Set FORENINGLET_WRITES_ENABLED=true in .env - Clear config cache - Use with caution!

Best practice - Keep flag disabled in local - Test with read operations only - Test write logic without actual API calls - Only enable for specific testing scenarios