validate([ 'purpose' => ['nullable', 'in:free_allocation,store_inventory,promotional'], 'target_area_id' => ['nullable', 'integer'], 'q' => ['nullable', 'string', 'max:100'], 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], ]); $perPage = (int) $request->input('per_page', 25); $batches = QrCodeBatch::query() ->with(['targetArea', 'createdBy']) ->withCount('codes') ->when($request->filled('purpose'), fn ($q) => $q->where('purpose', $request->string('purpose'))) ->when( $request->filled('target_area_id'), fn ($q) => $q->where('target_area_id', $request->integer('target_area_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(); $area = isset($data['target_area_id']) ? ServiceArea::where('uuid', $data['target_area_id'])->first() : null; $batch = $generator->generate( quantity: (int) $data['quantity'], purpose: $data['purpose'], targetArea: $area, targetStoreId: $data['target_store_id'] ?? null, createdBy: $request->user(), expiresAt: isset($data['expires_at']) ? \Carbon\Carbon::parse($data['expires_at']) : null, notes: $data['notes'] ?? null, ); $batch->load(['targetArea', 'createdBy', 'codes']); return $this->created(new QrCodeBatchResource($batch), 'Batch generated'); } public function show(QrCodeBatch $qrCodeBatch): JsonResponse { $qrCodeBatch->load(['targetArea', '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 = \App\Models\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 { $result = (new Builder( writer: new PngWriter(), data: $serial, errorCorrectionLevel: ErrorCorrectionLevel::Medium, size: 140, margin: 4, ))->build(); return $result->getDataUri(); } private function barcodeDataUri(string $serial): string { $generator = new BarcodeGeneratorPNG(); $png = $generator->getBarcode($serial, BarcodeGeneratorPNG::TYPE_CODE_128, 1, 28); return 'data:image/png;base64,'.base64_encode($png); } }