157 lines
5.3 KiB
PHP
157 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\V1\Admin;
|
|
|
|
use App\Events\HouseholdVerified;
|
|
use App\Http\Controllers\Api\V1\ApiController;
|
|
use App\Models\Household;
|
|
use App\Models\QrCode;
|
|
use App\Models\User;
|
|
use App\States\QrCode\Voided;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class BulkActionController extends ApiController
|
|
{
|
|
/**
|
|
* Approve N pending households in one shot. Per-id outcome reported
|
|
* back so the UI can show what worked and what didn't.
|
|
*/
|
|
public function approveHouseholds(Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'household_ids' => ['required', 'array', 'min:1', 'max:200'],
|
|
'household_ids.*' => ['string', 'exists:households,uuid'],
|
|
]);
|
|
|
|
$admin = $request->user();
|
|
$results = [];
|
|
|
|
foreach ($data['household_ids'] as $uuid) {
|
|
$h = Household::where('uuid', $uuid)->first();
|
|
if (! $h) {
|
|
$results[] = ['id' => $uuid, 'ok' => false, 'reason' => 'not_found'];
|
|
|
|
continue;
|
|
}
|
|
if ($h->verification_status === Household::VERIFICATION_APPROVED) {
|
|
$results[] = ['id' => $uuid, 'ok' => false, 'reason' => 'already_approved'];
|
|
|
|
continue;
|
|
}
|
|
if (! $h->proof_of_residency_path) {
|
|
$results[] = ['id' => $uuid, 'ok' => false, 'reason' => 'no_proof'];
|
|
|
|
continue;
|
|
}
|
|
|
|
DB::transaction(function () use ($h, $admin) {
|
|
$h->markVerified($admin);
|
|
});
|
|
HouseholdVerified::dispatch($h->fresh(), $admin);
|
|
|
|
$results[] = ['id' => $uuid, 'ok' => true];
|
|
}
|
|
|
|
$okCount = collect($results)->where('ok', true)->count();
|
|
|
|
return $this->ok([
|
|
'approved' => $okCount,
|
|
'rejected' => count($results) - $okCount,
|
|
'results' => $results,
|
|
], "Approved {$okCount} of ".count($results).' households');
|
|
}
|
|
|
|
public function suspendUsers(Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'user_ids' => ['required', 'array', 'min:1', 'max:200'],
|
|
'user_ids.*' => ['string', 'exists:users,uuid'],
|
|
]);
|
|
|
|
$results = [];
|
|
foreach ($data['user_ids'] as $uuid) {
|
|
$u = User::where('uuid', $uuid)->first();
|
|
if (! $u) {
|
|
$results[] = ['id' => $uuid, 'ok' => false, 'reason' => 'not_found'];
|
|
|
|
continue;
|
|
}
|
|
if ($u->role === User::ROLE_ADMIN) {
|
|
$results[] = ['id' => $uuid, 'ok' => false, 'reason' => 'admin_protected'];
|
|
|
|
continue;
|
|
}
|
|
$u->forceFill(['status' => User::STATUS_SUSPENDED])->save();
|
|
$results[] = ['id' => $uuid, 'ok' => true];
|
|
}
|
|
|
|
$okCount = collect($results)->where('ok', true)->count();
|
|
|
|
return $this->ok(['suspended' => $okCount, 'results' => $results], "Suspended {$okCount} users");
|
|
}
|
|
|
|
public function activateUsers(Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'user_ids' => ['required', 'array', 'min:1', 'max:200'],
|
|
'user_ids.*' => ['string', 'exists:users,uuid'],
|
|
]);
|
|
|
|
$results = [];
|
|
foreach ($data['user_ids'] as $uuid) {
|
|
$u = User::where('uuid', $uuid)->first();
|
|
if (! $u) {
|
|
$results[] = ['id' => $uuid, 'ok' => false, 'reason' => 'not_found'];
|
|
|
|
continue;
|
|
}
|
|
$u->forceFill(['status' => User::STATUS_ACTIVE])->save();
|
|
$results[] = ['id' => $uuid, 'ok' => true];
|
|
}
|
|
|
|
$okCount = collect($results)->where('ok', true)->count();
|
|
|
|
return $this->ok(['activated' => $okCount, 'results' => $results], "Activated {$okCount} users");
|
|
}
|
|
|
|
public function voidQrCodes(Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'serials' => ['required', 'array', 'min:1', 'max:500'],
|
|
'serials.*' => ['string'],
|
|
'reason' => ['required', 'string', 'min:3', 'max:500'],
|
|
]);
|
|
|
|
$admin = $request->user();
|
|
$results = [];
|
|
foreach ($data['serials'] as $serial) {
|
|
$code = QrCode::where('serial', $serial)->first();
|
|
if (! $code) {
|
|
$results[] = ['serial' => $serial, 'ok' => false, 'reason' => 'not_found'];
|
|
|
|
continue;
|
|
}
|
|
if (! $code->status->canTransitionTo(Voided::class)) {
|
|
$results[] = ['serial' => $serial, 'ok' => false, 'reason' => 'cannot_void_from_'.(string) $code->status];
|
|
|
|
continue;
|
|
}
|
|
$code->status->transitionTo(Voided::class);
|
|
$code->forceFill([
|
|
'metadata' => array_merge($code->metadata ?? [], [
|
|
'voided_at' => now()->toIso8601String(),
|
|
'voided_by_admin_id' => $admin->id,
|
|
'void_reason' => $data['reason'],
|
|
]),
|
|
])->save();
|
|
$results[] = ['serial' => $serial, 'ok' => true];
|
|
}
|
|
|
|
$okCount = collect($results)->where('ok', true)->count();
|
|
|
|
return $this->ok(['voided' => $okCount, 'results' => $results], "Voided {$okCount} codes");
|
|
}
|
|
}
|