where('election_id', $electionId) // Ensure we keep all voter columns when adding computed selects ->select('voter.*') // Computed: whether voter has any vote record for this election ->selectSub(function ($sq) use ($electionId) { $sq->from('result') ->selectRaw('COUNT(*)') ->whereColumn('result.voter_id', 'voter.id') ->where('result.election_id', $electionId); }, 'votes_count'); // Computed: payout timestamps (paperless coupon replacement state) $q->selectSub(function ($sq) use ($electionId) { $sq->from('allowance_payouts') ->selectRaw('MAX(paid_at)') ->whereColumn('allowance_payouts.voter_id', 'voter.id') ->where('allowance_payouts.election_id', $electionId) ->where('allowance_payouts.method', 'cash') ->where('allowance_payouts.status', 'paid'); }, 'cash_paid_at'); $q->selectSub(function ($sq) use ($electionId) { $sq->from('allowance_payouts') ->selectRaw('MAX(id)') ->whereColumn('allowance_payouts.voter_id', 'voter.id') ->where('allowance_payouts.election_id', $electionId) ->where('allowance_payouts.method', 'cash') ->where('allowance_payouts.status', 'paid'); }, 'cash_payout_id'); // Latest paid cash payout — Rujukan / Nota for admin allowance modal ($electionId bound as int) $eid = (int) $electionId; $q->addSelect([ DB::raw("(SELECT ap.reference FROM allowance_payouts ap WHERE ap.voter_id = voter.id AND ap.election_id = {$eid} AND ap.method = 'cash' AND ap.status = 'paid' ORDER BY ap.id DESC LIMIT 1) as cash_payout_reference"), DB::raw("(SELECT ap.note FROM allowance_payouts ap WHERE ap.voter_id = voter.id AND ap.election_id = {$eid} AND ap.method = 'cash' AND ap.status = 'paid' ORDER BY ap.id DESC LIMIT 1) as cash_payout_note"), DB::raw("(SELECT u.name FROM allowance_payouts ap LEFT JOIN users u ON u.id = ap.paid_by_admin_id WHERE ap.voter_id = voter.id AND ap.election_id = {$eid} AND ap.method = 'cash' AND ap.status = 'paid' ORDER BY ap.id DESC LIMIT 1) as cash_paid_by_admin_name"), DB::raw("(SELECT u.email FROM allowance_payouts ap LEFT JOIN users u ON u.id = ap.paid_by_admin_id WHERE ap.voter_id = voter.id AND ap.election_id = {$eid} AND ap.method = 'cash' AND ap.status = 'paid' ORDER BY ap.id DESC LIMIT 1) as cash_paid_by_admin_email"), ]); $q->selectSub(function ($sq) use ($electionId) { $sq->from('allowance_payouts') ->selectRaw('MAX(paid_at)') ->whereColumn('allowance_payouts.voter_id', 'voter.id') ->where('allowance_payouts.election_id', $electionId) ->where('allowance_payouts.method', 'bank') ->where('allowance_payouts.status', 'paid'); }, 'bank_paid_at'); $q->selectSub(function ($sq) use ($electionId) { $sq->from('allowance_payouts') ->selectRaw('MAX(id)') ->whereColumn('allowance_payouts.voter_id', 'voter.id') ->where('allowance_payouts.election_id', $electionId) ->where('allowance_payouts.method', 'bank') ->where('allowance_payouts.status', 'paid'); }, 'bank_payout_id'); // Optional search filters $noAnggota = request('no_anggota'); if ($noAnggota !== null && $noAnggota !== '') { $q->where('no_anggota', 'like', '%' . $noAnggota . '%'); } $noKp = request('no_kp'); if ($noKp !== null && $noKp !== '') { $q->where('no_kp', 'like', '%' . $noKp . '%'); } $name = request('name'); if ($name !== null && $name !== '') { $q->where('name', 'like', '%' . $name . '%'); } // Optional attendance filter: 1 = Fizikal, 2 = Maya $kehadiran = request('kehadiran'); if ($kehadiran !== null && $kehadiran !== '') { if ($kehadiran === 'unset') { $q->where(function ($qq) { $qq->whereNull('kehadiran')->orWhere('kehadiran', 0); }); } else { $k = (int) $kehadiran; if (in_array($k, [1, 2], true)) { $q->where('kehadiran', $k); } } } // Fizikal registration queue: pending = fizikal but not admin-verified yet $fizikalReg = request('fizikal_reg'); if ($fizikalReg !== null && $fizikalReg !== '') { if ($fizikalReg === 'pending') { $q->where('kehadiran', 1)->whereNull('fizikal_registration_verified_at'); } elseif ($fizikalReg === 'verified') { $q->where('kehadiran', 1)->whereNotNull('fizikal_registration_verified_at'); } } // Vote status: 1 = has voted, 0 = has not voted (for this election) $hasVoted = request('has_voted'); if ($hasVoted !== null && $hasVoted !== '') { $hv = (string) $hasVoted; if ($hv === '1' || $hv === 'true' || $hv === 'yes') { $q->whereExists(function ($sub) use ($electionId) { $sub->from('result') ->whereColumn('result.voter_id', 'voter.id') ->where('result.election_id', $electionId); }); } elseif ($hv === '0' || $hv === 'false' || $hv === 'no') { $q->whereNotExists(function ($sub) use ($electionId) { $sub->from('result') ->whereColumn('result.voter_id', 'voter.id') ->where('result.election_id', $electionId); }); } } // Cash allowance payment status (matches admin "Status" column: cash_paid_at) $cashStatus = request('cash_status'); if ($cashStatus !== null && $cashStatus !== '') { if ($cashStatus === 'paid') { $q->whereExists(function ($sub) use ($electionId) { $sub->from('allowance_payouts') ->whereColumn('allowance_payouts.voter_id', 'voter.id') ->where('allowance_payouts.election_id', $electionId) ->where('allowance_payouts.method', 'cash') ->where('allowance_payouts.status', 'paid'); }); } elseif ($cashStatus === 'unpaid') { $q->whereNotExists(function ($sub) use ($electionId) { $sub->from('allowance_payouts') ->whereColumn('allowance_payouts.voter_id', 'voter.id') ->where('allowance_payouts.election_id', $electionId) ->where('allowance_payouts.method', 'cash') ->where('allowance_payouts.status', 'paid'); }); } } // Return all voters with pagination return $q->paginate(1000); } }