validate($request, [ 'selected_no_kp' => 'present|array', 'selected_no_kp.*' => 'nullable|string|max:60', ]); Util::ensureDraftElectionAfterCompletedCycle(); $electionId = Util::getCurrentElection(); $selected = collect($request->input('selected_no_kp', [])) ->map(function ($v) { return trim((string) $v); }) ->filter(function ($v) { return $v !== ''; }) ->unique() ->values(); $stats = [ 'deleted' => 0, 'added' => 0, 'selected_count' => $selected->count(), ]; DB::transaction(function () use ($electionId, $selected) { $selectedSet = $selected->flip(); $current = Voter::where('election_id', $electionId)->get(); foreach ($current as $voter) { $kp = $voter->no_kp; if ($kp === null || $kp === '') { continue; } if (!$selectedSet->has($kp)) { $voter->delete(); } } $existingKp = Voter::where('election_id', $electionId)->pluck('no_kp')->all(); $existingFlip = array_flip($existingKp); foreach ($selected as $noKp) { if (isset($existingFlip[$noKp])) { continue; } $template = Voter::where('no_kp', $noKp)->orderBy('id', 'desc')->first(); if (!$template) { continue; } $new = $template->replicate(); $new->election_id = $electionId; $new->kehadiran = 0; $new->fizikal_registration_verified_at = null; $new->persetujuan = null; $new->status_penyata = 'DRAF'; $new->tarikh_sah = null; $new->cadangan = null; $new->save(); } }); // Log after transaction succeeds (no joins; data from request & current election). activity() ->withProperties([ 'election_id' => $electionId, 'selected_count' => $selected->count(), 'selected_no_kp' => $selected->take(50)->values()->all(), 'ip' => $request->ip(), 'user_agent' => $request->userAgent(), ]) ->log('sync applicable voters'); return response()->json([ 'status' => 'success', 'message' => 'Senarai pengundi untuk pilihan raya semasa dikemas kini.', ]); } }