390 lines
15 KiB
PHP
390 lines
15 KiB
PHP
<?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();
|
|
}
|
|
}
|