netgescon-day0/app/Services/Anagrafiche/PersonaSyncService.php

345 lines
11 KiB
PHP

<?php
namespace App\Services\Anagrafiche;
use App\Models\Persona;
use App\Models\PersonaEmailMultipla;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
class PersonaSyncService
{
public function syncFromSoggettoRecord(?object $soggetto, bool $dryRun = false): ?int
{
if (!$soggetto || !Schema::hasTable('persone') || !Schema::hasColumn('soggetti', 'persona_id')) {
return null;
}
if ($dryRun) {
return $soggetto->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;
}
}