128 lines
4.8 KiB
PHP
128 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace Database\Seeders;
|
|
|
|
use App\Models\Barangay;
|
|
use App\Models\Household;
|
|
use App\Models\NotificationPreference;
|
|
use App\Models\QrCodeBatch;
|
|
use App\Models\ResidentProfile;
|
|
use App\Models\User;
|
|
use App\Services\DropOff\DropOffPointFinder;
|
|
use App\Services\Qr\BatchGenerator;
|
|
use App\Services\Qr\QrAllocator;
|
|
use Illuminate\Database\Seeder;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use MatanYadaev\EloquentSpatial\Objects\Point;
|
|
|
|
/**
|
|
* Demo resident accounts for the customer-web walkthrough. Idempotent —
|
|
* safe to re-run. Skips QR allocation when a household already has codes.
|
|
*
|
|
* Logins (all password "password"):
|
|
* juan@verde.local → approved household, 10 free codes (full dashboard)
|
|
* maria@verde.local → pending household (status banner state)
|
|
* pedro@verde.local → no household (lands on onboarding wizard)
|
|
*/
|
|
class DemoResidentSeeder extends Seeder
|
|
{
|
|
public function run(QrAllocator $allocator, BatchGenerator $batchGen, DropOffPointFinder $dops): void
|
|
{
|
|
$barangay = Barangay::first();
|
|
if (! $barangay) {
|
|
$this->command->warn('No barangay seeded — run SamplePsgcSeeder first.');
|
|
|
|
return;
|
|
}
|
|
|
|
// Make sure a free QR batch exists so allocateFreeToHousehold has
|
|
// codes to hand out. Idempotent — only creates if no free batch.
|
|
if (! QrCodeBatch::where('purpose', QrCodeBatch::PURPOSE_FREE)->exists()) {
|
|
$batchGen->generate(quantity: 200, purpose: QrCodeBatch::PURPOSE_FREE);
|
|
$this->command->info('Generated free QR batch (200 codes).');
|
|
}
|
|
|
|
// Use the barangay centroid as the demo address pin (always inside
|
|
// the boundary so geo-resolve and DOP-finder both succeed).
|
|
$lat = $barangay->centroid?->latitude ?? 14.6760;
|
|
$lng = $barangay->centroid?->longitude ?? 121.0437;
|
|
|
|
// 1) Juan — approved household + 10 free codes
|
|
$juan = $this->createResident('juan@verde.local', '+639170000001', 'Juan', 'Dela Cruz');
|
|
$this->createHousehold($juan, $barangay, $lat, $lng, 'approved', $allocator, $dops);
|
|
|
|
// 2) Maria — pending household
|
|
$maria = $this->createResident('maria@verde.local', '+639170000002', 'Maria', 'Santos');
|
|
$this->createHousehold($maria, $barangay, $lat + 0.001, $lng + 0.001, 'pending', null, $dops);
|
|
|
|
// 3) Pedro — no household (tests onboarding flow)
|
|
$this->createResident('pedro@verde.local', '+639170000003', 'Pedro', 'Reyes');
|
|
|
|
$this->command->info('Demo residents seeded:');
|
|
$this->command->info(' juan@verde.local → approved + 10 codes');
|
|
$this->command->info(' maria@verde.local → pending review');
|
|
$this->command->info(' pedro@verde.local → fresh, no household yet');
|
|
$this->command->info(' (password for all: "password")');
|
|
}
|
|
|
|
private function createResident(string $email, string $phone, string $first, string $last): User
|
|
{
|
|
$user = User::firstOrCreate(
|
|
['email' => $email],
|
|
[
|
|
'phone' => $phone,
|
|
'password' => Hash::make('password'),
|
|
'first_name' => $first,
|
|
'last_name' => $last,
|
|
'role' => User::ROLE_RESIDENT,
|
|
'status' => User::STATUS_ACTIVE,
|
|
'email_verified_at' => now(),
|
|
'phone_verified_at' => now(),
|
|
'preferred_language' => 'en',
|
|
],
|
|
);
|
|
|
|
$user->syncRoles([User::ROLE_RESIDENT]);
|
|
|
|
ResidentProfile::firstOrCreate(['user_id' => $user->id]);
|
|
NotificationPreference::firstOrCreate(['user_id' => $user->id]);
|
|
|
|
return $user;
|
|
}
|
|
|
|
private function createHousehold(
|
|
User $head,
|
|
Barangay $barangay,
|
|
float $lat,
|
|
float $lng,
|
|
string $status,
|
|
?QrAllocator $allocator,
|
|
DropOffPointFinder $dops,
|
|
): Household {
|
|
// Auto-pick the nearest active DOP (matches the production flow
|
|
// in HouseholdController::store).
|
|
$dop = $dops->nearest($lat, $lng);
|
|
|
|
$household = Household::firstOrCreate(
|
|
['head_user_id' => $head->id],
|
|
[
|
|
'barangay_id' => $barangay->id,
|
|
'address_line' => '123 Demo Street, '.$barangay->name,
|
|
'coordinates' => new Point($lat, $lng, 4326),
|
|
'household_size' => 4,
|
|
'verification_status' => $status,
|
|
'verified_at' => $status === 'approved' ? now() : null,
|
|
'proof_of_residency_path' => 'demo/proof.jpg',
|
|
'assigned_drop_off_point_id' => $dop?->id,
|
|
],
|
|
);
|
|
|
|
if ($status === 'approved' && $allocator) {
|
|
// Allocator is idempotent — skips when codes already exist.
|
|
$allocator->allocateFreeToHousehold($household);
|
|
}
|
|
|
|
return $household;
|
|
}
|
|
}
|