validateRequest($request, $id); $this->assertPhysicalGateCode($request); $this->updateAttendance($request, $id); return response()->json([ 'status' => 'success', 'message' => 'Kehadiran anda telah disahkan.' ]); } private function validateRequest($request, $id) { $rules = [ 'name' => [ 'required', Rule::unique('voter')->ignore($id)->where(function($query){ $query->where('election_id', Util::getCurrentElection()); }) ], 'no_kp' => [ 'required', Rule::unique('voter')->ignore($id)->where(function($query){ $query->where('election_id', Util::getCurrentElection()); }) ], 'unit' => 'required', 'no_anggota' => [ 'required', Rule::unique('voter')->ignore($id)->where(function($query){ $query->where('election_id', Util::getCurrentElection()); }) ], 'kehadiran' => 'required', ]; if (config('physical_attendance_gate.enabled')) { $rules['physical_gate_code'] = 'required_if:kehadiran,1|string|max:64'; } $this->validate($request, $rules); } /** * When gate is enabled, Fizikal (kehadiran = 1) must match the rotating counter code. * * @param \Illuminate\Http\Request $request * @return void */ private function assertPhysicalGateCode(Request $request) { if (!config('physical_attendance_gate.enabled')) { return; } if ((int) $request->input('kehadiran') !== 1) { return; } $gate = app(PhysicalAttendanceGateCode::class); if (!$gate->isConfigured()) { throw new HttpResponseException(response()->json([ 'status' => 'failed', 'message' => 'Pengesahan kod fizikal tidak dikonfigurasi dengan betul.', ], 503)); } if (!$gate->validates(Util::getCurrentElection(), (string) $request->input('physical_gate_code'))) { throw new HttpResponseException(response()->json([ 'status' => 'failed', 'message' => 'Kod kaunter tidak sah atau telah tamat tempoh. Sila semak skrin pendaftaran dan cuba lagi.', ], 422)); } } private function updateAttendance($request, $id) { $voter = Voter::findOrFail($id); $voter->fill($request->only(['name', 'no_kp', 'no_anggota', 'unit', 'kehadiran'])); // Skip admin verification phase for Fizikal. // When Fizikal is selected and (optionally) the counter gate code is valid, we auto-verify. // For non-Fizikal attendance, keep this field cleared. if ((int) $voter->kehadiran === 1) { $voter->fizikal_registration_verified_at = now(); } else { $voter->fizikal_registration_verified_at = null; } $voter->save(); $mode = ((int) $voter->kehadiran === 1) ? 'physical' : 'online'; activity() ->performedOn($voter) ->withProperties([ 'voter_id' => $voter->id, 'voter_name' => $voter->name, 'election_id' => Util::getCurrentElection(), 'attendance_mode' => $mode, 'ip' => request()->ip(), 'user_agent' => request()->userAgent(), ]) ->log("confirm attendance ({$mode}): {$voter->name}"); } }