feat(backend): complete Module 7 — QR code system (core)
qr_code_batches + qr_codes tables. State machine via
spatie/laravel-model-states (unassigned → allocated → active → used,
plus expired/voided). Serial format PH-{area}-{YYMM}-{batch}-{code}-
{checksum} with 2-char SHA-256-derived checksum. BatchGenerator
bulk-inserts in chunks. QrAllocator allocates free codes to verified
households (prefers area-targeted batch, falls back to any). Real
HouseholdVerified listener replaces the placeholder. QrBalanceLow
event for Module 11.
Admin: batch CRUD, mark-printed, void code, lifecycle search, PDF
print sheet (24/A4 via dompdf + endroid/qr-code + picqer/barcode).
Resident: list own codes, balance with low_balance flag, activate
allocated codes.
Removed explicit Event::listen for HouseholdVerified — Laravel 11
auto-discovers it via handle() type-hint, and double-registering was
causing double-dispatch.
130 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
41
app/Http/Controllers/Api/V1/Admin/AdminQrCodeController.php
Normal file
41
app/Http/Controllers/Api/V1/Admin/AdminQrCodeController.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Admin;
|
||||
|
||||
use App\Http\Controllers\Api\V1\ApiController;
|
||||
use App\Http\Requests\Qr\VoidQrCodeRequest;
|
||||
use App\Http\Resources\QrCodeResource;
|
||||
use App\Models\QrCode;
|
||||
use App\States\QrCode\Voided;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class AdminQrCodeController extends ApiController
|
||||
{
|
||||
public function show(string $serial): JsonResponse
|
||||
{
|
||||
$code = QrCode::with(['batch.targetArea', 'household.head', 'usedAtDropOff', 'scannedBy'])
|
||||
->where('serial', $serial)
|
||||
->firstOrFail();
|
||||
|
||||
return $this->ok(new QrCodeResource($code));
|
||||
}
|
||||
|
||||
public function void(VoidQrCodeRequest $request, QrCode $qrCode): JsonResponse
|
||||
{
|
||||
if (! $qrCode->status->canTransitionTo(Voided::class)) {
|
||||
return $this->fail('Code cannot be voided from its current state', null, 422);
|
||||
}
|
||||
|
||||
$qrCode->status->transitionTo(Voided::class);
|
||||
$existingMeta = $qrCode->metadata ?? [];
|
||||
$qrCode->forceFill([
|
||||
'metadata' => array_merge($existingMeta, [
|
||||
'voided_at' => now()->toIso8601String(),
|
||||
'voided_by_admin_id' => $request->user()->id,
|
||||
'void_reason' => $request->validated('reason'),
|
||||
]),
|
||||
])->save();
|
||||
|
||||
return $this->ok(new QrCodeResource($qrCode->fresh()), 'Code voided');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user