stabile; // Dati preesistenti o storicizzati in cache $service = new SmsMachineService(); return view('anagrafica.aggiornamento', [ 'unita' => $unita, 'stabile' => $stabile, 'token' => $token, 'soggetto' => $unita->proprietari()->first() ?? null ]); } catch (Exception $e) { abort(403, 'Link di aggiornamento non valido o scaduto.'); } } /** * Invia il codice OTP tramite l'SMS Gateway locale. */ public function inviaOtp(Request $request) { $request->validate([ 'token' => 'required|string', 'cellulare' => 'required|string|min:9', ]); try { $decrypted = Crypt::decryptString($request->token); $payload = json_decode($decrypted, true); $otp = strval(rand(100000, 999999)); // Storicizza l'OTP in sessione per verifica session([ 'anagrafe_otp_' . $payload['unita_id'] => [ 'code' => $otp, 'cellulare' => $request->cellulare, 'expires_at' => now()->addMinutes(10)->timestamp ] ]); // Invia tramite SmsMachineService $smsService = new SmsMachineService(); $success = $smsService->sendOtp($request->cellulare, $otp); if ($success) { return response()->json(['status' => 'success', 'message' => 'Codice OTP inviato correttamente via SMS.']); } return response()->json(['status' => 'error', 'message' => 'Impossibile inviare l\'SMS al gateway locale.'], 500); } catch (Exception $e) { return response()->json(['status' => 'error', 'message' => 'Richiesta non autorizzata.'], 403); } } /** * Valida ed archivia l'aggiornamento anagrafico. */ public function store(Request $request) { $request->validate([ 'token' => 'required|string', 'otp' => 'required|string|size:6', 'nome' => 'required|string|max:100', 'cognome' => 'required|string|max:100', 'codice_fiscale' => 'required|string|size:16', 'email' => 'nullable|email|max:100', 'pec' => 'nullable|email|max:100', 'cellulare' => 'required|string|min:9', 'telefono' => 'nullable|string|max:30', 'indirizzo' => 'required|string|max:150', 'cap' => 'required|string|size:5', 'citta' => 'required|string|max:100', 'provincia' => 'required|string|size:2', // Dati Catastali integrati 'foglio' => 'nullable|string|max:10', 'particella' => 'nullable|string|max:10', 'subalterno' => 'nullable|string|max:10', ]); try { $decrypted = Crypt::decryptString($request->token); $payload = json_decode($decrypted, true); $unitaId = $payload['unita_id']; // Verifica OTP $savedOtp = session('anagrafe_otp_' . $unitaId); if (!$savedOtp || $savedOtp['code'] !== $request->otp || $savedOtp['expires_at'] < now()->timestamp) { return back()->withErrors(['otp' => 'Codice OTP errato, non corrispondente o scaduto.'])->withInput(); } // Pulisce la sessione dell'OTP session()->forget('anagrafe_otp_' . $unitaId); $unita = UnitaImmobiliare::findOrFail($unitaId); $service = new \App\Services\SisterCatastoService(); $dirPath = $service->getStoragePath($unita->stabile_id) . "/anno_2026"; Storage::disk('public')->makeDirectory($dirPath); $consolidatedData = [ 'unita_id' => $unitaId, 'soggetto' => [ 'nome' => $request->nome, 'cognome' => $request->cognome, 'codice_fiscale' => strtoupper($request->codice_fiscale), 'email' => $request->email, 'pec' => $request->pec, 'cellulare' => $request->cellulare, 'telefono' => $request->telefono, 'residenza' => [ 'indirizzo' => $request->indirizzo, 'cap' => $request->cap, 'citta' => $request->citta, 'provincia' => strtoupper($request->provincia), ] ], 'catasto' => [ 'foglio' => $request->foglio, 'particella' => $request->particella, 'subalterno' => $request->subalterno, ], 'submitted_at' => now()->toDateTimeString(), 'ip_address' => $request->ip() ]; Storage::disk('public')->put("{$dirPath}/anagrafe_validata_unita_{$unitaId}.json", json_encode($consolidatedData, JSON_PRETTY_PRINT)); return view('anagrafica.success', [ 'unita' => $unita ]); } catch (Exception $e) { return back()->withErrors(['error' => 'Si รจ verificato un errore durante l\'archiviazione: ' . $e->getMessage()])->withInput(); } } /** * Esporta la scheda anagrafica precompilata in formato PDF. */ public function exportPdf(Request $request, string $token) { try { $decrypted = \Illuminate\Support\Facades\Crypt::decryptString($token); $payload = json_decode($decrypted, true); $unitaId = $payload['unita_id']; $unita = UnitaImmobiliare::findOrFail($unitaId); $stabile = $unita->stabile; $isVuoto = (bool) $request->query('vuoto'); $comproprietari = []; if (!$isVuoto) { if (\Schema::hasTable('proprieta')) { $comproprietari = \DB::table('proprieta') ->join('anagrafiche', 'anagrafiche.id', '=', 'proprieta.anagrafica_id') ->where('proprieta.unita_immobiliare_id', $unitaId) ->select('anagrafiche.cognome', 'anagrafiche.nome', 'anagrafiche.codice_fiscale', 'proprieta.percentuale_possesso as quota', 'proprieta.tipo_diritto') ->get() ->toArray(); } } $service = new \App\Services\SisterCatastoService(); $overlayPath = $service->getStoragePath($unita->stabile_id) . "/anno_2026/anagrafe_validata_unita_{$unitaId}.json"; $soggetto = null; $catasto = [ 'foglio' => \Illuminate\Support\Facades\Schema::hasColumn('unita_immobiliari', 'foglio') ? $unita->foglio : '', 'particella' => \Illuminate\Support\Facades\Schema::hasColumn('unita_immobiliari', 'particella') ? $unita->particella : '', 'subalterno' => \Illuminate\Support\Facades\Schema::hasColumn('unita_immobiliari', 'subalterno') ? $unita->subalterno : '', 'rendita' => \Illuminate\Support\Facades\Schema::hasColumn('unita_immobiliari', 'rendita_catastale') ? $unita->rendita_catastale : '', ]; if (!$isVuoto) { if (Storage::disk('public')->exists($overlayPath)) { $overlay = json_decode(Storage::disk('public')->get($overlayPath), true); if ($overlay) { $soggetto = $overlay['soggetto'] ?? null; $catasto['foglio'] = $overlay['catasto']['foglio'] ?? $catasto['foglio']; $catasto['particella'] = $overlay['catasto']['particella'] ?? $catasto['particella']; $catasto['subalterno'] = $overlay['catasto']['subalterno'] ?? $catasto['subalterno']; } } if (!$soggetto) { // Preleva da database locale $proprietari = []; if (\Schema::hasTable('unita_anagrafica_periodo')) { $proprietari = \DB::table('unita_anagrafica_periodo') ->join('anagrafiche', 'anagrafiche.id', '=', 'unita_anagrafica_periodo.anagrafica_id') ->where('unita_anagrafica_periodo.unita_immobiliare_id', $unitaId) ->where('unita_anagrafica_periodo.ruolo_occupazione', 'condomino') ->select('anagrafiche.*') ->get(); } $proprietario = $proprietari->first() ?? null; $soggetto = [ 'nome' => $proprietario?->nome ?? '', 'cognome' => $proprietario?->cognome ?? '', 'codice_fiscale' => $proprietario?->codice_fiscale ?? '', 'email' => $proprietario?->email ?? '', 'pec' => $proprietario?->pec ?? '', 'cellulare' => $proprietario?->cellulare ?? '', 'telefono' => $proprietario?->telefono ?? '', 'residenza' => [ 'indirizzo' => $proprietario?->indirizzo ?? '', 'cap' => $proprietario?->cap ?? '', 'citta' => $proprietario?->citta ?? '', 'provincia' => $proprietario?->provincia ?? '', ] ]; } } else { $catasto = [ 'foglio' => '', 'particella' => '', 'subalterno' => '', 'rendita' => '', ]; } $pdfService = new \App\Services\AnagraficaPdfGeneratorService(); $pdfContent = $pdfService->generate([ 'unita' => $unita, 'stabile' => $stabile, 'soggetto' => $soggetto, 'catasto' => $catasto, 'comproprietari' => $comproprietari, 'is_vuoto' => $isVuoto, ]); $fileName = $isVuoto ? 'scheda_anagrafica_vuota_' . $unitaId . '.pdf' : 'scheda_anagrafica_unita_' . $unitaId . '.pdf'; return response($pdfContent, 200) ->header('Content-Type', 'application/pdf') ->header('Content-Disposition', 'attachment; filename="' . $fileName . '"'); } catch (Exception $e) { abort(500, 'Impossibile esportare la scheda anagrafica: ' . $e->getMessage()); } } }