first
This commit is contained in:
38
database/seeders/DatabaseSeeder.php
Normal file
38
database/seeders/DatabaseSeeder.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
use WithoutModelEvents;
|
||||
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$this->call(RolePermissionSeeder::class);
|
||||
$this->call(PositionSeeder::class);
|
||||
|
||||
$admin = User::query()->firstOrCreate([
|
||||
'email' => config('election.seed_admin.email'),
|
||||
], [
|
||||
'name' => config('election.seed_admin.name'),
|
||||
'password' => Hash::make(config('election.seed_admin.password')),
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
|
||||
if ($admin->email_verified_at === null) {
|
||||
$admin->forceFill(['email_verified_at' => now()])->save();
|
||||
}
|
||||
|
||||
$admin->assignRole('Admin');
|
||||
|
||||
$this->call(SampleElectionSeeder::class);
|
||||
}
|
||||
}
|
||||
41
database/seeders/PositionSeeder.php
Normal file
41
database/seeders/PositionSeeder.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Position;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class PositionSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed default election staffing positions.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$positions = [
|
||||
['code' => 'PPM', 'name' => 'Penyelia Pusat Mengundi', 'scope' => 'pusat', 'is_public_applyable' => false, 'is_assignable' => true, 'allows_dual_role' => true, 'sort_order' => 10],
|
||||
['code' => 'KTM', 'name' => 'Ketua Tempat Mengundi', 'scope' => 'saluran', 'is_public_applyable' => true, 'is_assignable' => true, 'allows_dual_role' => true, 'sort_order' => 20],
|
||||
['code' => 'KPDP', 'name' => 'Kerani Penyemak Daftar Pemilih', 'scope' => 'saluran', 'is_public_applyable' => true, 'is_assignable' => true, 'allows_dual_role' => false, 'sort_order' => 30],
|
||||
['code' => 'KP', 'name' => 'Kerani Pengundian', 'scope' => 'saluran', 'is_public_applyable' => true, 'is_assignable' => true, 'allows_dual_role' => false, 'sort_order' => 40],
|
||||
['code' => 'PAPM', 'name' => 'Penolong Penyelia Pusat Mengundi', 'scope' => 'pusat', 'is_public_applyable' => true, 'is_assignable' => true, 'allows_dual_role' => false, 'sort_order' => 50],
|
||||
['code' => 'POLIS', 'name' => 'Polis Pengiring', 'scope' => 'external', 'is_public_applyable' => false, 'is_assignable' => false, 'allows_dual_role' => false, 'sort_order' => 60],
|
||||
['code' => 'KKM', 'name' => 'Wakil KKM', 'scope' => 'external', 'is_public_applyable' => false, 'is_assignable' => false, 'allows_dual_role' => false, 'sort_order' => 70],
|
||||
['code' => 'JKM', 'name' => 'Wakil JKM', 'scope' => 'external', 'is_public_applyable' => false, 'is_assignable' => false, 'allows_dual_role' => false, 'sort_order' => 80],
|
||||
['code' => 'CALON_SIMPANAN', 'name' => 'Calon Simpanan', 'scope' => 'saluran', 'is_public_applyable' => false, 'is_assignable' => true, 'allows_dual_role' => false, 'sort_order' => 90],
|
||||
];
|
||||
|
||||
foreach ($positions as $position) {
|
||||
Position::query()->updateOrCreate(
|
||||
['code' => $position['code']],
|
||||
$position,
|
||||
);
|
||||
}
|
||||
|
||||
Position::query()
|
||||
->where('code', 'CALON_SIMPANAN')
|
||||
->update([
|
||||
'code' => 'CALON_TAMBAHAN',
|
||||
'name' => 'Calon Tambahan',
|
||||
]);
|
||||
}
|
||||
}
|
||||
119
database/seeders/RolePermissionSeeder.php
Normal file
119
database/seeders/RolePermissionSeeder.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Spatie\Permission\PermissionRegistrar;
|
||||
|
||||
class RolePermissionSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed the application's roles and permissions.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
app(PermissionRegistrar::class)->forgetCachedPermissions();
|
||||
|
||||
$permissions = [
|
||||
'admin.dashboard.view',
|
||||
'users.manage',
|
||||
'roles.manage',
|
||||
'election.manage',
|
||||
'hierarchy.manage',
|
||||
'positions.manage',
|
||||
'quotas.manage',
|
||||
'qr.manage',
|
||||
'applications.view_all',
|
||||
'applications.create_manual',
|
||||
'applications.update',
|
||||
'applications.approve',
|
||||
'applications.reject',
|
||||
'assignments.manage_all',
|
||||
'representatives.manage',
|
||||
'wheelchairs.manage',
|
||||
'attendance.view_all',
|
||||
'attendance.record_all',
|
||||
'finance.dashboard.view',
|
||||
'finance.view',
|
||||
'finance.verify',
|
||||
'finance.export',
|
||||
'applications.view_finance_fields',
|
||||
'exports.generate_all',
|
||||
'audit.view',
|
||||
'system_notes.create',
|
||||
'ppm.dashboard.view',
|
||||
'applications.view_own_pusat',
|
||||
'applications.review_own_pusat',
|
||||
'applications.approve_own_pusat',
|
||||
'applications.reject_own_pusat',
|
||||
'applications.change_role_own_pusat',
|
||||
'assignments.manage_own_pusat',
|
||||
'representatives.view_own_pusat',
|
||||
'attendance.record_own_pusat',
|
||||
'attendance.view_own_pusat',
|
||||
'exports.generate_own_pusat',
|
||||
'ktm.dashboard.view',
|
||||
'ktm.team.view',
|
||||
'ktm.kp.create',
|
||||
'ktm.kp.approve',
|
||||
'ktm.kp.delete_created',
|
||||
'applications.view_own_team',
|
||||
'public_application.create',
|
||||
'public_application.view_own_status',
|
||||
];
|
||||
|
||||
$permissionModels = [];
|
||||
|
||||
foreach ($permissions as $permission) {
|
||||
$permissionModels[$permission] = Permission::findOrCreate($permission, 'web');
|
||||
}
|
||||
|
||||
$rolePermissions = [
|
||||
'Admin' => $permissions,
|
||||
'Admin Kewangan' => [
|
||||
'finance.dashboard.view',
|
||||
'finance.view',
|
||||
'finance.verify',
|
||||
'finance.export',
|
||||
'applications.view_finance_fields',
|
||||
],
|
||||
'PPM' => [
|
||||
'ppm.dashboard.view',
|
||||
'applications.view_own_pusat',
|
||||
'applications.review_own_pusat',
|
||||
'applications.approve_own_pusat',
|
||||
'applications.reject_own_pusat',
|
||||
'applications.change_role_own_pusat',
|
||||
'assignments.manage_own_pusat',
|
||||
'representatives.view_own_pusat',
|
||||
'attendance.record_own_pusat',
|
||||
'attendance.view_own_pusat',
|
||||
'exports.generate_own_pusat',
|
||||
],
|
||||
'KTM' => [
|
||||
'ktm.dashboard.view',
|
||||
'ktm.team.view',
|
||||
'ktm.kp.create',
|
||||
'ktm.kp.approve',
|
||||
'ktm.kp.delete_created',
|
||||
'applications.view_own_team',
|
||||
],
|
||||
'Pemohon' => [
|
||||
'public_application.create',
|
||||
'public_application.view_own_status',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($rolePermissions as $roleName => $permissionNames) {
|
||||
$role = Role::findOrCreate($roleName, 'web');
|
||||
$role->syncPermissions(array_map(
|
||||
fn (string $permission) => $permissionModels[$permission],
|
||||
$permissionNames,
|
||||
));
|
||||
}
|
||||
|
||||
app(PermissionRegistrar::class)->forgetCachedPermissions();
|
||||
}
|
||||
}
|
||||
248
database/seeders/SampleElectionSeeder.php
Normal file
248
database/seeders/SampleElectionSeeder.php
Normal file
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Dun;
|
||||
use App\Models\Election;
|
||||
use App\Models\Position;
|
||||
use App\Models\PositionQuota;
|
||||
use App\Models\PusatMengundi;
|
||||
use App\Models\SaluranMengundi;
|
||||
use App\Models\StaffAssignment;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SampleElectionSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed sample election hierarchy for development and tests.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$this->cleanupSampleData();
|
||||
|
||||
$election = Election::query()->firstOrNew(['code' => 'PRN2026']);
|
||||
|
||||
if (! $election->exists) {
|
||||
$election->public_uuid = (string) Str::uuid();
|
||||
}
|
||||
|
||||
$election->forceFill([
|
||||
'name' => 'Pilihanraya Negeri 2026',
|
||||
'status' => 'active',
|
||||
'starts_at' => now()->toDateString(),
|
||||
'polling_date' => now()->addWeeks(4)->toDateString(),
|
||||
])->save();
|
||||
|
||||
$election->settings()->updateOrCreate(
|
||||
['election_id' => $election->id],
|
||||
[
|
||||
'registration_start_date' => now()->toDateString(),
|
||||
'registration_end_date' => now()->addWeeks(3)->toDateString(),
|
||||
'polling_date' => $election->polling_date,
|
||||
'is_registration_open' => true,
|
||||
'is_registration_open_override' => null,
|
||||
'is_attendance_active' => false,
|
||||
],
|
||||
);
|
||||
|
||||
$dun = Dun::query()->updateOrCreate(
|
||||
['election_id' => $election->id, 'code' => 'N49'],
|
||||
['name' => 'Kota Iskandar'],
|
||||
);
|
||||
|
||||
$pusat = new PusatMengundi;
|
||||
$pusat->public_uuid = (string) Str::uuid();
|
||||
$pusat->forceFill([
|
||||
'election_id' => $election->id,
|
||||
'code' => 'PM001',
|
||||
'dun_id' => $dun->id,
|
||||
'name' => 'SK Seri Contoh',
|
||||
'address' => 'Jalan Contoh 1, 43000 Kajang, Selangor',
|
||||
])->save();
|
||||
|
||||
$pusat->forceFill([
|
||||
'registration_url' => url('/pohon/'.$pusat->public_uuid),
|
||||
])->save();
|
||||
|
||||
$this->seedSaluran($election, $pusat);
|
||||
$this->seedQuotas($election, $pusat);
|
||||
$this->seedOperationalUsersAndAssignments($election, $pusat);
|
||||
|
||||
$pusat->wheelchairAllocation()->updateOrCreate(
|
||||
['election_id' => $election->id],
|
||||
[
|
||||
'allocated_quantity' => 2,
|
||||
'notes' => 'Sample allocation for Phase 2.',
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
private function cleanupSampleData(): void
|
||||
{
|
||||
$election = Election::query()->where('code', 'PRN2026')->first();
|
||||
|
||||
if (! $election) {
|
||||
return;
|
||||
}
|
||||
|
||||
PositionQuota::query()->where('election_id', $election->id)->forceDelete();
|
||||
SaluranMengundi::query()->where('election_id', $election->id)->forceDelete();
|
||||
PusatMengundi::query()->where('election_id', $election->id)->forceDelete();
|
||||
}
|
||||
|
||||
private function seedSaluran(Election $election, PusatMengundi $pusat): void
|
||||
{
|
||||
foreach (range(1, 3) as $number) {
|
||||
$pusat->saluranMengundis()->updateOrCreate(
|
||||
['number' => (string) $number],
|
||||
[
|
||||
'election_id' => $election->id,
|
||||
'name' => null,
|
||||
'voter_count' => 450 + ($number * 25),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function seedQuotas(Election $election, PusatMengundi $pusat): void
|
||||
{
|
||||
$positions = Position::query()->whereIn('code', [
|
||||
'PPM',
|
||||
'PAPM',
|
||||
'KTM',
|
||||
'KPDP',
|
||||
'KP',
|
||||
'POLIS',
|
||||
'CALON_TAMBAHAN',
|
||||
])->get()->keyBy('code');
|
||||
|
||||
PositionQuota::query()->updateOrCreate(
|
||||
[
|
||||
'election_id' => $election->id,
|
||||
'pusat_mengundi_id' => $pusat->id,
|
||||
'saluran_mengundi_id' => null,
|
||||
'position_id' => $positions['PPM']->id,
|
||||
],
|
||||
['quota' => 1],
|
||||
);
|
||||
|
||||
PositionQuota::query()->updateOrCreate(
|
||||
[
|
||||
'election_id' => $election->id,
|
||||
'pusat_mengundi_id' => $pusat->id,
|
||||
'saluran_mengundi_id' => null,
|
||||
'position_id' => $positions['PAPM']->id,
|
||||
],
|
||||
['quota' => 3],
|
||||
);
|
||||
|
||||
/** @var Collection<int, SaluranMengundi> $salurans */
|
||||
$salurans = $pusat->saluranMengundis()->get();
|
||||
|
||||
foreach ($salurans as $saluran) {
|
||||
foreach ([
|
||||
'KTM' => 1,
|
||||
'KPDP' => 1,
|
||||
'KP' => 4,
|
||||
'POLIS' => 1,
|
||||
'CALON_TAMBAHAN' => 1,
|
||||
] as $code => $quota) {
|
||||
PositionQuota::query()->updateOrCreate(
|
||||
[
|
||||
'election_id' => $election->id,
|
||||
'pusat_mengundi_id' => $pusat->id,
|
||||
'saluran_mengundi_id' => $saluran->id,
|
||||
'position_id' => $positions[$code]->id,
|
||||
],
|
||||
['quota' => $quota],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function seedOperationalUsersAndAssignments(Election $election, PusatMengundi $pusat): void
|
||||
{
|
||||
$ppm = $this->seedUser('PPM Contoh', 'ppm@prn.local', ['PPM', 'KTM']);
|
||||
$ktm = $this->seedUser('KTM Contoh', 'ktm@prn.local', ['KTM']);
|
||||
$this->seedUser('Admin Kewangan Contoh', 'kewangan@prn.local', ['Admin Kewangan']);
|
||||
|
||||
$positions = Position::query()->whereIn('code', ['PPM', 'KTM'])->get()->keyBy('code');
|
||||
/** @var SaluranMengundi $saluranPertama */
|
||||
$saluranPertama = $pusat->saluranMengundis()->where('number', '1')->firstOrFail();
|
||||
|
||||
/** @var SaluranMengundi $saluranKedua */
|
||||
$saluranKedua = $pusat->saluranMengundis()->where('number', '2')->firstOrFail();
|
||||
|
||||
StaffAssignment::query()->updateOrCreate(
|
||||
[
|
||||
'election_id' => $election->id,
|
||||
'user_id' => $ppm->id,
|
||||
'position_id' => $positions['PPM']->id,
|
||||
'pusat_mengundi_id' => $pusat->id,
|
||||
'saluran_mengundi_id' => null,
|
||||
],
|
||||
[
|
||||
'status' => 'active',
|
||||
'assigned_at' => now(),
|
||||
'source' => 'sample_seed',
|
||||
],
|
||||
);
|
||||
|
||||
StaffAssignment::query()->updateOrCreate(
|
||||
[
|
||||
'election_id' => $election->id,
|
||||
'user_id' => $ppm->id,
|
||||
'position_id' => $positions['KTM']->id,
|
||||
'pusat_mengundi_id' => $pusat->id,
|
||||
'saluran_mengundi_id' => $saluranPertama->id,
|
||||
],
|
||||
[
|
||||
'status' => 'active',
|
||||
'assigned_at' => now(),
|
||||
'source' => 'sample_seed_dual_role',
|
||||
],
|
||||
);
|
||||
|
||||
StaffAssignment::query()->updateOrCreate(
|
||||
[
|
||||
'election_id' => $election->id,
|
||||
'user_id' => $ktm->id,
|
||||
'position_id' => $positions['KTM']->id,
|
||||
'pusat_mengundi_id' => $pusat->id,
|
||||
'saluran_mengundi_id' => $saluranKedua->id,
|
||||
],
|
||||
[
|
||||
'status' => 'active',
|
||||
'assigned_at' => now(),
|
||||
'source' => 'sample_seed',
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $roles
|
||||
*/
|
||||
private function seedUser(string $name, string $email, array $roles): User
|
||||
{
|
||||
$user = User::query()->firstOrCreate(
|
||||
['email' => $email],
|
||||
[
|
||||
'name' => $name,
|
||||
'password' => Hash::make('password'),
|
||||
'email_verified_at' => now(),
|
||||
],
|
||||
);
|
||||
|
||||
if ($user->email_verified_at === null) {
|
||||
$user->forceFill(['email_verified_at' => now()])->save();
|
||||
}
|
||||
|
||||
$user->syncRoles($roles);
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user