netgescon-day0/app/Http/Controllers/Condomino/AnagrafeController.php

390 lines
15 KiB
PHP
Executable File

<?php
namespace App\Http\Controllers\Condomino;
use App\Http\Controllers\Condomino\Concerns\ResolvesCondominoAccess;
use App\Http\Controllers\Controller;
use App\Models\Persona;
use App\Models\PersonaAudit;
use App\Models\PersonaEmailMultipla;
use App\Models\UnitaRecapitoServizio;
use App\Models\User;
use App\Services\Comunicazioni\RecapitiServizioResolver;
use Dompdf\Dompdf;
use Dompdf\Options;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\Response;
class AnagrafeController extends Controller
{
use ResolvesCondominoAccess;
public function __construct(private readonly RecapitiServizioResolver $resolver)
{
}
public function show(): View
{
/** @var User|null $user */
$user = Auth::user();
$unitaImmobiliari = $this->resolveUserUnita($user)->values();
$persona = $this->resolvePortalPersona($user, $unitaImmobiliari);
$serviceLabels = UnitaRecapitoServizio::serviceLabels();
return view('condomino.anagrafe.show', [
'persona' => $persona,
'unitaImmobiliari' => $unitaImmobiliari,
'serviceLabels' => $serviceLabels,
'emailRows' => $this->buildEmailRows($persona),
'serviceContactMatrix' => $this->buildServiceContactMatrix($persona, $unitaImmobiliari),
]);
}
public function update(Request $request): RedirectResponse
{
/** @var User|null $user */
$user = Auth::user();
$unitaImmobiliari = $this->resolveUserUnita($user)->values();
$persona = $this->resolvePortalPersona($user, $unitaImmobiliari);
if (! $persona instanceof Persona) {
return back()->withErrors([
'anagrafe' => 'Non e stato possibile collegare con certezza il tuo profilo a una scheda anagrafica digitale.',
])->withInput();
}
$validated = $request->validate([
'cognome' => 'nullable|string|max:100',
'nome' => 'nullable|string|max:100',
'codice_fiscale' => 'nullable|string|max:16',
'partita_iva' => 'nullable|string|max:16',
'data_nascita' => 'nullable|date',
'residenza_via' => 'nullable|string|max:255',
'telefono_principale' => 'nullable|string|max:50',
'email_principale' => 'nullable|email|max:255',
'email_pec' => 'nullable|email|max:255',
'whatsapp' => 'nullable|string|max:50',
'lingua_preferita' => 'nullable|string|max:10',
'modalita_comunicazione_preferita' => 'nullable|in:email,sms,whatsapp,telefono,posta',
'consenso_privacy' => 'nullable|boolean',
'note' => 'nullable|string|max:4000',
'email_multiple' => 'nullable|array',
'email_multiple.*.id' => 'nullable|integer',
'email_multiple.*.email' => 'nullable|email|max:255',
'email_multiple.*.tipo_email' => 'nullable|string|max:50',
'email_multiple.*.attiva' => 'nullable|boolean',
'service_contacts' => 'nullable|array',
]);
DB::transaction(function () use ($validated, $persona, $unitaImmobiliari): void {
$this->updatePersona($persona, $validated);
$this->syncAdditionalEmails($persona, (array) ($validated['email_multiple'] ?? []));
$this->syncServiceContacts($persona, $unitaImmobiliari, (array) ($validated['service_contacts'] ?? []));
});
return redirect()->route('condomino.anagrafe.show')
->with('success', 'Scheda anagrafica aggiornata. Le modifiche sono tracciate con data, utente e IP.');
}
public function downloadModulo(): Response
{
/** @var User|null $user */
$user = Auth::user();
$unitaImmobiliari = $this->resolveUserUnita($user)->values();
$persona = $this->resolvePortalPersona($user, $unitaImmobiliari);
$html = view('condomino.anagrafe.modulo-pdf', [
'persona' => $persona,
'utente' => $user,
'unitaImmobiliari' => $unitaImmobiliari,
'serviceLabels' => UnitaRecapitoServizio::serviceLabels(),
'emailRows' => $this->buildEmailRows($persona),
'serviceContactMatrix' => $this->buildServiceContactMatrix($persona, $unitaImmobiliari),
'generatedAt' => now(),
])->render();
$options = new Options();
$options->set('isRemoteEnabled', true);
$options->set('defaultFont', 'DejaVu Sans');
$dompdf = new Dompdf($options);
$dompdf->setPaper('A4', 'portrait');
$dompdf->loadHtml($html, 'UTF-8');
$dompdf->render();
return response($dompdf->output(), 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="modulo-anagrafe-condominiale.pdf"',
]);
}
private function updatePersona(Persona $persona, array $validated): void
{
$fields = [
'cognome',
'nome',
'codice_fiscale',
'partita_iva',
'data_nascita',
'residenza_via',
'telefono_principale',
'email_principale',
'email_pec',
'whatsapp',
'lingua_preferita',
'modalita_comunicazione_preferita',
'note',
];
foreach ($fields as $field) {
$newValue = $validated[$field] ?? null;
$oldValue = $persona->getAttribute($field);
if ((string) $oldValue === (string) $newValue) {
continue;
}
PersonaAudit::registraModifica($persona->id, $field, $oldValue, $newValue);
$persona->setAttribute($field, $newValue);
}
$consensoPrivacy = (bool) ($validated['consenso_privacy'] ?? false);
if ((bool) $persona->consenso_privacy !== $consensoPrivacy) {
PersonaAudit::registraModifica($persona->id, 'consenso_privacy', $persona->consenso_privacy ? '1' : '0', $consensoPrivacy ? '1' : '0');
$persona->consenso_privacy = $consensoPrivacy;
$persona->data_consenso_privacy = $consensoPrivacy ? now() : null;
}
$persona->save();
}
private function syncAdditionalEmails(Persona $persona, array $rows): void
{
$before = $persona->emailMultiple()
->orderBy('id')
->get(['email', 'tipo_email', 'attiva'])
->map(fn(PersonaEmailMultipla $row): array=> [
'email' => mb_strtolower(trim((string) $row->email)),
'tipo_email' => (string) $row->tipo_email,
'attiva' => (bool) $row->attiva,
])
->values()
->all();
$keepIds = [];
foreach ($rows as $row) {
$email = mb_strtolower(trim((string) ($row['email'] ?? '')));
if ($email === '') {
continue;
}
$attrs = [
'email' => $email,
'tipo_email' => trim((string) ($row['tipo_email'] ?? 'secondaria')) ?: 'secondaria',
'attiva' => (bool) ($row['attiva'] ?? true),
];
$id = (int) ($row['id'] ?? 0);
if ($id > 0) {
$existing = $persona->emailMultiple()->whereKey($id)->first();
if ($existing instanceof PersonaEmailMultipla) {
$existing->fill($attrs);
$existing->save();
$keepIds[] = (int) $existing->id;
continue;
}
}
$created = $persona->emailMultiple()->create($attrs);
$keepIds[] = (int) $created->id;
}
$deleteQuery = $persona->emailMultiple();
if ($keepIds !== []) {
$deleteQuery->whereNotIn('id', $keepIds);
}
$deleteQuery->delete();
$after = $persona->emailMultiple()
->orderBy('id')
->get(['email', 'tipo_email', 'attiva'])
->map(fn(PersonaEmailMultipla $row): array=> [
'email' => mb_strtolower(trim((string) $row->email)),
'tipo_email' => (string) $row->tipo_email,
'attiva' => (bool) $row->attiva,
])
->values()
->all();
if (json_encode($before) !== json_encode($after)) {
PersonaAudit::registraModifica($persona->id, 'email_multiple', json_encode($before), json_encode($after));
}
}
private function syncServiceContacts(Persona $persona, Collection $unitaImmobiliari, array $matrix): void
{
$allowedUnitIds = $unitaImmobiliari->pluck('id')->map(fn($id) => (int) $id)->all();
foreach ($allowedUnitIds as $unitaId) {
$serviceRows = (array) ($matrix[$unitaId] ?? []);
foreach (array_keys(UnitaRecapitoServizio::serviceLabels()) as $serviceType) {
$email = mb_strtolower(trim((string) ($serviceRows[$serviceType] ?? '')));
$query = UnitaRecapitoServizio::query()
->where('unita_id', $unitaId)
->where('persona_id', (int) $persona->id)
->where('tipo_servizio', $serviceType);
$before = $query->orderBy('id')->pluck('email')->map(fn($value) => mb_strtolower(trim((string) $value)))->values()->all();
if ($email === '') {
$query->delete();
$after = [];
} else {
$record = $query->orderBy('id')->first();
if (! $record instanceof UnitaRecapitoServizio) {
$record = new UnitaRecapitoServizio([
'unita_id' => $unitaId,
'persona_id' => (int) $persona->id,
'tipo_servizio' => $serviceType,
'ordine' => 1,
'is_default' => true,
'attivo' => true,
'sorgente' => 'portale_condomino',
]);
}
$record->email = $email;
$record->etichetta = UnitaRecapitoServizio::serviceLabels()[$serviceType];
$record->last_confirmed_at = now();
$record->save();
UnitaRecapitoServizio::query()
->where('unita_id', $unitaId)
->where('persona_id', (int) $persona->id)
->where('tipo_servizio', $serviceType)
->where('id', '!=', (int) $record->id)
->delete();
$after = [$email];
}
if (json_encode($before) !== json_encode($after)) {
PersonaAudit::registraModifica(
$persona->id,
'unita_recapito:' . $unitaId . ':' . $serviceType,
json_encode($before),
json_encode($after)
);
}
}
}
}
/**
* @return array<int, array<string, mixed>>
*/
private function buildEmailRows(?Persona $persona): array
{
$rows = [];
if ($persona instanceof Persona) {
$rows = $persona->emailMultiple()
->orderByDesc('attiva')
->orderBy('tipo_email')
->orderBy('email')
->get()
->map(fn(PersonaEmailMultipla $row): array=> [
'id' => (int) $row->id,
'email' => (string) $row->email,
'tipo_email' => (string) ($row->tipo_email ?: 'secondaria'),
'attiva' => (bool) $row->attiva,
])
->values()
->all();
}
while (count($rows) < 4) {
$rows[] = [
'id' => null,
'email' => '',
'tipo_email' => 'secondaria',
'attiva' => true,
];
}
return $rows;
}
/**
* @return array<int, array<string, array<string, mixed>>>
*/
private function buildServiceContactMatrix(?Persona $persona, Collection $unitaImmobiliari): array
{
$matrix = [];
$labels = UnitaRecapitoServizio::serviceLabels();
$storedRows = collect();
if ($persona instanceof Persona) {
$storedRows = UnitaRecapitoServizio::query()
->where('persona_id', (int) $persona->id)
->whereIn('unita_id', $unitaImmobiliari->pluck('id')->all())
->get()
->groupBy(['unita_id', 'tipo_servizio']);
}
foreach ($unitaImmobiliari as $unita) {
$unitRows = [];
foreach ($labels as $serviceType => $label) {
$stored = data_get($storedRows, $unita->id . '.' . $serviceType . '.0.email');
$resolved = $this->resolver->resolveForUnita($unita, $serviceType);
$unitRows[$serviceType] = [
'label' => $label,
'value' => (string) ($stored ?? ''),
'resolved' => $resolved,
];
}
$matrix[(int) $unita->id] = $unitRows;
}
return $matrix;
}
private function resolvePortalPersona(?User $user, Collection $unitaImmobiliari): ?Persona
{
if (! $user) {
return null;
}
$email = mb_strtolower(trim((string) $user->email));
if ($email === '') {
return null;
}
$unitIds = $unitaImmobiliari->pluck('id')->map(fn($id) => (int) $id)->all();
$baseQuery = Persona::query()->with(['emailMultiple', 'relazioniUnitaAttive.unitaImmobiliare.stabile']);
if ($unitIds !== []) {
$baseQuery->whereHas('relazioniUnitaAttive', fn($query) => $query->whereIn('unita_id', $unitIds));
}
$primaryMatch = (clone $baseQuery)
->whereRaw('LOWER(email_principale) = ?', [$email])
->first();
if ($primaryMatch instanceof Persona) {
return $primaryMatch;
}
return (clone $baseQuery)
->whereHas('emailMultiple', fn($query) => $query->whereRaw('LOWER(email) = ?', [$email])->where('attiva', true))
->first();
}
}