Files
Verde-Web/app/Http/Controllers/Api/V1/Admin/AdminQrBatchController.php

189 lines
6.2 KiB
PHP

<?php
namespace App\Http\Controllers\Api\V1\Admin;
use App\Http\Controllers\Api\V1\ApiController;
use App\Http\Requests\Qr\GenerateBatchRequest;
use App\Http\Resources\QrCodeBatchResource;
use App\Http\Resources\QrCodeResource;
use App\Models\Household;
use App\Models\QrCode;
use App\Models\QrCodeBatch;
use App\Models\ServiceArea;
use App\Services\Qr\BatchGenerator;
use Barryvdh\DomPDF\Facade\Pdf;
use Carbon\Carbon;
use Endroid\QrCode\Builder\Builder;
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\Writer\PngWriter;
use Endroid\QrCode\Writer\SvgWriter;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Picqer\Barcode\BarcodeGeneratorPNG;
use Picqer\Barcode\BarcodeGeneratorSVG;
use Symfony\Component\HttpFoundation\Response;
class AdminQrBatchController extends ApiController
{
public function index(Request $request): JsonResponse
{
$request->validate([
'purpose' => ['nullable', 'in:free_allocation,store_inventory,promotional'],
'household_id' => ['nullable', 'integer'],
'q' => ['nullable', 'string', 'max:100'],
'per_page' => ['nullable', 'integer', 'min:1', 'max:500'],
]);
$perPage = (int) $request->input('per_page', 25);
$batches = QrCodeBatch::query()
->with(['household.head', 'createdBy'])
->withCount('codes')
->when($request->filled('purpose'), fn ($q) => $q->where('purpose', $request->string('purpose')))
->when(
$request->filled('household_id'),
fn ($q) => $q->where('household_id', $request->integer('household_id')),
)
->when(
$request->filled('q'),
fn ($q) => $q->where('batch_number', 'like', '%'.$request->string('q').'%'),
)
->orderByDesc('id')
->paginate($perPage);
return $this->ok(
QrCodeBatchResource::collection($batches),
null,
[
'page' => $batches->currentPage(),
'per_page' => $batches->perPage(),
'total' => $batches->total(),
'last_page' => $batches->lastPage(),
],
);
}
public function store(GenerateBatchRequest $request, BatchGenerator $generator): JsonResponse
{
$data = $request->validated();
$household = Household::where('uuid', $data['household_id'])->firstOrFail();
$batch = $generator->generate(
quantity: (int) $data['quantity'],
purpose: $data['purpose'],
targetArea: null,
targetStoreId: $data['target_store_id'] ?? null,
createdBy: $request->user(),
expiresAt: isset($data['expires_at']) ? Carbon::parse($data['expires_at']) : null,
notes: $data['notes'] ?? null,
householdId: $household->id,
);
$batch->load(['household.head', 'createdBy', 'codes']);
return $this->created(new QrCodeBatchResource($batch), 'Batch generated');
}
public function show(QrCodeBatch $qrCodeBatch): JsonResponse
{
$qrCodeBatch->load(['household.head', 'createdBy', 'codes']);
return $this->ok(new QrCodeBatchResource($qrCodeBatch));
}
public function markPrinted(QrCodeBatch $qrCodeBatch): JsonResponse
{
if ($qrCodeBatch->printed_at) {
return $this->fail('Batch already marked as printed', null, 422);
}
$qrCodeBatch->forceFill(['printed_at' => now()])->save();
return $this->ok(
new QrCodeBatchResource($qrCodeBatch->fresh()->load(['targetArea', 'createdBy', 'codes'])),
'Batch marked as printed',
);
}
public function showCode(string $serial): JsonResponse
{
$code = QrCode::query()
->with(['batch.targetArea', 'household.head', 'usedAtDropOff', 'scannedBy'])
->where('serial', $serial)
->firstOrFail();
return $this->ok(new QrCodeResource($code));
}
public function printPdf(QrCodeBatch $qrCodeBatch): Response
{
$codes = $qrCodeBatch->codes()->orderBy('id')->get();
$rendered = $codes->map(function ($c) {
return [
'serial' => $c->serial,
'qr_data_uri' => $this->qrDataUri($c->serial),
'barcode_data_uri' => $this->barcodeDataUri($c->serial),
];
})->all();
$perPage = 24;
$pages = array_chunk($rendered, $perPage);
$pdf = Pdf::loadView('pdf.qr-batch', [
'batch' => $qrCodeBatch,
'pages' => $pages,
])->setPaper('a4');
$filename = "verde-{$qrCodeBatch->batch_number}.pdf";
return $pdf->download($filename);
}
private function qrDataUri(string $serial): string
{
try {
$result = (new Builder(
writer: new PngWriter,
data: $serial,
errorCorrectionLevel: ErrorCorrectionLevel::Medium,
size: 140,
margin: 4,
))->build();
return $result->getDataUri();
} catch (\Throwable $e) {
if (class_exists(SvgWriter::class)) {
$result = (new Builder(
writer: new SvgWriter,
data: $serial,
errorCorrectionLevel: ErrorCorrectionLevel::Medium,
size: 140,
margin: 4,
))->build();
return 'data:image/svg+xml;base64,'.base64_encode($result->getString());
}
throw $e;
}
}
private function barcodeDataUri(string $serial): string
{
try {
$generator = new BarcodeGeneratorPNG;
$png = $generator->getBarcode($serial, BarcodeGeneratorPNG::TYPE_CODE_128, 1, 28);
return 'data:image/png;base64,'.base64_encode($png);
} catch (\Throwable $e) {
if (class_exists(BarcodeGeneratorSVG::class)) {
$generator = new BarcodeGeneratorSVG;
$svg = $generator->getBarcode($serial, BarcodeGeneratorSVG::TYPE_CODE_128, 1, 28);
return 'data:image/svg+xml;base64,'.base64_encode($svg);
}
throw $e;
}
}
}