345 lines
11 KiB
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;
|
|
}
|
|
}
|