132 lines
4.4 KiB
PHP
132 lines
4.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\API\v1\Allowance;
|
|
|
|
use App\AllowanceClaimCode;
|
|
use App\AllowancePayout;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Controllers\Util;
|
|
use App\Result;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Hash;
|
|
|
|
class ClaimCodeController extends Controller
|
|
{
|
|
public function __invoke(Request $request)
|
|
{
|
|
$this->validate($request, [
|
|
'method' => 'nullable|string|in:cash,bank',
|
|
]);
|
|
|
|
$electionId = Util::getCurrentElection();
|
|
$method = $request->input('method', 'cash');
|
|
|
|
// voter auth guard; Auth::id() should be voter id under voterAPI
|
|
$voterId = Auth::id();
|
|
|
|
$voter = $request->user();
|
|
if (!$voter || (int) $voter->id !== (int) $voterId) {
|
|
return response()->json([
|
|
'status' => 'failed',
|
|
'message' => 'Unauthenticated.',
|
|
], 401);
|
|
}
|
|
|
|
// Enforce method by attendance
|
|
if ($method === 'cash' && (int) $voter->kehadiran !== 1) {
|
|
return response()->json([
|
|
'status' => 'failed',
|
|
'message' => 'Kod tuntutan tunai hanya untuk kehadiran Fizikal.',
|
|
], 422);
|
|
}
|
|
if ($method === 'bank' && (int) $voter->kehadiran !== 2) {
|
|
return response()->json([
|
|
'status' => 'failed',
|
|
'message' => 'Kod tuntutan bank hanya untuk kehadiran Maya.',
|
|
], 422);
|
|
}
|
|
|
|
$hasVoted = Result::where('election_id', $electionId)->where('voter_id', $voterId)->exists();
|
|
|
|
// Tunai: normally must have voted; exception — fizikal hadir & layak mesyuarat tetapi tidak dibenarkan
|
|
// mengundi kerana saham < RM500 (same rule as voter UI).
|
|
$kehadiran = (int) $voter->kehadiran;
|
|
$saham = (float) $voter->saham;
|
|
$canVoteBySaham = $saham >= 500.0;
|
|
$canProceedToVote = $kehadiran !== 0
|
|
&& ($kehadiran !== 1 || $voter->fizikal_registration_verified_at !== null);
|
|
$eligibleClaimCashWithoutVote = $method === 'cash'
|
|
&& $kehadiran === 1
|
|
&& $canProceedToVote
|
|
&& !$canVoteBySaham;
|
|
|
|
if (!$hasVoted && !$eligibleClaimCashWithoutVote) {
|
|
return response()->json([
|
|
'status' => 'failed',
|
|
'message' => 'Sila undi terlebih dahulu sebelum menuntut elaun.',
|
|
], 422);
|
|
}
|
|
|
|
// If already paid, don't generate codes
|
|
$alreadyPaid = AllowancePayout::where('election_id', $electionId)
|
|
->where('voter_id', $voterId)
|
|
->where('method', $method)
|
|
->where('status', 'paid')
|
|
->exists();
|
|
|
|
if ($alreadyPaid) {
|
|
return response()->json([
|
|
'status' => 'failed',
|
|
'message' => 'Elaun anda telah direkodkan sebagai sudah dibayar.',
|
|
], 422);
|
|
}
|
|
|
|
// Generate a 6-digit numeric code
|
|
$codeInt = random_int(0, 999999);
|
|
$code = str_pad((string) $codeInt, 6, '0', STR_PAD_LEFT);
|
|
|
|
$expiresAt = Carbon::now()->addHours(6);
|
|
|
|
$row = AllowanceClaimCode::updateOrCreate(
|
|
[
|
|
'election_id' => $electionId,
|
|
'voter_id' => $voterId,
|
|
'method' => $method,
|
|
],
|
|
[
|
|
'code_hash' => Hash::make($code),
|
|
'code_last4' => substr($code, -4),
|
|
'expires_at' => $expiresAt,
|
|
'used_at' => null,
|
|
'used_by_admin_id' => null,
|
|
]
|
|
);
|
|
|
|
activity()
|
|
->performedOn($row)
|
|
->withProperties([
|
|
'election_id' => $electionId,
|
|
'voter_id' => (int) $voterId,
|
|
'voter_name' => $voter->name,
|
|
'attendance' => (int) $voter->kehadiran,
|
|
'method' => $method,
|
|
'expires_at' => $expiresAt->toDateTimeString(),
|
|
'code_last4' => $row->code_last4,
|
|
'ip' => $request->ip(),
|
|
'user_agent' => $request->userAgent(),
|
|
])
|
|
->log("generate claim code ({$method}): {$voter->name}");
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'message' => 'Kod tuntutan berjaya dijana.',
|
|
'code' => $code, // return plaintext ONCE to voter
|
|
'expires_at' => $expiresAt,
|
|
'method' => $method,
|
|
]);
|
|
}
|
|
}
|
|
|