persona_id ?? null; } $payload = $this->buildPersonaPayloadFromSoggetto($soggetto); $persona = $this->resolvePersonaMatch($payload, isset($soggetto->persona_id) ? (int)$soggetto->persona_id : null); if ($persona) { $personaId = (int)$persona->id; $this->fillPersonaMissingFields($persona, $payload); $this->syncEmailMultiple($personaId, $payload); } else { $personaId = $this->createPersonaFromPayload($payload); if ($personaId) { $this->syncEmailMultiple($personaId, $payload); } } if ($personaId && ((int)($soggetto->persona_id ?? 0) !== $personaId)) { DB::table('soggetti')->where('id', $soggetto->id)->update([ 'persona_id' => $personaId, 'updated_at' => now(), ]); } return $personaId; } private function buildPersonaPayloadFromSoggetto(object $soggetto): array { $ragioneSociale = $this->sanitizeString($soggetto->ragione_sociale ?? null, 255); $nome = $this->sanitizeString($soggetto->nome ?? null, 100); $cognome = $this->sanitizeString($soggetto->cognome ?? null, 100); if ($ragioneSociale && !$cognome) { $cognome = $ragioneSociale; } if (!$nome) { $nome = $ragioneSociale ? 'Impresa' : ($cognome ?: 'N/D'); } if (!$cognome) { $cognome = $nome ?: 'N/D'; } $tipologia = $ragioneSociale ? 'giuridica' : 'fisica'; $cf = $this->sanitizeIdentifier($soggetto->codice_fiscale ?? null); $partitaIva = $this->sanitizeIdentifier($soggetto->partita_iva ?? null); $email = $this->sanitizeEmail($soggetto->email ?? null); $telefono = $this->normalizePhone($soggetto->telefono ?? null); $indirizzo = $this->sanitizeString($soggetto->indirizzo ?? null, 255); $note = $this->sanitizeString($soggetto->note ?? null, 500); return [ 'soggetto_id' => $soggetto->id, 'tipologia' => $tipologia ?: 'fisica', 'nome' => $nome, 'cognome' => $cognome, 'ragione_sociale' => $ragioneSociale, 'codice_fiscale' => $cf, 'partita_iva' => $partitaIva, 'email_principale' => $email, 'telefono_principale' => $telefono, 'residenza_via' => $indirizzo, 'note' => $note, 'attivo' => (bool)($soggetto->attivo ?? true), 'name_key' => $this->normalizeNameKey($nome, $cognome), ]; } private function resolvePersonaMatch(array $payload, ?int $preferredId = null): ?object { if (!Schema::hasTable('persone')) { return null; } if ($preferredId) { $existing = DB::table('persone')->where('id', $preferredId)->first(); if ($existing) { return $existing; } } // Ordine richiesto: CF -> P.IVA -> telefono -> email $cf = $payload['codice_fiscale'] ?? null; if ($cf) { $found = DB::table('persone')->where('codice_fiscale', $cf)->orderBy('id')->first(); if ($found) { return $found; } } $piva = $payload['partita_iva'] ?? null; if ($piva) { $found = DB::table('persone') ->where(function ($q) use ($piva) { $q->where('partita_iva', $piva)->orWhere('partita_iva_societa', $piva); }) ->orderBy('id') ->first(); if ($found) { return $found; } } $telefono = $payload['telefono_principale'] ?? null; if ($telefono) { $found = DB::table('persone')->where('telefono_principale', $telefono)->orderBy('id')->first(); if ($found) { return $found; } } $email = $payload['email_principale'] ?? null; if ($email) { $found = DB::table('persone')->where('email_principale', $email)->orderBy('id')->first(); if ($found) { return $found; } if (Schema::hasTable('persone_email_multiple')) { $found = DB::table('persone_email_multiple as pem') ->join('persone as p', 'p.id', '=', 'pem.persona_id') ->where('pem.email', $email) ->orderBy('p.id') ->select('p.*') ->first(); if ($found) { return $found; } } } $nameKey = $payload['name_key'] ?? null; if ($nameKey) { $found = DB::table('persone') ->whereRaw("UPPER(CONCAT(TRIM(cognome),' ',TRIM(nome))) = ?", [$nameKey]) ->first(); if ($found) { return $found; } } return null; } private function fillPersonaMissingFields(object $persona, array $payload): void { $updates = []; $fields = [ 'cognome', 'nome', 'ragione_sociale', 'codice_fiscale', 'partita_iva', 'residenza_via', 'telefono_principale', 'email_principale', 'note', ]; foreach ($fields as $field) { $value = $payload[$field] ?? null; if (!$value) { continue; } $current = $persona->$field ?? null; if (empty($current) || $this->isPlaceholderValue((string) $current)) { if ( $field === 'telefono_principale' && DB::table('persone') ->where('telefono_principale', $value) ->where('id', '!=', $persona->id) ->exists() ) { continue; } $updates[$field] = $value; } } if (!empty($payload['tipologia']) && $persona->tipologia !== $payload['tipologia'] && $persona->tipologia === 'fisica') { $updates['tipologia'] = $payload['tipologia']; } if (!empty($updates)) { $updates['updated_at'] = now(); DB::table('persone')->where('id', $persona->id)->update($updates); } } private function syncEmailMultiple(int $personaId, array $payload): void { if (!Schema::hasTable('persone') || !Schema::hasTable('persone_email_multiple')) { return; } $email = $this->sanitizeEmail($payload['email_principale'] ?? null); if (!$email) { return; } $persona = DB::table('persone')->where('id', $personaId)->first(); if (!$persona) { return; } $principale = $this->sanitizeEmail($persona->email_principale ?? null); if (!$principale) { DB::table('persone')->where('id', $personaId)->update([ 'email_principale' => $email, 'updated_at' => now(), ]); return; } if ($principale === $email) { return; } $exists = DB::table('persone_email_multiple') ->where('persona_id', $personaId) ->where('email', $email) ->exists(); if (!$exists) { PersonaEmailMultipla::create([ 'persona_id' => $personaId, 'email' => $email, 'tipo_email' => 'secondaria', 'attiva' => true, ]); } } private function createPersonaFromPayload(array $payload): ?int { if (!$payload['nome'] || !$payload['cognome']) { return null; } $data = [ 'tipologia' => $payload['tipologia'] ?? 'fisica', 'cognome' => $payload['cognome'], 'nome' => $payload['nome'], 'ragione_sociale' => $payload['ragione_sociale'], 'codice_fiscale' => $payload['codice_fiscale'], 'partita_iva' => $payload['partita_iva'], 'residenza_via' => $payload['residenza_via'], 'telefono_principale' => $payload['telefono_principale'], 'email_principale' => $payload['email_principale'], 'note' => $payload['note'], 'attivo' => $payload['attivo'] ?? true, ]; if ($data['telefono_principale'] && DB::table('persone')->where('telefono_principale', $data['telefono_principale'])->exists()) { $data['telefono_principale'] = null; } try { $persona = Persona::create($data); return $persona->id ?? null; } catch (\Throwable $e) { Log::warning('PersonaSyncService: impossibile creare persona per soggetto #' . ($payload['soggetto_id'] ?? '?') . ' - ' . $e->getMessage()); return null; } } private function sanitizeString(?string $value, int $maxLength = 255): ?string { if ($value === null) { return null; } $normalized = trim((string)$value); if ($normalized === '') { return null; } return mb_substr($normalized, 0, $maxLength); } private function sanitizeIdentifier(?string $value): ?string { if ($value === null) { return null; } $normalized = strtoupper(trim($value)); $normalized = preg_replace('/[^A-Z0-9]/', '', $normalized); return $normalized === '' ? null : $normalized; } private function sanitizeEmail(?string $value): ?string { if ($value === null) { return null; } $normalized = strtolower(trim($value)); $normalized = preg_replace('/\s+/', '', $normalized); return $normalized === '' ? null : $normalized; } private function isPlaceholderValue(string $value): bool { $v = strtoupper(trim($value)); return in_array($v, ['ND', 'N/D', 'IMPRESA'], true); } private function normalizePhone(?string $value): ?string { if ($value === null) { return null; } $normalized = preg_replace('/[^\d+]/', '', (string)$value); return $normalized === '' ? null : $normalized; } private function normalizeNameKey(?string $nome, ?string $cognome): ?string { $full = trim(strtoupper(trim((string)($cognome ?? '')) . ' ' . trim((string)($nome ?? '')))); $full = preg_replace('/\s+/', ' ', $full); $full = preg_replace('/[^A-Z0-9 ]/', '', $full); $full = trim($full); return $full !== '' ? $full : null; } }