Allowance check
This commit is contained in:
@@ -13,7 +13,22 @@ class AddController extends Controller
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
$this->validateRequest($request);
|
||||
$this->insertVoter($request);
|
||||
$voter = $this->insertVoter($request);
|
||||
|
||||
activity()
|
||||
->performedOn($voter)
|
||||
->withProperties([
|
||||
'election_id' => $voter->election_id,
|
||||
'voter_id' => $voter->id,
|
||||
'voter_name' => $voter->name,
|
||||
'no_kp' => $voter->no_kp,
|
||||
'no_anggota' => $voter->no_anggota,
|
||||
'unit' => $voter->unit,
|
||||
'created_by_admin_id' => $request->user() ? (int) $request->user()->id : null,
|
||||
'ip' => $request->ip(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
])
|
||||
->log("create voter: {$voter->name}");
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Voter added successfully'
|
||||
@@ -24,7 +39,7 @@ class AddController extends Controller
|
||||
{
|
||||
$voter = $request->all();
|
||||
$voter['election_id'] = Util::getCurrentElection();
|
||||
Voter::create($voter);
|
||||
return Voter::create($voter);
|
||||
}
|
||||
|
||||
private function validateRequest($request)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API\v1\Voter;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Util;
|
||||
use App\Voter;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Lists every distinct member (by no_kp) found anywhere in voter,
|
||||
* for picking who is applicable to the current election.
|
||||
*/
|
||||
class AllMembersCatalogController extends Controller
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
$electionId = Util::getCurrentElection();
|
||||
|
||||
$maxIds = DB::table('voter')
|
||||
->select(DB::raw('MAX(id) as id'))
|
||||
->whereNotNull('no_kp')
|
||||
->where('no_kp', '!=', '')
|
||||
->groupBy('no_kp')
|
||||
->pluck('id');
|
||||
|
||||
$rows = Voter::whereIn('id', $maxIds)
|
||||
->orderBy('name')
|
||||
->get(['no_kp', 'no_anggota', 'name', 'unit']);
|
||||
|
||||
$inCurrent = Voter::where('election_id', $electionId)
|
||||
->pluck('no_kp')
|
||||
->filter(function ($kp) {
|
||||
return $kp !== null && $kp !== '';
|
||||
})
|
||||
->flip();
|
||||
|
||||
$catalog = $rows->map(function ($row) use ($inCurrent) {
|
||||
return [
|
||||
'no_kp' => $row->no_kp,
|
||||
'no_anggota' => $row->no_anggota,
|
||||
'name' => $row->name,
|
||||
'unit' => $row->unit,
|
||||
'in_current_election' => $inCurrent->has($row->no_kp),
|
||||
];
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'election_id' => $electionId,
|
||||
'catalog' => $catalog->values()->all(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,11 @@
|
||||
namespace App\Http\Controllers\API\v1\Voter;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Util;
|
||||
use App\Services\PhysicalAttendanceGateCode;
|
||||
use App\Voter;
|
||||
|
||||
class AttendanceController extends Controller
|
||||
@@ -14,6 +16,7 @@ class AttendanceController extends Controller
|
||||
public function __invoke(Request $request, $id)
|
||||
{
|
||||
$this->validateRequest($request, $id);
|
||||
$this->assertPhysicalGateCode($request);
|
||||
$this->updateAttendance($request, $id);
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
@@ -23,7 +26,7 @@ class AttendanceController extends Controller
|
||||
|
||||
private function validateRequest($request, $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
$rules = [
|
||||
'name' => [
|
||||
'required',
|
||||
Rule::unique('voter')->ignore($id)->where(function($query){
|
||||
@@ -46,15 +49,72 @@ class AttendanceController extends Controller
|
||||
})
|
||||
],
|
||||
|
||||
'kehadiran' => 'required'
|
||||
'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 = $request->all();
|
||||
Voter::find($id)->update($voter);
|
||||
$voter = Voter::findOrFail($id);
|
||||
$voter->fill($request->only(['name', 'no_kp', 'no_anggota', 'unit', 'kehadiran']));
|
||||
|
||||
if ((int) $voter->kehadiran === 1) {
|
||||
$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}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,12 +5,105 @@ namespace App\Http\Controllers\API\v1\Voter;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Util;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Voter;
|
||||
|
||||
class GetController extends Controller
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
return Voter::where('election_id', Util::getCurrentElection())->paginate(50);
|
||||
$electionId = Util::getCurrentElection();
|
||||
|
||||
$q = Voter::query()
|
||||
->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');
|
||||
|
||||
$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');
|
||||
}
|
||||
}
|
||||
|
||||
return $q->paginate(50);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,201 +16,249 @@ class LoginController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
//return response()->json(Auth::guard('user'));
|
||||
/* Request OTP */
|
||||
try {
|
||||
$currentDateTime = Carbon::now();
|
||||
|
||||
/* Request OTP */
|
||||
try{
|
||||
$currentDateTime = Carbon::now();
|
||||
$expired_at = Carbon::now()->addMinutes(config('onewaysms.minutes'));
|
||||
$created_at = $currentDateTime->toDateTimeString();
|
||||
|
||||
$expired_at = Carbon::now()->addMinutes(config('onewaysms.minutes'));
|
||||
$created_at = $currentDateTime->toDateTimeString();
|
||||
$voter = Voter::where('no_kp', $request->no_kp)->firstOrFail();
|
||||
|
||||
$voter = Voter::where('no_kp',$request->no_kp)->firstOrFail();
|
||||
if ($voter) {
|
||||
$expiration = $this->checkExpirationTAC($request->no_kp);
|
||||
|
||||
if($voter){
|
||||
$expiration = $this->checkExpirationTAC($request->no_kp);
|
||||
|
||||
if($expiration['notexpired'] == true){
|
||||
throw new \Exception('Expired TAC : ' . $expiration['remaining']);
|
||||
}
|
||||
|
||||
$sms_token = $this->getTokenSMS($voter->telefon,$currentDateTime);
|
||||
|
||||
if($sms_token == 'error'){
|
||||
throw new \Exception('error in getting the data from SMSToken');
|
||||
}
|
||||
|
||||
$user_otp = new UserOTP();
|
||||
$user_otp->nokp = $voter->no_kp;
|
||||
$user_otp->telefon = $voter->telefon;
|
||||
$user_otp->token = $sms_token;
|
||||
$user_otp->created_at = $created_at;
|
||||
$user_otp->expired_at = $expired_at;
|
||||
$user_otp->save();
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'message' => 'generated SMS Token',
|
||||
'notel' => $voter->telefon,
|
||||
'nokp' => $voter->no_kp,
|
||||
];
|
||||
|
||||
if (app()->environment(['local', 'development'])) {
|
||||
$response['debug_otp'] = $sms_token;
|
||||
}
|
||||
|
||||
return response()->json($response);
|
||||
|
||||
}else{
|
||||
return response()->json([
|
||||
'status' => 'failed',
|
||||
'message' => 'Does not found NO KP.'
|
||||
]);
|
||||
}
|
||||
}catch(Exception $e){
|
||||
return response()->json([
|
||||
'status' => 'failed',
|
||||
'message' => 'Error: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkExpirationTAC($nokp) : Array{
|
||||
$currentDateTime = Carbon::now();
|
||||
|
||||
$expiretac = UserOTP::where('nokp',$nokp)
|
||||
->whereDate('created_at','=',$currentDateTime)
|
||||
->whereDate('expired_at','=',$currentDateTime)
|
||||
->whereTime('expired_at','>=',$currentDateTime->toTimeString())
|
||||
->whereTime('created_at','<=',$currentDateTime->toTimeString())
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
if($expiretac){
|
||||
$remaining = $this->getRemainingTime($expiretac->expired_at);
|
||||
return ['notexpired' => true,'remaining' => $remaining];
|
||||
}else{
|
||||
return ['notexpired' => false];
|
||||
}
|
||||
}
|
||||
|
||||
public function verifyTAC(Request $request){
|
||||
|
||||
$authenticateOTP = $this->getTACModel($request->no_kp,$request->token);
|
||||
|
||||
if(!$authenticateOTP['condition']){
|
||||
return response()->json([
|
||||
'status' => 'failed',
|
||||
'message' => $authenticateOTP['message']
|
||||
]);
|
||||
}
|
||||
|
||||
if (Auth::guard('voter')->attempt(['no_kp' => $request->no_kp, 'password' => 'admin', 'election_id' => Util::getCurrentElection()])) {
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Login successfully.',
|
||||
'user' => Auth::guard('voter')->user(),
|
||||
'election_status' => Util::getElectionStatus(),
|
||||
'token' => Auth::guard('voter')->user()->createToken('My Token', ['vote'])->accessToken
|
||||
]);
|
||||
} else {
|
||||
return response()->json([
|
||||
'status' => 'failed',
|
||||
'message' => 'No. KP tidak wujud. Atau sesi mengundi telah tamat'
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Login successfully.',
|
||||
]);
|
||||
}
|
||||
|
||||
private function getTACModel($no_kp,$token){
|
||||
$currentDateTime = Carbon::now();
|
||||
|
||||
$result = UserOTP::where('nokp',$no_kp)
|
||||
->whereDate('created_at','=',$currentDateTime)
|
||||
->whereDate('expired_at','=',$currentDateTime)
|
||||
->whereTime('expired_at','>=',$currentDateTime->toTimeString())
|
||||
->whereTime('created_at','<=',$currentDateTime->toTimeString())
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
if(!$result){
|
||||
return ['condition' => false, 'message' => 'Token Expired'];
|
||||
}
|
||||
|
||||
if($result['token'] == $token){
|
||||
return ['condition' => true];
|
||||
}else{
|
||||
return ['condition' => false, 'message' => 'Invalid Token'];
|
||||
}
|
||||
}
|
||||
|
||||
private function getTokenSMS($tele,$date){
|
||||
$tac_token = $this->randum_number(6,2,false);
|
||||
if(env("APP_ENV") == 'production'){
|
||||
$mobileno = '6' . $tele;
|
||||
$messages = sprintf(config('onewaysms.message'),$tac_token,$date);
|
||||
|
||||
$parameter = [
|
||||
'apiusername' => env("ONEWAY_SMS_USERNAME"),
|
||||
'apipassword' => env("ONEWAY_SMS_PASSWORD"),
|
||||
'senderid' => env("ONEWAY_SMS_SENDERID",'INFO'),
|
||||
'mobileno' => $mobileno,
|
||||
'message'=> $messages,
|
||||
'languagetype'=> env("ONEWAY_SMS_LANG",1)
|
||||
];
|
||||
|
||||
$client = new Client(['verify' => false]);
|
||||
$status = $client->get('http://gateway.onewaysms.com.my:10001/api.aspx',[
|
||||
'query' => $parameter
|
||||
]);
|
||||
|
||||
if($status){
|
||||
return $tac_token;
|
||||
}else{
|
||||
return 'error';
|
||||
}
|
||||
|
||||
}else{
|
||||
return $tac_token;
|
||||
}
|
||||
}
|
||||
|
||||
private function randum_number($len = 6,$dup = 1, $sort = false){
|
||||
if($dup < 1)
|
||||
throw new \InvalidArgumentException('Second argument is < 1');
|
||||
|
||||
$num = range(0,9);
|
||||
shuffle($num);
|
||||
|
||||
$num = array_slice($num, 0, ($len-$dup) + 1);
|
||||
|
||||
if($dup > 0){
|
||||
$k = array_rand($num, 1);
|
||||
for($i=0;$i<($dup-1);$i++)
|
||||
{
|
||||
$num[] = $num[$k];
|
||||
if ($expiration['notexpired'] == true) {
|
||||
throw new \Exception('Expired TAC : ' . $expiration['remaining']);
|
||||
}
|
||||
|
||||
$sms_token = $this->getTokenSMS($voter->telefon, $currentDateTime);
|
||||
|
||||
if ($sms_token == 'error') {
|
||||
throw new \Exception('error in getting the data from SMSToken');
|
||||
}
|
||||
|
||||
$user_otp = new UserOTP();
|
||||
$user_otp->nokp = $voter->no_kp;
|
||||
$user_otp->telefon = $voter->telefon;
|
||||
$user_otp->token = $sms_token;
|
||||
$user_otp->created_at = $created_at;
|
||||
$user_otp->expired_at = $expired_at;
|
||||
$user_otp->save();
|
||||
|
||||
activity()
|
||||
->performedOn($voter)
|
||||
->withProperties([
|
||||
'election_id' => Util::getCurrentElection(),
|
||||
'voter_id' => $voter->id,
|
||||
'voter_name' => $voter->name,
|
||||
'no_kp' => $voter->no_kp,
|
||||
'telefon' => $voter->telefon,
|
||||
'otp_expires_at' => $expired_at->toDateTimeString(),
|
||||
'ip' => $request->ip(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
])
|
||||
->log('voter request tac');
|
||||
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'message' => 'generated SMS Token',
|
||||
'notel' => $voter->telefon,
|
||||
'nokp' => $voter->no_kp,
|
||||
];
|
||||
|
||||
if (app()->environment(['local', 'development'])) {
|
||||
$response['debug_otp'] = $sms_token;
|
||||
}
|
||||
|
||||
return response()->json($response);
|
||||
}
|
||||
|
||||
if($sort){
|
||||
sort($num);
|
||||
}
|
||||
return response()->json([
|
||||
'status' => 'failed',
|
||||
'message' => 'Does not found NO KP.',
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
activity()
|
||||
->withProperties([
|
||||
'no_kp' => (string) $request->input('no_kp'),
|
||||
'ip' => $request->ip(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
'error' => $e->getMessage(),
|
||||
])
|
||||
->log('voter request tac failed');
|
||||
|
||||
return implode('',$num);
|
||||
return response()->json([
|
||||
'status' => 'failed',
|
||||
'message' => 'Error: ' . $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function getRemainingTime($expired_at){
|
||||
public function checkExpirationTAC($nokp): array
|
||||
{
|
||||
$currentDateTime = Carbon::now();
|
||||
$expiredAt = Carbon::parse($expired_at);
|
||||
|
||||
$remainingSeconds = $currentDateTime->diffInSeconds($expiredAt);
|
||||
$expiretac = UserOTP::where('nokp', $nokp)
|
||||
->whereDate('created_at', '=', $currentDateTime)
|
||||
->whereDate('expired_at', '=', $currentDateTime)
|
||||
->whereTime('expired_at', '>=', $currentDateTime->toTimeString())
|
||||
->whereTime('created_at', '<=', $currentDateTime->toTimeString())
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
$remainingFormatted = gmdate('H:i:s', $remainingSeconds);
|
||||
if ($expiretac) {
|
||||
$remaining = $this->getRemainingTime($expiretac->expired_at);
|
||||
|
||||
return $remainingFormatted;
|
||||
}
|
||||
return ['notexpired' => true, 'remaining' => $remaining];
|
||||
}
|
||||
|
||||
return ['notexpired' => false];
|
||||
}
|
||||
|
||||
public function verifyTAC(Request $request)
|
||||
{
|
||||
$authenticateOTP = $this->getTACModel($request->no_kp, $request->token);
|
||||
|
||||
if (! $authenticateOTP['condition']) {
|
||||
activity()
|
||||
->withProperties([
|
||||
'no_kp' => (string) $request->input('no_kp'),
|
||||
'ip' => $request->ip(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
'reason' => $authenticateOTP['message'] ?? 'invalid',
|
||||
])
|
||||
->log('voter verify tac failed');
|
||||
|
||||
return response()->json([
|
||||
'status' => 'failed',
|
||||
'message' => $authenticateOTP['message'],
|
||||
]);
|
||||
}
|
||||
|
||||
if (Auth::guard('voter')->attempt(['no_kp' => $request->no_kp, 'password' => 'admin', 'election_id' => Util::getCurrentElection()])) {
|
||||
$user = Auth::guard('voter')->user();
|
||||
activity()
|
||||
->performedOn($user)
|
||||
->withProperties([
|
||||
'election_id' => Util::getCurrentElection(),
|
||||
'voter_id' => $user->id,
|
||||
'voter_name' => $user->name,
|
||||
'no_kp' => $user->no_kp,
|
||||
'ip' => $request->ip(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
])
|
||||
->log("voter login: {$user->name}");
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Login successfully.',
|
||||
'user' => Auth::guard('voter')->user(),
|
||||
'election_status' => Util::getElectionStatus(),
|
||||
'token' => Auth::guard('voter')->user()->createToken('My Token', ['vote'])->accessToken,
|
||||
]);
|
||||
}
|
||||
|
||||
activity()
|
||||
->withProperties([
|
||||
'no_kp' => (string) $request->input('no_kp'),
|
||||
'ip' => $request->ip(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
])
|
||||
->log('voter login failed');
|
||||
|
||||
return response()->json([
|
||||
'status' => 'failed',
|
||||
'message' => 'No. KP tidak wujud. Atau sesi mengundi telah tamat',
|
||||
]);
|
||||
}
|
||||
|
||||
private function getTACModel($no_kp, $token)
|
||||
{
|
||||
$currentDateTime = Carbon::now();
|
||||
|
||||
$result = UserOTP::where('nokp', $no_kp)
|
||||
->whereDate('created_at', '=', $currentDateTime)
|
||||
->whereDate('expired_at', '=', $currentDateTime)
|
||||
->whereTime('expired_at', '>=', $currentDateTime->toTimeString())
|
||||
->whereTime('created_at', '<=', $currentDateTime->toTimeString())
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
if (! $result) {
|
||||
return ['condition' => false, 'message' => 'Token Expired'];
|
||||
}
|
||||
|
||||
if ($result['token'] == $token) {
|
||||
return ['condition' => true];
|
||||
}
|
||||
|
||||
return ['condition' => false, 'message' => 'Invalid Token'];
|
||||
}
|
||||
|
||||
private function getTokenSMS($tele, $date)
|
||||
{
|
||||
$tac_token = $this->randum_number(6, 2, false);
|
||||
if (env('APP_ENV') == 'production') {
|
||||
$mobileno = '6' . $tele;
|
||||
$messages = sprintf(config('onewaysms.message'), $tac_token, $date);
|
||||
|
||||
$parameter = [
|
||||
'apiusername' => env('ONEWAY_SMS_USERNAME'),
|
||||
'apipassword' => env('ONEWAY_SMS_PASSWORD'),
|
||||
'senderid' => env('ONEWAY_SMS_SENDERID', 'INFO'),
|
||||
'mobileno' => $mobileno,
|
||||
'message' => $messages,
|
||||
'languagetype' => env('ONEWAY_SMS_LANG', 1),
|
||||
];
|
||||
|
||||
$client = new Client(['verify' => false]);
|
||||
$status = $client->get('http://gateway.onewaysms.com.my:10001/api.aspx', [
|
||||
'query' => $parameter,
|
||||
]);
|
||||
|
||||
if ($status) {
|
||||
return $tac_token;
|
||||
}
|
||||
|
||||
return 'error';
|
||||
}
|
||||
|
||||
return $tac_token;
|
||||
}
|
||||
|
||||
private function randum_number($len = 6, $dup = 1, $sort = false)
|
||||
{
|
||||
if ($dup < 1) {
|
||||
throw new \InvalidArgumentException('Second argument is < 1');
|
||||
}
|
||||
|
||||
$num = range(0, 9);
|
||||
shuffle($num);
|
||||
|
||||
$num = array_slice($num, 0, ($len - $dup) + 1);
|
||||
|
||||
if ($dup > 0) {
|
||||
$k = array_rand($num, 1);
|
||||
for ($i = 0; $i < ($dup - 1); $i++) {
|
||||
$num[] = $num[$k];
|
||||
}
|
||||
}
|
||||
|
||||
if ($sort) {
|
||||
sort($num);
|
||||
}
|
||||
|
||||
return implode('', $num);
|
||||
}
|
||||
|
||||
private function getRemainingTime($expired_at)
|
||||
{
|
||||
$currentDateTime = Carbon::now();
|
||||
$expiredAt = Carbon::parse($expired_at);
|
||||
|
||||
$remainingSeconds = $currentDateTime->diffInSeconds($expiredAt);
|
||||
|
||||
return gmdate('H:i:s', $remainingSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API\v1\Voter;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Util;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LogoutController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
if ($user) {
|
||||
activity()
|
||||
->performedOn($user)
|
||||
->withProperties([
|
||||
'election_id' => Util::getCurrentElection(),
|
||||
'voter_id' => $user->id,
|
||||
'voter_name' => $user->name,
|
||||
'no_kp' => $user->no_kp,
|
||||
'ip' => $request->ip(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
])
|
||||
->log("voter logout: {$user->name}");
|
||||
|
||||
// Revoke current access token if using Passport.
|
||||
try {
|
||||
if (method_exists($user, 'token') && $user->token()) {
|
||||
$user->token()->revoke();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Logging should not block logout.
|
||||
}
|
||||
} else {
|
||||
activity()
|
||||
->withProperties([
|
||||
'ip' => $request->ip(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
])
|
||||
->log('voter logout');
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Logout successfully',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API\v1\Voter;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Util;
|
||||
use App\Voter;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Applies admin selection: voters ticked exist for current election (copied from latest row per no_kp);
|
||||
* unticked are removed from the current election only.
|
||||
*/
|
||||
class SyncApplicableVotersController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'selected_no_kp' => 'present|array',
|
||||
'selected_no_kp.*' => 'nullable|string|max:60',
|
||||
]);
|
||||
|
||||
$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 = null;
|
||||
$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.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,24 @@ class UpdateController extends Controller
|
||||
|
||||
private function updateVoter($request, $id)
|
||||
{
|
||||
$voter = $request->all();
|
||||
Voter::find($id)->update($voter);
|
||||
$voter = Voter::findOrFail($id);
|
||||
|
||||
$old = $voter->toArray();
|
||||
$voter->fill($request->all());
|
||||
$voter->save();
|
||||
|
||||
$dirtyKeys = array_keys($voter->getChanges());
|
||||
|
||||
activity()
|
||||
->performedOn($voter)
|
||||
->withProperties([
|
||||
'voter_id' => $voter->id,
|
||||
'voter_name' => $voter->name,
|
||||
'election_id' => Util::getCurrentElection(),
|
||||
'changed_keys' => $dirtyKeys,
|
||||
'ip' => $request->ip(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
])
|
||||
->log("update voter: {$voter->name}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API\v1\Voter;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Util;
|
||||
use App\Voter;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class VerifyFizikalRegistrationController extends Controller
|
||||
{
|
||||
/**
|
||||
* Admin confirms physical registration so the voter may vote (fizikal_registration_verified_at).
|
||||
*/
|
||||
public function __invoke(Request $request, $id)
|
||||
{
|
||||
$electionId = Util::getCurrentElection();
|
||||
|
||||
$voter = Voter::where('election_id', $electionId)
|
||||
->where('id', $id)
|
||||
->firstOrFail();
|
||||
|
||||
if ((int) $voter->kehadiran !== 1) {
|
||||
return response()->json([
|
||||
'status' => 'failed',
|
||||
'message' => 'Pengesahan pendaftaran fizikal hanya untuk pengundi Fizikal.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
if ($voter->fizikal_registration_verified_at !== null) {
|
||||
activity()
|
||||
->performedOn($voter)
|
||||
->withProperties([
|
||||
'election_id' => $electionId,
|
||||
'voter_id' => $voter->id,
|
||||
'voter_name' => $voter->name,
|
||||
'already_verified' => true,
|
||||
'verified_at' => (string) $voter->fizikal_registration_verified_at,
|
||||
'verified_by_admin_id' => $request->user() ? (int) $request->user()->id : null,
|
||||
'ip' => $request->ip(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
])
|
||||
->log("verify fizikal registration: {$voter->name}");
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Pendaftaran telah disahkan sebelum ini.',
|
||||
'voter' => $voter,
|
||||
]);
|
||||
}
|
||||
|
||||
$voter->fizikal_registration_verified_at = now();
|
||||
$voter->save();
|
||||
|
||||
activity()
|
||||
->performedOn($voter)
|
||||
->withProperties([
|
||||
'election_id' => $electionId,
|
||||
'voter_id' => $voter->id,
|
||||
'voter_name' => $voter->name,
|
||||
'already_verified' => false,
|
||||
'verified_at' => (string) $voter->fizikal_registration_verified_at,
|
||||
'verified_by_admin_id' => $request->user() ? (int) $request->user()->id : null,
|
||||
'ip' => $request->ip(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
])
|
||||
->log("verify fizikal registration: {$voter->name}");
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Pendaftaran fizikal telah disahkan.',
|
||||
'voter' => $voter->fresh(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user