644 lines
27 KiB
PHP
Executable File
644 lines
27 KiB
PHP
Executable File
<?php
|
|
namespace App\Services\Consumi;
|
|
|
|
use App\Filament\Pages\Supporto\TicketAcquaGenerale;
|
|
use App\Models\Scadenza;
|
|
use App\Models\StabileContratto;
|
|
use App\Models\StabileServizio;
|
|
use App\Models\StabileServizioLettura;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Facades\URL;
|
|
|
|
class AcquaContractSyncService
|
|
{
|
|
/** @return array<string,int> */
|
|
public function normalizeStabile(int $stabileId, int $userId): array
|
|
{
|
|
$stats = [
|
|
'services' => 0,
|
|
'synced' => 0,
|
|
'contracts_before' => 0,
|
|
'contracts_after' => 0,
|
|
'merged' => 0,
|
|
];
|
|
|
|
if ($stabileId <= 0) {
|
|
return $stats;
|
|
}
|
|
|
|
$stats['contracts_before'] = StabileContratto::query()
|
|
->where('stabile_id', $stabileId)
|
|
->where('tipo_contratto', 'acqua')
|
|
->count();
|
|
|
|
$services = StabileServizio::query()
|
|
->with('fornitore:id,ragione_sociale,nome,cognome')
|
|
->where('stabile_id', $stabileId)
|
|
->where('tipo', 'acqua')
|
|
->orderByDesc('attivo')
|
|
->orderBy('id')
|
|
->get();
|
|
|
|
$stats['services'] = $services->count();
|
|
|
|
foreach ($services as $service) {
|
|
if ($this->syncForService($service, $userId) instanceof StabileContratto) {
|
|
$stats['synced']++;
|
|
}
|
|
}
|
|
|
|
$stats['contracts_after'] = StabileContratto::query()
|
|
->where('stabile_id', $stabileId)
|
|
->where('tipo_contratto', 'acqua')
|
|
->count();
|
|
|
|
$stats['merged'] = max(0, $stats['contracts_before'] - $stats['contracts_after']);
|
|
|
|
return $stats;
|
|
}
|
|
|
|
public function syncForService(StabileServizio $service, int $userId): ?StabileContratto
|
|
{
|
|
if ((string) ($service->tipo ?? '') !== 'acqua') {
|
|
return null;
|
|
}
|
|
|
|
$service->loadMissing('fornitore:id,ragione_sociale,nome,cognome');
|
|
|
|
$contracts = StabileContratto::query()
|
|
->where('stabile_id', (int) $service->stabile_id)
|
|
->where('tipo_contratto', 'acqua')
|
|
->orderByRaw("CASE WHEN stato = 'attivo' THEN 0 ELSE 1 END")
|
|
->orderByDesc('updated_at')
|
|
->orderByDesc('id')
|
|
->get();
|
|
|
|
$contract = $this->resolveCanonicalContract($service, $contracts);
|
|
if (! $contract instanceof StabileContratto) {
|
|
$contract = new StabileContratto([
|
|
'stabile_id' => (int) $service->stabile_id,
|
|
'tipo_contratto' => 'acqua',
|
|
]);
|
|
}
|
|
|
|
$serviceIdentifiers = $this->collectServiceIdentifiers($service);
|
|
$fornitoreLabel = trim((string) ($service->fornitore?->ragione_sociale ?: trim(($service->fornitore?->nome ?? '') . ' ' . ($service->fornitore?->cognome ?? ''))));
|
|
$title = trim('Contratto acqua' . ($fornitoreLabel !== '' ? ' - ' . $fornitoreLabel : ''));
|
|
|
|
$contract->fill([
|
|
'fornitore_id' => (int) ($service->fornitore_id ?? 0) ?: null,
|
|
'titolo' => $title !== '' ? $title : 'Contratto acqua',
|
|
'categoria_impianto' => 'acqua',
|
|
'servizio_label' => $service->nome,
|
|
'codice_contratto' => $this->nullableString($service->codice_contratto),
|
|
'riferimento_esterno' => $this->nullableString($service->codice_utenza ?: $service->codice_cliente),
|
|
'stato' => $contract->exists ? ($contract->stato ?: 'attivo'): 'attivo',
|
|
'decorrenza_dal' => $contract->decorrenza_dal ?: now()->startOfYear()->toDateString(),
|
|
'frequenza_scadenze' => $contract->frequenza_scadenze ?: 'annuale',
|
|
'giorni_preavviso' => $contract->giorni_preavviso ?: 30,
|
|
'rinnovo_automatico' => $contract->exists ? (bool) $contract->rinnovo_automatico : true,
|
|
'codici_collegati' => $this->buildLinkedCodes($service),
|
|
'dati_contratto' => array_merge(is_array($contract->dati_contratto) ? $contract->dati_contratto : [], [
|
|
'origine' => 'acqua_contract_sync',
|
|
'servizio_id' => (int) $service->id,
|
|
'last_synced_at' => now()->toIso8601String(),
|
|
'service_identifiers' => $serviceIdentifiers,
|
|
]),
|
|
'updated_by' => $userId,
|
|
]);
|
|
|
|
if (! $contract->exists) {
|
|
$contract->created_by = $userId;
|
|
$contract->valuta = 'EUR';
|
|
}
|
|
|
|
$contract->save();
|
|
|
|
$duplicates = $this->resolveDuplicateContracts($contracts, $service, $contract);
|
|
if ($duplicates !== []) {
|
|
$this->mergeDuplicateContracts($contract, $duplicates, $userId);
|
|
$contract->refresh();
|
|
}
|
|
|
|
$this->syncGeneratedScadenze($contract, $service);
|
|
|
|
return $contract;
|
|
}
|
|
|
|
/** @param \\Illuminate\\Support\\Collection<int,StabileContratto> $contracts */
|
|
private function resolveCanonicalContract(StabileServizio $service, $contracts): ?StabileContratto
|
|
{
|
|
$serviceIdentifiers = $this->collectServiceIdentifiers($service);
|
|
$serviceStrongIdentifiers = $this->collectServiceStrongIdentifiers($service);
|
|
$serviceWeakIdentifiers = $this->collectServiceWeakIdentifiers($service);
|
|
$serviceCount = StabileServizio::query()
|
|
->where('stabile_id', (int) $service->stabile_id)
|
|
->where('tipo', 'acqua')
|
|
->count();
|
|
|
|
$best = null;
|
|
$bestScore = 0;
|
|
|
|
foreach ($contracts as $contract) {
|
|
$score = 0;
|
|
$contractIdentifiers = $this->collectContractIdentifiers($contract);
|
|
$contractStrongIdentifiers = $this->collectContractStrongIdentifiers($contract);
|
|
$contractWeakIdentifiers = $this->collectContractWeakIdentifiers($contract);
|
|
$linkedServiceId = (int) data_get($contract->dati_contratto ?? [], 'servizio_id', 0);
|
|
|
|
if ($linkedServiceId > 0 && $linkedServiceId === (int) $service->id) {
|
|
$score += 250;
|
|
}
|
|
|
|
$serviceContract = $this->normalizeIdentifier((string) ($service->codice_contratto ?? ''));
|
|
$contractCode = $this->normalizeIdentifier((string) ($contract->codice_contratto ?? ''));
|
|
if ($serviceContract !== '' && $contractCode !== '' && $serviceContract === $contractCode) {
|
|
$score += 200;
|
|
}
|
|
|
|
$serviceReference = $this->normalizeIdentifier((string) ($service->codice_utenza ?: ''));
|
|
$contractReference = $this->normalizeIdentifier((string) ($contract->riferimento_esterno ?? ''));
|
|
if ($serviceReference !== '' && $contractReference !== '' && $serviceReference === $contractReference) {
|
|
$score += 90;
|
|
}
|
|
|
|
$overlap = array_intersect($serviceStrongIdentifiers, $contractStrongIdentifiers);
|
|
if ($overlap !== []) {
|
|
$score += 50 * count($overlap);
|
|
}
|
|
|
|
if ($serviceStrongIdentifiers === [] && $serviceCount === 1) {
|
|
$weakOverlap = array_intersect($serviceWeakIdentifiers, $contractWeakIdentifiers);
|
|
if ($weakOverlap !== []) {
|
|
$score += 20 * count($weakOverlap);
|
|
}
|
|
}
|
|
|
|
if ((string) ($contract->stato ?? '') === 'attivo') {
|
|
$score += 10;
|
|
}
|
|
|
|
if ((int) ($contract->fornitore_id ?? 0) > 0 && (int) ($contract->fornitore_id ?? 0) === (int) ($service->fornitore_id ?? 0)) {
|
|
$score += 5;
|
|
}
|
|
|
|
if ($score > $bestScore) {
|
|
$best = $contract;
|
|
$bestScore = $score;
|
|
}
|
|
}
|
|
|
|
if ($best instanceof StabileContratto && $bestScore >= 50) {
|
|
return $best;
|
|
}
|
|
|
|
if ($contracts->count() === 1 && $serviceCount === 1) {
|
|
return $contracts->first();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/** @param \\Illuminate\\Support\\Collection<int,StabileContratto> $contracts
|
|
* @return array<int,StabileContratto>
|
|
*/
|
|
private function resolveDuplicateContracts($contracts, StabileServizio $service, StabileContratto $canonical): array
|
|
{
|
|
$canonicalCode = $this->normalizeIdentifier((string) ($canonical->codice_contratto ?? ''));
|
|
$canonicalIdentifiers = $this->collectContractStrongIdentifiers($canonical);
|
|
$serviceIdentifiers = $this->collectServiceStrongIdentifiers($service);
|
|
|
|
return $contracts
|
|
->filter(function (StabileContratto $contract) use ($canonical, $canonicalCode, $canonicalIdentifiers, $serviceIdentifiers): bool {
|
|
if ((int) $contract->id === (int) $canonical->id) {
|
|
return false;
|
|
}
|
|
|
|
$contractCode = $this->normalizeIdentifier((string) ($contract->codice_contratto ?? ''));
|
|
if ($canonicalCode !== '' && $contractCode !== '' && $canonicalCode === $contractCode) {
|
|
return true;
|
|
}
|
|
|
|
$contractIdentifiers = $this->collectContractStrongIdentifiers($contract);
|
|
if ($canonicalIdentifiers !== [] && $contractIdentifiers !== [] && array_intersect($canonicalIdentifiers, $contractIdentifiers) !== []) {
|
|
return true;
|
|
}
|
|
|
|
return $serviceIdentifiers !== []
|
|
&& $contractIdentifiers !== []
|
|
&& array_intersect($serviceIdentifiers, $contractIdentifiers) !== [];
|
|
})
|
|
->values()
|
|
->all();
|
|
}
|
|
|
|
/** @param array<int,StabileContratto> $duplicates */
|
|
private function mergeDuplicateContracts(StabileContratto $canonical, array $duplicates, int $userId): void
|
|
{
|
|
$mergedIds = [];
|
|
$linkedCodes = is_array($canonical->codici_collegati) ? $canonical->codici_collegati : [];
|
|
$meta = is_array($canonical->dati_contratto) ? $canonical->dati_contratto : [];
|
|
$dirty = false;
|
|
|
|
foreach ($duplicates as $duplicate) {
|
|
$mergedIds[] = (int) $duplicate->id;
|
|
|
|
if (! $canonical->documento_principale_id && $duplicate->documento_principale_id) {
|
|
$canonical->documento_principale_id = $duplicate->documento_principale_id;
|
|
$dirty = true;
|
|
}
|
|
|
|
if (! $canonical->data_stipula && $duplicate->data_stipula) {
|
|
$canonical->data_stipula = $duplicate->data_stipula;
|
|
$dirty = true;
|
|
}
|
|
|
|
if (! $canonical->decorrenza_dal && $duplicate->decorrenza_dal) {
|
|
$canonical->decorrenza_dal = $duplicate->decorrenza_dal;
|
|
$dirty = true;
|
|
}
|
|
|
|
if (! $canonical->decorrenza_al && $duplicate->decorrenza_al) {
|
|
$canonical->decorrenza_al = $duplicate->decorrenza_al;
|
|
$dirty = true;
|
|
}
|
|
|
|
if (! filled($canonical->note) && filled($duplicate->note)) {
|
|
$canonical->note = $duplicate->note;
|
|
$dirty = true;
|
|
}
|
|
|
|
$linkedCodes = $this->mergeLinkedCodes($linkedCodes, is_array($duplicate->codici_collegati) ? $duplicate->codici_collegati : []);
|
|
|
|
Scadenza::query()
|
|
->where('stabile_contratto_id', (int) $duplicate->id)
|
|
->update(['stabile_contratto_id' => (int) $canonical->id]);
|
|
|
|
$duplicate->delete();
|
|
}
|
|
|
|
if ($mergedIds !== []) {
|
|
$meta['merged_from_contract_ids'] = array_values(array_unique(array_map('intval', array_merge(
|
|
array_map('intval', (array) ($meta['merged_from_contract_ids'] ?? [])),
|
|
$mergedIds,
|
|
))));
|
|
$meta['last_merged_at'] = now()->toIso8601String();
|
|
$meta['last_merged_by'] = $userId;
|
|
$canonical->codici_collegati = $linkedCodes;
|
|
$canonical->dati_contratto = $meta;
|
|
$canonical->updated_by = $userId;
|
|
$dirty = true;
|
|
}
|
|
|
|
if ($dirty) {
|
|
$canonical->save();
|
|
}
|
|
}
|
|
|
|
private function syncGeneratedScadenze(StabileContratto $contract, StabileServizio $service): void
|
|
{
|
|
Scadenza::query()
|
|
->where('stabile_contratto_id', (int) $contract->id)
|
|
->where(function ($query): void {
|
|
$query->where('natura', 'contratto_stabile')
|
|
->orWhere(function ($nested): void {
|
|
$nested->where('natura', 'acqua')
|
|
->where('legacy_payload->source', 'acqua_contract_sync');
|
|
});
|
|
})
|
|
->delete();
|
|
|
|
foreach ($this->buildContractScheduleDates($contract) as $date) {
|
|
$popupDal = $date->copy()->subDays(max(0, (int) ($contract->giorni_preavviso ?? 0)));
|
|
|
|
Scadenza::query()->create([
|
|
'stabile_id' => (int) $contract->stabile_id,
|
|
'stabile_contratto_id' => (int) $contract->id,
|
|
'descrizione_sintetica' => $contract->titolo,
|
|
'data_scadenza' => $date->toDateString(),
|
|
'natura' => 'contratto_stabile',
|
|
'natura_label' => 'Scadenza contratto',
|
|
'gestione_tipo' => $contract->tipo_contratto,
|
|
'anno' => $date->format('Y'),
|
|
'fatto' => 'N',
|
|
'tipo_riga' => trim((string) ($contract->servizio_label ?: $contract->tipo_contratto)),
|
|
'richiede_pop_up' => true,
|
|
'giorni_anticipo' => (int) ($contract->giorni_preavviso ?? 0),
|
|
'popup_dal' => $popupDal->toDateString(),
|
|
'legacy_payload' => [
|
|
'source' => 'acqua_contract_sync',
|
|
'schedule_type' => 'contract',
|
|
'codice_contratto' => $contract->codice_contratto,
|
|
'riferimento_esterno' => $contract->riferimento_esterno,
|
|
'codici_collegati' => $contract->codici_collegati ?? [],
|
|
],
|
|
]);
|
|
}
|
|
|
|
$this->syncGeneralReadingScadenze($contract, $service);
|
|
$this->syncUnitReadingScadenze($contract, $service);
|
|
}
|
|
|
|
private function syncGeneralReadingScadenze(StabileContratto $contract, StabileServizio $service): void
|
|
{
|
|
$rows = StabileServizioLettura::query()
|
|
->where('stabile_id', (int) $service->stabile_id)
|
|
->where('stabile_servizio_id', (int) $service->id)
|
|
->whereNull('unita_immobiliare_id')
|
|
->whereNull('lettura_fine')
|
|
->whereNotNull('deadline_lettura_at')
|
|
->orderBy('deadline_lettura_at')
|
|
->get(['id', 'stabile_id', 'deadline_lettura_at', 'riferimento_acquisizione', 'periodo_dal', 'periodo_al']);
|
|
|
|
foreach ($rows as $reading) {
|
|
$deadline = $reading->deadline_lettura_at instanceof Carbon
|
|
? $reading->deadline_lettura_at->copy()
|
|
: Carbon::parse((string) $reading->deadline_lettura_at);
|
|
|
|
Scadenza::query()->create([
|
|
'stabile_id' => (int) $service->stabile_id,
|
|
'stabile_contratto_id' => (int) $contract->id,
|
|
'descrizione_sintetica' => 'Lettura acqua generale ' . $this->serviceContractLabel($service),
|
|
'data_scadenza' => $deadline->toDateString(),
|
|
'natura' => 'acqua',
|
|
'natura_label' => 'Lettura acqua generale',
|
|
'gestione_tipo' => 'operativa',
|
|
'anno' => $deadline->format('Y'),
|
|
'fatto' => 'N',
|
|
'tipo_riga' => trim((string) ($service->nome ?: 'Acqua')),
|
|
'richiede_pop_up' => true,
|
|
'giorni_anticipo' => 2,
|
|
'popup_dal' => $deadline->copy()->subDays(2)->toDateString(),
|
|
'legacy_payload' => [
|
|
'source' => 'acqua_contract_sync',
|
|
'schedule_type' => 'general_reading',
|
|
'stabile_servizio_id' => (int) $service->id,
|
|
'reading_id' => (int) $reading->id,
|
|
'reading_reference' => $reading->riferimento_acquisizione,
|
|
'periodo_dal' => optional($reading->periodo_dal)->toDateString(),
|
|
'periodo_al' => optional($reading->periodo_al)->toDateString(),
|
|
'action_url' => TicketAcquaGenerale::getUrl([
|
|
'stabile' => (int) $service->stabile_id,
|
|
'servizio' => (int) $service->id,
|
|
], panel: 'admin-filament'),
|
|
],
|
|
]);
|
|
}
|
|
}
|
|
|
|
private function syncUnitReadingScadenze(StabileContratto $contract, StabileServizio $service): void
|
|
{
|
|
$rows = StabileServizioLettura::query()
|
|
->with('unitaImmobiliare:id,codice_unita,scala,interno')
|
|
->where('stabile_id', (int) $service->stabile_id)
|
|
->where('stabile_servizio_id', (int) $service->id)
|
|
->whereNotNull('unita_immobiliare_id')
|
|
->whereNull('lettura_fine')
|
|
->whereNotNull('deadline_lettura_at')
|
|
->orderBy('deadline_lettura_at')
|
|
->get(['id', 'stabile_id', 'stabile_servizio_id', 'unita_immobiliare_id', 'deadline_lettura_at', 'riferimento_acquisizione']);
|
|
|
|
foreach ($rows as $reading) {
|
|
$deadline = $reading->deadline_lettura_at instanceof Carbon
|
|
? $reading->deadline_lettura_at->copy()
|
|
: Carbon::parse((string) $reading->deadline_lettura_at);
|
|
|
|
$unitLabel = trim(implode(' ', array_filter([
|
|
trim((string) ($reading->unitaImmobiliare?->codice_unita ?? '')),
|
|
trim((string) ($reading->unitaImmobiliare?->interno ?? '')),
|
|
])));
|
|
|
|
Scadenza::query()->create([
|
|
'stabile_id' => (int) $service->stabile_id,
|
|
'stabile_contratto_id' => (int) $contract->id,
|
|
'descrizione_sintetica' => 'Autolettura acqua ' . ($unitLabel !== '' ? $unitLabel : ('UI #' . (int) $reading->unita_immobiliare_id)),
|
|
'data_scadenza' => $deadline->toDateString(),
|
|
'natura' => 'acqua',
|
|
'natura_label' => 'Autolettura acqua unità',
|
|
'gestione_tipo' => 'operativa',
|
|
'anno' => $deadline->format('Y'),
|
|
'fatto' => 'N',
|
|
'tipo_riga' => trim((string) ($service->nome ?: 'Acqua')),
|
|
'richiede_pop_up' => true,
|
|
'giorni_anticipo' => 3,
|
|
'popup_dal' => $deadline->copy()->subDays(3)->toDateString(),
|
|
'legacy_payload' => [
|
|
'source' => 'acqua_contract_sync',
|
|
'schedule_type' => 'unit_reading',
|
|
'stabile_servizio_id' => (int) $service->id,
|
|
'reading_id' => (int) $reading->id,
|
|
'unita_immobiliare_id' => (int) $reading->unita_immobiliare_id,
|
|
'reading_reference' => $reading->riferimento_acquisizione,
|
|
'action_url' => URL::temporarySignedRoute(
|
|
'public.water-reading.show',
|
|
now()->addDays(90),
|
|
[
|
|
'servizio' => (int) $service->id,
|
|
'unita' => (int) $reading->unita_immobiliare_id,
|
|
'request' => (int) $reading->id,
|
|
]
|
|
),
|
|
],
|
|
]);
|
|
}
|
|
}
|
|
|
|
/** @return array<int,Carbon> */
|
|
private function buildContractScheduleDates(StabileContratto $contract): array
|
|
{
|
|
$start = $contract->decorrenza_dal instanceof Carbon
|
|
? $contract->decorrenza_dal->copy()
|
|
: ($contract->decorrenza_al instanceof Carbon ? $contract->decorrenza_al->copy() : now()->copy());
|
|
$end = $contract->decorrenza_al instanceof Carbon ? $contract->decorrenza_al->copy() : null;
|
|
$frequency = trim((string) ($contract->frequenza_scadenze ?? 'una_tantum'));
|
|
|
|
if ($contract->giorno_scadenza) {
|
|
$start->day(min(28, (int) $contract->giorno_scadenza));
|
|
}
|
|
|
|
if ($frequency === '' || $frequency === 'una_tantum') {
|
|
return [$end ?: $start];
|
|
}
|
|
|
|
$dates = [];
|
|
$cursor = $start->copy();
|
|
$limit = $end
|
|
? $end->copy()
|
|
: ($contract->rinnovo_automatico ? $start->copy()->addMonths(24) : $start->copy()->addMonths(12));
|
|
|
|
while ($cursor <= $limit && count($dates) < 60) {
|
|
$dates[] = $cursor->copy();
|
|
$cursor = match ($frequency) {
|
|
'mensile' => $cursor->copy()->addMonthNoOverflow(),
|
|
'bimestrale' => $cursor->copy()->addMonthsNoOverflow(2),
|
|
'trimestrale' => $cursor->copy()->addMonthsNoOverflow(3),
|
|
'semestrale' => $cursor->copy()->addMonthsNoOverflow(6),
|
|
default => $cursor->copy()->addYearNoOverflow(),
|
|
};
|
|
|
|
if ($contract->giorno_scadenza) {
|
|
$cursor->day(min(28, (int) $contract->giorno_scadenza));
|
|
}
|
|
}
|
|
|
|
return $dates;
|
|
}
|
|
|
|
/** @return array<int,string> */
|
|
private function collectServiceIdentifiers(StabileServizio $service): array
|
|
{
|
|
return array_values(array_filter(array_unique([
|
|
$this->normalizeIdentifier((string) ($service->codice_utenza ?? '')),
|
|
$this->normalizeIdentifier((string) ($service->codice_cliente ?? '')),
|
|
$this->normalizeIdentifier((string) ($service->codice_contratto ?? '')),
|
|
$this->normalizeIdentifier((string) ($service->contatore_matricola ?? '')),
|
|
])));
|
|
}
|
|
|
|
/** @return array<int,string> */
|
|
private function collectServiceStrongIdentifiers(StabileServizio $service): array
|
|
{
|
|
return array_values(array_filter(array_unique([
|
|
$this->normalizeIdentifier((string) ($service->codice_utenza ?? '')),
|
|
$this->normalizeIdentifier((string) ($service->codice_contratto ?? '')),
|
|
$this->normalizeIdentifier((string) ($service->contatore_matricola ?? '')),
|
|
])));
|
|
}
|
|
|
|
/** @return array<int,string> */
|
|
private function collectServiceWeakIdentifiers(StabileServizio $service): array
|
|
{
|
|
return array_values(array_filter(array_unique([
|
|
$this->normalizeIdentifier((string) ($service->codice_cliente ?? '')),
|
|
])));
|
|
}
|
|
|
|
/** @return array<int,string> */
|
|
private function collectContractIdentifiers(StabileContratto $contract): array
|
|
{
|
|
$identifiers = [
|
|
$this->normalizeIdentifier((string) ($contract->codice_contratto ?? '')),
|
|
$this->normalizeIdentifier((string) ($contract->riferimento_esterno ?? '')),
|
|
];
|
|
|
|
foreach ((array) ($contract->codici_collegati ?? []) as $row) {
|
|
if (! is_array($row)) {
|
|
continue;
|
|
}
|
|
|
|
$identifiers[] = $this->normalizeIdentifier((string) ($row['valore'] ?? ''));
|
|
}
|
|
|
|
return array_values(array_filter(array_unique($identifiers)));
|
|
}
|
|
|
|
/** @return array<int,string> */
|
|
private function collectContractStrongIdentifiers(StabileContratto $contract): array
|
|
{
|
|
$identifiers = [
|
|
$this->normalizeIdentifier((string) ($contract->codice_contratto ?? '')),
|
|
];
|
|
|
|
foreach ((array) ($contract->codici_collegati ?? []) as $row) {
|
|
if (! is_array($row)) {
|
|
continue;
|
|
}
|
|
|
|
$key = (string) ($row['chiave'] ?? '');
|
|
if (! in_array($key, ['utenza', 'codice_contratto', 'matricola_contatore'], true)) {
|
|
continue;
|
|
}
|
|
|
|
$identifiers[] = $this->normalizeIdentifier((string) ($row['valore'] ?? ''));
|
|
}
|
|
|
|
if ($identifiers === [] || count(array_filter($identifiers)) === 0) {
|
|
$identifiers[] = $this->normalizeIdentifier((string) ($contract->riferimento_esterno ?? ''));
|
|
}
|
|
|
|
return array_values(array_filter(array_unique($identifiers)));
|
|
}
|
|
|
|
/** @return array<int,string> */
|
|
private function collectContractWeakIdentifiers(StabileContratto $contract): array
|
|
{
|
|
$identifiers = [];
|
|
|
|
foreach ((array) ($contract->codici_collegati ?? []) as $row) {
|
|
if (! is_array($row) || (string) ($row['chiave'] ?? '') !== 'codice_cliente') {
|
|
continue;
|
|
}
|
|
|
|
$identifiers[] = $this->normalizeIdentifier((string) ($row['valore'] ?? ''));
|
|
}
|
|
|
|
if ($identifiers === []) {
|
|
$identifiers[] = $this->normalizeIdentifier((string) ($contract->riferimento_esterno ?? ''));
|
|
}
|
|
|
|
return array_values(array_filter(array_unique($identifiers)));
|
|
}
|
|
|
|
/** @return array<int,array{chiave:string,valore:string}> */
|
|
private function buildLinkedCodes(StabileServizio $service): array
|
|
{
|
|
return array_values(array_filter([
|
|
filled($service->codice_utenza) ? ['chiave' => 'utenza', 'valore' => (string) $service->codice_utenza] : null,
|
|
filled($service->codice_cliente) ? ['chiave' => 'codice_cliente', 'valore' => (string) $service->codice_cliente] : null,
|
|
filled($service->codice_contratto) ? ['chiave' => 'codice_contratto', 'valore' => (string) $service->codice_contratto] : null,
|
|
filled($service->contatore_matricola) ? ['chiave' => 'matricola_contatore', 'valore' => (string) $service->contatore_matricola] : null,
|
|
]));
|
|
}
|
|
|
|
/** @param array<int,array<string,mixed>> $left
|
|
* @param array<int,array<string,mixed>> $right
|
|
* @return array<int,array{chiave:string,valore:string}>
|
|
*/
|
|
private function mergeLinkedCodes(array $left, array $right): array
|
|
{
|
|
$map = [];
|
|
|
|
foreach (array_merge($left, $right) as $row) {
|
|
if (! is_array($row)) {
|
|
continue;
|
|
}
|
|
|
|
$chiave = trim((string) ($row['chiave'] ?? ''));
|
|
$valore = trim((string) ($row['valore'] ?? ''));
|
|
if ($chiave === '' || $valore === '') {
|
|
continue;
|
|
}
|
|
|
|
$map[$chiave . '|' . $valore] = [
|
|
'chiave' => $chiave,
|
|
'valore' => $valore,
|
|
];
|
|
}
|
|
|
|
return array_values($map);
|
|
}
|
|
|
|
private function normalizeIdentifier(string $value): string
|
|
{
|
|
return preg_replace('/[^A-Z0-9]/', '', strtoupper(trim($value))) ?? '';
|
|
}
|
|
|
|
private function nullableString(mixed $value): ?string
|
|
{
|
|
$value = trim((string) $value);
|
|
|
|
return $value !== '' ? $value : null;
|
|
}
|
|
|
|
private function serviceContractLabel(StabileServizio $service): string
|
|
{
|
|
$bits = array_filter([
|
|
trim((string) ($service->nome ?? 'Acqua')),
|
|
trim((string) ($service->codice_contratto ?? '')),
|
|
trim((string) ($service->codice_utenza ?? '')),
|
|
]);
|
|
|
|
return implode(' ', $bits);
|
|
}
|
|
}
|