fix(catasto-hub): elimina mock e fallback hardcoded Milizie 3, sincronizza dati catastali reali da DB

This commit is contained in:
michele 2026-08-31 15:11:41 +02:00
parent 595a8c8947
commit 95af1666c7
4 changed files with 229 additions and 632 deletions

View File

@ -214,6 +214,8 @@ public function mount(): void
$dirPath = $service->getStoragePath($this->stabileAttivo->id);
if (Storage::disk('public')->exists("{$dirPath}/geometria_stabile.json")) {
$this->stabileSubalterni = json_decode(Storage::disk('public')->get("{$dirPath}/geometria_stabile.json"), true) ?: [];
} else {
$this->sincronizzaGeometriaDaDb();
}
$associazioniPath = "{$dirPath}/anno_2026/associazioni_soppressi.json";
@ -476,17 +478,6 @@ public function loadUnitaList(): void
$jsonFilename = "risultato_immobile_" . (string)$uFoglio . "_" . (string)$uParticella . "{$subLabel}.json";
$jsonRelativePath = "{$dirPath}/{$jsonFilename}";
if (! Storage::disk('public')->exists($jsonRelativePath) && ! empty($uSubalterno)) {
$subPattern = "_sub{$uSubalterno}.json";
$allFiles = Storage::disk('public')->files($dirPath);
foreach ($allFiles as $f) {
if (str_contains($f, $subPattern)) {
$jsonRelativePath = $f;
break;
}
}
}
if (Storage::disk('public')->exists($jsonRelativePath)) {
try {
$sisterContent = json_decode(Storage::disk('public')->get($jsonRelativePath), true);
@ -657,17 +648,6 @@ public function selectUnita(int $unitaId): void
}
}
// Volture automatiche deterministiche per supercondominio Milizie 3
if (trim((string)$u->interno) === '3' && trim((string)$uSubalterno) === '3') {
$uSubalterno = '503';
}
if (trim((string)$u->interno) === '27' && trim((string)$uSubalterno) === '27') {
$uSubalterno = '56';
}
if (trim((string)$u->interno) === '20' && trim((string)$u->scala) === 'A') {
$uSubalterno = '524';
}
$this->stagingRawData = [];
if ($u->legacy_cond_id && Schema::connection('gescon_import')->hasTable('condomin')) {
$raw = DB::connection('gescon_import')->table('condomin')
@ -712,17 +692,6 @@ public function selectUnita(int $unitaId): void
$jsonFilename = "risultato_immobile_" . (string)$uFoglio . "_" . (string)$uParticella . "{$subLabel}.json";
$jsonRelativePath = "{$dirPath}/{$jsonFilename}";
if (! Storage::disk('public')->exists($jsonRelativePath) && ! empty($uSubalterno)) {
$subPattern = "_sub{$uSubalterno}.json";
$allFiles = Storage::disk('public')->files($dirPath);
foreach ($allFiles as $f) {
if (str_contains($f, $subPattern)) {
$jsonRelativePath = $f;
break;
}
}
}
if (Storage::disk('public')->exists($jsonRelativePath)) {
try {
$sisterContent = json_decode(Storage::disk('public')->get($jsonRelativePath), true);
@ -893,6 +862,60 @@ protected function loadProprietari(): void
});
}
public function sincronizzaGeometriaDaDb(): void
{
if (!$this->stabileAttivo) {
return;
}
$service = new SisterCatastoService();
$dirPath = $service->getStoragePath($this->stabileAttivo->id);
$unitaStabile = UnitaImmobiliare::where('stabile_id', $this->stabileAttivo->id)
->orderBy('scala')
->orderBy('piano')
->orderBy('interno')
->get();
$subalterniList = [];
$indirizzoBase = $this->stabileAttivo->indirizzo ?? '';
foreach ($unitaStabile as $u) {
$subVal = trim((string)($u->subalterno ?: ''));
if ($subVal === '') {
continue;
}
$rendVal = is_numeric($u->rendita_catastale) ? (float)$u->rendita_catastale : 0.0;
$renditaStr = $rendVal > 0.01 ? '€ ' . number_format($rendVal, 2, ',', '.') : '—';
$cat = $u->categoria_catastale ? self::formattaCategoriaCatastale($u->categoria_catastale) : 'A/2 - Abitazioni di tipo civile';
$indDett = $indirizzoBase;
if ($u->scala) {
$indDett .= " Scala {$u->scala}";
}
if ($u->interno) {
$indDett .= " Int. {$u->interno}";
}
if ($u->piano) {
$indDett .= " Piano {$u->piano}";
}
$subalterniList[] = [
'sub' => $subVal,
'rendita' => $renditaStr,
'partita' => 'ATTIVA',
'destinazione' => $cat,
'indirizzo_completo' => $indDett,
];
}
$this->stabileSubalterni = $subalterniList;
$this->ordinaSubalterniPerInterno();
Storage::disk('public')->put("{$dirPath}/geometria_stabile.json", json_encode($this->stabileSubalterni, JSON_PRETTY_PRINT));
}
public function interrogaGeometriaStabile(): void
{
if (!$this->stabileAttivo) {
@ -900,249 +923,17 @@ public function interrogaGeometriaStabile(): void
}
try {
$service = new SisterCatastoService();
$dirPath = $service->getStoragePath($this->stabileAttivo->id);
// Caricamento dei dati catastali 1a1 reali forniti dall'utente e dallo staging condomin
$subalterni256 = [];
$subalterni253 = [];
// 1. Mappa dei valori estratti dal documento reale di Sister (per Particella 256)
$realSisterMap256 = [
'1' => ['rendita' => '€ 2.227,22', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 1'],
'2' => ['rendita' => '€ 1.633,29', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 2'],
'3' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'4' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 4'],
'5' => ['rendita' => '€ 1.633,29', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 5'],
'6' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 6'],
'7' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'8' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 8'],
'9' => ['rendita' => '€ 1.781,78', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 9'],
'10' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 10'],
'11' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'12' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'13' => ['rendita' => '€ 1.633,29', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 13'],
'14' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 14'],
'15' => ['rendita' => '€ 1.781,78', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 15'],
'16' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 16'],
'17' => ['rendita' => '€ 1.633,29', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 17'],
'18' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'19' => ['rendita' => '€ 1.781,78', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 19'],
'20' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'21' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'22' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 22'],
'23' => ['rendita' => '€ 1.781,78', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 23'],
'24' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 24'],
'25' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'26' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 26'],
'27' => ['rendita' => '€ 1.781,78', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. 27'],
'28' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'29' => ['rendita' => '€ 1.336,33', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Int. B'],
'30' => ['rendita' => '€ 1.633,29', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 1'],
'31' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 2'],
'32' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 3'],
'33' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 4'],
'34' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 5'],
'35' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 6'],
'36' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'37' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'38' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'39' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 10'],
'40' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 11'],
'41' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 12'],
'42' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 13'],
'43' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'44' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 15 Scala B Int. 15'],
'45' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 16'],
'46' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 17'],
'47' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 18'],
'48' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 19'],
'49' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'50' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 21'],
'51' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 22'],
'52' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 23'],
'53' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 24'],
'54' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'55' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Piano 6-S1'],
'56' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'57' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Int. 28'],
'58' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 1'],
'59' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'60' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 3'],
'61' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'62' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 5'],
'63' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'64' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 7'],
'65' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'66' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 9'],
'67' => ['rendita' => '€ 1.633,29', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 10'],
'68' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 11'],
'69' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 12'],
'70' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 13'],
'71' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'72' => ['rendita' => '€ 2.227,22', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 15'],
'73' => ['rendita' => '€ 1.781,78', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 16'],
'74' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 17'],
'75' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'76' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'77' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'78' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'79' => ['rendita' => '€ 1.633,29', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 22'],
'80' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 23'],
'81' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'82' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 25'],
'83' => ['rendita' => '€ 1.633,29', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 26'],
'84' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 27'],
'85' => ['rendita' => '€ 1.781,78', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Int. 28'],
'86' => ['rendita' => '€ 1.781,78', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Interno 24 Piano 5'],
'87' => ['rendita' => '€ 105,77', 'partita' => 'ATTIVA', 'dest' => 'Cantina / Box (C/2)', 'ind' => 'Viale delle Milizie 3 Scala C Interno 24 Piano S1'],
// Subalterni speciali >500
'501' => ['rendita' => '€ 1.633,29', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Interno 14'],
'502' => ['rendita' => '€ 1.633,29', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Interno 18'],
'503' => ['rendita' => '€ 2.375,70', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Interno 3'],
'504' => ['rendita' => '€ 1.633,29', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Interno 4'],
'505' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Interno 19'],
'506' => ['rendita' => '€ 461,71', 'partita' => 'ATTIVA', 'dest' => 'Cantina / Box (C/2)', 'ind' => 'Viale delle Milizie 3 Scala A Piano S1'],
'507' => ['rendita' => '—', 'partita' => 'Soppressa', 'dest' => 'Soppresso', 'ind' => 'Viale delle Milizie 3'],
'508' => ['rendita' => '€ 277,03', 'partita' => 'ATTIVA', 'dest' => 'Cantina / Box (C/2)', 'ind' => 'Viale delle Milizie 3 Scala A Interno A Piano 7'],
'509' => ['rendita' => '€ 1.930,26', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Interno 14 Piano 3'],
'510' => ['rendita' => '€ 200,08', 'partita' => 'ATTIVA', 'dest' => 'Cantina / Box (C/2)', 'ind' => 'Viale delle Milizie 3 Interno 14 Piano S1'],
'512' => ['rendita' => '€ 923,43', 'partita' => 'ATTIVA', 'dest' => 'C/2 - Magazzini e locali di deposito', 'ind' => 'Viale delle Milizie 3 Scala A Piano S1'],
'513' => ['rendita' => 'R.Euro:', 'partita' => 'Bene comune non censibile', 'dest' => 'Bene comune non censibile', 'ind' => 'Viale delle Milizie 3 Scala A Piano S1'],
'514' => ['rendita' => '€ 1.930,26', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Interno 8 Piano 1'],
'515' => ['rendita' => '€ 227,24', 'partita' => 'ATTIVA', 'dest' => 'Cantina / Box (C/2)', 'ind' => 'Viale delle Milizie 3 Scala B Interno 8 Piano S1'],
'516' => ['rendita' => '€ 1.930,26', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Interno 21 Piano 5'],
'517' => ['rendita' => '€ 169,29', 'partita' => 'ATTIVA', 'dest' => 'Cantina / Box (C/2)', 'ind' => 'Viale delle Milizie 3 Scala C Interno 16 Piano S1'],
'518' => ['rendita' => '€ 1.633,29', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala C Interno 2 Piano T'],
'519' => ['rendita' => '€ 123,12', 'partita' => 'ATTIVA', 'dest' => 'Cantina / Box (C/2)', 'ind' => 'Viale delle Milizie 3 Scala C Piano S1'],
'520' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Interno 27 Piano 6'],
'521' => ['rendita' => '€ 123,12', 'partita' => 'ATTIVA', 'dest' => 'Cantina / Box (C/2)', 'ind' => 'Viale delle Milizie 3 Scala B Interno 27 Piano S1'],
'522' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala B Interno 25 Piano 6'],
'523' => ['rendita' => '€ 124,98', 'partita' => 'ATTIVA', 'dest' => 'Cantina / Box (C/2)', 'ind' => 'Viale delle Milizie 3 Scala B Interno 25 Piano S1'],
'524' => ['rendita' => '€ 2.078,74', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Interno 20 Piano 5'],
'525' => ['rendita' => '€ 1.781,78', 'partita' => 'ATTIVA', 'dest' => 'Abitazione (A/2)', 'ind' => 'Viale delle Milizie 3 Scala A Interno 16 Piano S1'],
];
foreach ($realSisterMap256 as $subStr => $item) {
$destVal = $item['dest'];
if (preg_match('/\(([A-F]\/?\d+)\)/i', $destVal, $m)) {
$destVal = self::formattaCategoriaCatastale($m[1]);
} elseif ($subStr === '512') {
$destVal = self::formattaCategoriaCatastale('C02');
} else {
if (str_contains(strtolower($destVal), 'abitazione')) {
$destVal = self::formattaCategoriaCatastale('A02');
} elseif (str_contains(strtolower($destVal), 'cantina') || str_contains(strtolower($destVal), 'magazzino')) {
$destVal = self::formattaCategoriaCatastale('C02');
}
}
$subalterni256[] = [
'sub' => $subStr,
'rendita' => $item['rendita'],
'partita' => $item['partita'],
'destinazione' => $destVal,
'indirizzo_completo' => $item['ind']
];
}
// Aggiungiamo eventuali fallbacks da staging solo se non sono Scala D e appartengono a 256
if (Schema::connection('gescon_import')->hasTable('condomin')) {
$rawStaging256 = DB::connection('gescon_import')->table('condomin')
->where('cod_stabile', $this->stabileAttivo->codice_stabile)
->where(function($q) {
$q->whereNull('scala')->orWhere('scala', '<>', 'D');
})
->whereNotNull('catasto_sub')
->where('catasto_sub', '<>', '')
->get();
foreach ($rawStaging256 as $r) {
$sVal = trim((string)$r->catasto_sub);
if ($sVal !== '' && !isset($realSisterMap256[$sVal])) {
$rendVal = is_numeric($r->catasto_rendita) ? (float)$r->catasto_rendita : 0.0;
$renditaStr = $rendVal > 0.01 ? '€ ' . number_format($rendVal, 2, ',', '.') : '—';
$cat = strtoupper(trim((string)$r->catasto_categoria));
$dest = ($cat === 'C02' || $cat === 'C06' || $cat === 'C07') ? 'Cantina / Box (C/2)' : 'Abitazione (' . ($cat ?: 'A/2') . ')';
$subalterni256[] = [
'sub' => $sVal,
'rendita' => $renditaStr,
'partita' => 'ATTIVA',
'destinazione' => $dest,
'indirizzo_completo' => 'Viale delle Milizie 3' . ($r->scala ? ', Scala ' . $r->scala : '') . ($r->interno ? ', Interno ' . $r->interno : '')
];
$realSisterMap256[$sVal] = true;
}
}
}
// Scrittura cache isolata Compartimento 1
Storage::disk('public')->put("{$dirPath}/geometria_stabile_405_256.json", json_encode($subalterni256, JSON_PRETTY_PRINT));
// --- COMPARTIMENTO 2: Particella 253 (Scala D) ---
$real253Specs = [
'1' => ['cat' => 'A02', 'int' => '1', 'piano' => 'T-S1', 'rendita' => 'R.Euro:2227,22', 'cons' => '7,5 vani'],
'3' => ['cat' => 'A02', 'int' => '3', 'piano' => '1-S1', 'rendita' => 'R.Euro:1633,29', 'cons' => '5,5 vani'],
'4' => ['cat' => 'A02', 'int' => '4', 'piano' => 'S1-1', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'5' => ['cat' => 'A02', 'int' => '5', 'piano' => '2-S1', 'rendita' => 'R.Euro:1633,29', 'cons' => '5,5 vani'],
'6' => ['cat' => 'A02', 'int' => '6', 'piano' => '2-S1', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'7' => ['cat' => 'A02', 'int' => '7', 'piano' => '3-S1', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'8' => ['cat' => '', 'int' => '', 'piano' => '', 'rendita' => 'Soppressa', 'cons' => ''],
'9' => ['cat' => '', 'int' => '', 'piano' => '', 'rendita' => 'Soppressa', 'cons' => ''],
'10' => ['cat' => 'A02', 'int' => '10', 'piano' => '4-S1', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'11' => ['cat' => 'A02', 'int' => '11', 'piano' => '5-S1', 'rendita' => 'R.Euro:1633,29', 'cons' => '5,5 vani'],
'12' => ['cat' => 'A02', 'int' => '12', 'piano' => '5-S1', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'13' => ['cat' => 'A02', 'int' => '', 'piano' => '6-S1', 'rendita' => 'R.Euro:1781,78', 'cons' => '6 vani'],
'14' => ['cat' => 'A02', 'int' => '14', 'piano' => '6-S1', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'501' => ['cat' => 'A02', 'int' => '2', 'piano' => 'T-S1', 'rendita' => 'R.Euro:1633,29', 'cons' => '5,5 vani'],
'502' => ['cat' => 'C02', 'int' => 'Magazzino 9 mq', 'piano' => 'S1', 'rendita' => 'R.Euro:118,50', 'cons' => '9 mq'],
'503' => ['cat' => 'A02', 'int' => '9', 'piano' => '4', 'rendita' => 'R.Euro:2375,70', 'cons' => '8 vani'],
'504' => ['cat' => 'C02', 'int' => 'Magazzino 8 mq', 'piano' => 'S1', 'rendita' => 'R.Euro:105,77', 'cons' => '8 mq'],
'505' => ['cat' => 'A02', 'int' => '8', 'piano' => '3', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'506' => ['cat' => 'C02', 'int' => 'Magazzino 8 mq', 'piano' => 'S1', 'rendita' => 'R.Euro:105,77', 'cons' => '8 mq']
];
foreach ($real253Specs as $subD => $meta) {
$isSoppressa = $meta['rendita'] === 'Soppressa';
$rendVal = 0.0;
if (preg_match('/R\.Euro:\s*([\d\.,]+)/i', $meta['rendita'], $mr)) {
$rendVal = (float)str_replace(',', '.', $mr[1]);
}
$renditaStr = $isSoppressa ? 'Soppressa' : ($rendVal > 0.01 ? '€ ' . number_format($rendVal, 2, ',', '.') : '—');
$dest = $isSoppressa ? 'Soppresso' : (($meta['cat'] === 'C02') ? 'Cantina / Box (C/2)' : 'Abitazione (A/2)');
$ind = "VIALE DELLE MILIZIE n. 3 Scala D" . (!empty($meta['int']) ? " Interno " . $meta['int'] : "") . (!empty($meta['piano']) ? " Piano " . $meta['piano'] : "");
$subalterni253[] = [
'sub' => $subD,
'rendita' => $renditaStr,
'partita' => $isSoppressa ? 'Soppressa' : 'ATTIVA',
'destinazione' => $dest,
'indirizzo_completo' => $ind
];
}
// Scrittura cache isolata Compartimento 2
Storage::disk('public')->put("{$dirPath}/geometria_stabile_405_253.json", json_encode($subalterni253, JSON_PRETTY_PRINT));
// Unione delle due liste per la vista orizzontale a specchio TAB A
$this->stabileSubalterni = array_merge($subalterni256, $subalterni253);
$this->ordinaSubalterniPerInterno();
// Scrittura del file principale per retrocompatibilità
Storage::disk('public')->put("{$dirPath}/geometria_stabile.json", json_encode($this->stabileSubalterni, JSON_PRETTY_PRINT));
$this->sincronizzaGeometriaDaDb();
Notification::make()
->title('Geometria Stabile reale acquisita')
->body('Allineamento multi-particella completato (256 per A-B-C, 253 per D).')
->title('Geometria Stabile Sincronizzata dal Database')
->body('Mappa subalterni e indirizzi aggiornata con successo dai dati consolidati dello stabile.')
->success()
->send();
} catch (Exception $e) {
Notification::make()
->title('Errore Sister Stabile')
->title('Errore Geometria Stabile')
->body($e->getMessage())
->danger()
->send();
@ -1160,7 +951,6 @@ public function interrogaSister(bool $force = false): void
$subLabel = ($this->editSubalterno !== null && $this->editSubalterno !== '') ? "_sub{$this->editSubalterno}" : "";
$jsonFilename = "risultato_immobile_" . (string)$this->editFoglio . "_" . (string)$this->editParticella . "{$subLabel}.json";
$jsonPath = "{$dirPath}/{$jsonFilename}";
$subStr = (string)$this->editSubalterno;
// 1. Lettura immediata da Cache locale se presente e non forzato
@ -1175,306 +965,12 @@ public function interrogaSister(bool $force = false): void
return;
}
// 2. Controllo anagrafica reale pre-caricata (mappa reale)
$subRealParams = [
'1' => ['int' => '1', 'scala' => 'A', 'cat' => 'A02', 'cons' => '7,5 vani', 'rendita' => 'R.Euro:2227,22', 'id' => 'NjU0Mzg1IzY1NDM4NSNGIzQwNSMyNTYjSDUwMSMjMSMgI1JPTUE='],
'2' => ['int' => '2', 'scala' => 'A', 'cat' => 'A02', 'cons' => '5,5 vani', 'rendita' => 'R.Euro:1633,29', 'id' => 'NjU0Mzg2IzY1NDM4NiNGIzQwNSMyNTYjSDUwMSMwMjMwMzQxIzIjICNST01B'],
'3' => ['int' => '3', 'scala' => 'A', 'cat' => '', 'cons' => '', 'rendita' => 'Soppressa', 'id' => 'NjU0Mzg3IzY1NDM4NyNGIzQwNSMyNTYjSDUwMSNTb3BwcmVzc2EjMyMgI1JPTUE='],
'4' => ['int' => '4', 'scala' => 'A', 'cat' => 'A02', 'cons' => '7 vani', 'rendita' => 'R.Euro:2078,74', 'id' => 'NjU0Mzg4IzY1NDM4OCNGIzQwNSMyNTYjSDUwMSMjNCMgI1JPTUE='],
'5' => ['int' => '5', 'scala' => 'A', 'cat' => 'A02', 'cons' => '5,5 vani', 'rendita' => 'R.Euro:1633,29', 'id' => 'NjU0Mzg5IzY1NDM4OSNGIzQwNSMyNTYjSDUwMSMwMjMwMzQxIzUjICNST01B'],
'6' => ['int' => '6', 'scala' => 'A', 'cat' => 'A02', 'cons' => '7 vani', 'rendita' => 'R.Euro:2078,74', 'id' => 'NjU0MzkxIzY1NDM5MSNGIzQwNSMyNTYjSDUwMSNTb3BwcmVzc2EjNyMgI1JPTUE='],
'7' => ['int' => '7', 'scala' => 'A', 'cat' => '', 'cons' => '', 'rendita' => 'Soppressa', 'id' => 'NjU0MzkxIzY1NDM5MSNGIzQwNSMyNTYjSDUwMSNTb3BwcmVzc2EjNyMgI1JPTUE='],
'8' => ['int' => '8', 'scala' => 'A', 'cat' => 'A02', 'cons' => '7 vani', 'rendita' => 'R.Euro:2078,74', 'id' => 'NjU0MzkyIzY1NDM5MiNGIzQwNSMyNTYjSDUwMSMjOCMgI1JPTUE='],
'9' => ['int' => '9', 'scala' => 'A', 'cat' => 'A02', 'cons' => '6 vani', 'rendita' => 'R.Euro:1781,78', 'id' => 'NjU0MzkzIzY1NDM5MyNGIzQwNSMyNTYjSDUwMSMjOSMgI1JPTUE='],
'10' => ['int' => '10', 'scala' => 'A', 'cat' => 'A02', 'cons' => '7 vani', 'rendita' => 'R.Euro:2078,74', 'id' => 'NjU4Mzk0IzY1ODM5NCNGIzQwNSMyNTYjSDUwMSMjMTAjICNST01B'],
'18' => ['int' => '18', 'scala' => 'C', 'cat' => '', 'cons' => '', 'rendita' => 'Soppressa', 'id' => 'NjU1Mzg4IzY1NTM4OCNGIzQwNSMyNTYjSDUwMSN1Yl8xODMgI1JPTUE='],
'21' => ['int' => '19', 'scala' => 'C', 'cat' => '', 'cons' => '', 'rendita' => 'Soppressa', 'id' => 'NjU1Mzg5IzY1NTM5OSNGIzQwNSMyNTYjSDUwMSN1Yl8yMTMgI1JPTUE='],
'37' => ['int' => '21', 'scala' => 'C', 'cat' => '', 'cons' => '', 'rendita' => 'Soppressa', 'id' => 'NjU1MzkwIzY1NTM5MCNGIzQwNSMyNTYjSDUwMSN1Yl8zNzMgI1JPTUE='],
'86' => ['int' => '24', 'scala' => 'C', 'cat' => 'A02', 'cons' => '6 vani', 'rendita' => 'R.Euro:1781,78', 'id' => 'NjcwNTY0NyM2NzA1NjQ3I0YjNDA1IzI1NiNINTAxIyM4NiMgI1JPTUE='],
'87' => ['int' => '24', 'scala' => 'C', 'cat' => 'C02', 'cons' => '8 m2', 'rendita' => 'R.Euro:105,77', 'id' => 'NjcwNTY0OCM2NzA1NjQ4I0YjNDA1IzI1NiNINTAxIyM4NyMgI1JPTUE='],
// Sub > 500
'501' => ['int' => '14', 'scala' => 'C', 'cat' => 'A02', 'cons' => '5,5 vani', 'rendita' => 'R.Euro:1633,29', 'id' => 'NDQ5NTU3MiM0NDk1NTcyI0YjNDA1IzI1NiNINTAxIyM1MDEjICNST01B'],
'502' => ['int' => '18', 'scala' => 'C', 'cat' => 'A02', 'cons' => '5,5 vani', 'rendita' => 'R.Euro:1633,29', 'id' => 'NDQ5NTU3MyM0NDk1NTczI0YjNDA1IzI1NiNINTAxIyM1MDIjICNST01B'],
'503' => ['int' => '3', 'scala' => 'A', 'cat' => 'A02', 'cons' => '8 vani', 'rendita' => 'R.Euro:2375,70', 'id' => 'NDUxMjQ0NSM0NTEyNDQ1I0YjNDA1IzI1NiNINTAxIyM1MDMjICNST01B'],
'504' => ['int' => '4', 'scala' => 'C', 'cat' => 'A02', 'cons' => '5,5 vani', 'rendita' => 'R.Euro:1633,29', 'id' => 'NDU1MDk1MCM0NTUwOTUwI0YjNDA1IzI1NiNINTAxIyM1MDQjICNST01B'],
'505' => ['int' => '19', 'scala' => 'C', 'cat' => 'A02', 'cons' => '7 vani', 'rendita' => 'R.Euro:2078,74', 'id' => 'NDU1MDk1MSM0NTUwOTUxI0YjNDA1IzI1NiNINTAxIyM1MDUjICNST01B'],
'506' => ['int' => 'Piano S1', 'scala' => 'A', 'cat' => 'C02', 'cons' => '30 m2', 'rendita' => 'R.Euro:461,71', 'id' => 'NDgxMTQxOSM0ODExNDE5I0YjNDA1IzI1NiNINTAxIyM1MDYjICNST01B'],
'507' => ['int' => '', 'scala' => 'A', 'cat' => '', 'cons' => '', 'rendita' => 'Soppressa', 'id' => 'NDgxMTQyNyM0ODExNDI3I0YjNDA1IzI1NiNINTAxI1NvcHByZXNzYSM1MDcjICNST01B'],
'508' => ['int' => 'A Piano 7', 'scala' => 'A', 'cat' => 'C02', 'cons' => '18 m2', 'rendita' => 'R.Euro:277,03', 'id' => 'NTA4MTU4NCM1MDgxNTg0I0YjNDA1IzI1NiNINTAxIyM1MDgjICNST01B'],
'509' => ['int' => '14 Piano 3', 'scala' => '', 'cat' => 'A02', 'cons' => '6,5 vani', 'rendita' => 'R.Euro:1930,26', 'id' => 'NTgwNTU2NCM1ODA1NTY0I0YjNDA1IzI1NiNINTAxIyM1MDkjICNST01B'],
'510' => ['int' => '14 Piano S1', 'scala' => '', 'cat' => 'C02', 'cons' => '13 m2', 'rendita' => 'R.Euro:200,08', 'id' => 'NTgwNTU2NSM1ODA1NTY1I0YjNDA1IzI1NiNINTAxIyM1MTAjICNST01B'],
'512' => ['int' => 'Piano S1', 'scala' => 'A', 'cat' => 'C02', 'cons' => '60 m2', 'rendita' => 'R.Euro:923,43', 'id' => 'NTkwMzc2MiM1OTAzNzYyI0YjNDA1IzI1NiNINTAxIyM1MTIjICNST01B'],
'513' => ['int' => 'Piano S1', 'scala' => 'A', 'cat' => 'B.C.N.C.', 'cons' => '', 'rendita' => 'R.Euro:0,00', 'id' => 'NTkwMzc2MyM1OTAzNzYzI0YjNDA1IzI1NiNINTAxI0JlbmUgY29tdW5lIG5vbiBjZW5zaWJpbGUjNTEzIyAjUk9NQQ=='],
'514' => ['int' => '8 Piano 1', 'scala' => 'B', 'cat' => 'A02', 'cons' => '6,5 vani', 'rendita' => 'R.Euro:1930,26', 'id' => 'NjU2MDc2NCM2NTYwNzY0I0YjNDA1IzI1NiNINTAxIyM1MTQjICNST01B'],
'515' => ['int' => '8 Piano S1', 'scala' => 'B', 'cat' => 'C02', 'cons' => '20 m2', 'rendita' => 'R.Euro:227,24', 'id' => 'NjU2MDc2NSM2NTYwNzY1I0YjNDA1IzI1NiNINTAxIyM1MTUjICNST01B'],
'516' => ['int' => '21 Piano 5', 'scala' => 'C', 'cat' => 'A02', 'cons' => '6,5 vani', 'rendita' => 'R.Euro:1930,26', 'id' => 'NjU6MjYxMSM2NTYyNjExI0YjNDA1IzI1NiNINTAxIyM1MTYjICNST01B'],
'517' => ['int' => '16 Piano S1', 'scala' => 'C', 'cat' => 'C02', 'cons' => '11 m2', 'rendita' => 'R.Euro:169,29', 'id' => 'NjU2MjYxMiM2NTYyNjEyI0YjNDA1IzI1NiNINTAxIyM1MTcjICNST01B'],
'518' => ['int' => '2 Piano T', 'scala' => 'C', 'cat' => 'A02', 'cons' => '5,5 vani', 'rendita' => 'R.Euro:1633,29', 'id' => 'NjU4NzA4NCM2NTg3MDg0I0YjNDA1IzI1NiNINTAxIyM1MTgjICNST01B'],
'519' => ['int' => 'Piano S1', 'scala' => 'C', 'cat' => 'C02', 'cons' => '8 m2', 'rendita' => 'R.Euro:123,12', 'id' => 'NjU4NzA4NSM2NTg3MDg1I0YjNDA1IzI1NiNINTAxIyM1MTkjICNST01B'],
'520' => ['int' => '27 Piano 6', 'scala' => 'B', 'cat' => 'A02', 'cons' => '7 vani', 'rendita' => 'R.Euro:2078,74', 'id' => 'NjY4Mzc4NSM2NjgzNzg1I0YjNDA1IzI1NiNINTAxIyM1MjAjICNST01B'],
'521' => ['int' => '27 Piano S1', 'scala' => 'B', 'cat' => 'C02', 'cons' => '8 m2', 'rendita' => 'R.Euro:123,12', 'id' => 'NjY4Mzc4NiM2NjgzNzg2I0YjNDA1IzI1NiNINTAxIyM1MjEjICNST01B'],
'522' => ['int' => '25 Piano 6', 'scala' => 'B', 'cat' => 'A02', 'cons' => '7 vani', 'rendita' => 'R.Euro:2078,74', 'id' => 'NjY5NDcyMCM2Njk0NzIwI0YjNDA1IzI1NiNINTAxIyM1MjIjICNST01B'],
'523' => ['int' => '25 Piano S1', 'scala' => 'B', 'cat' => 'C02', 'cons' => '11 m2', 'rendita' => 'R.Euro:124,98', 'id' => 'NjY5NDcyMSM2Njk0NzIxI0YjNDA1IzI1NiNINTAxIyM1MjMjICNST01B'],
'524' => ['int' => '20 Piano 5', 'scala' => 'A', 'cat' => 'A02', 'cons' => '7 vani', 'rendita' => 'R.Euro:2078,74', 'id' => 'NjcxMzk0NCM2NzEzOTQ0I0YjNDA1IzI1NiNINTAxIyM1MjQjICNST01B'],
'525' => ['int' => '16 Piano S1', 'scala' => 'A', 'cat' => 'A02', 'cons' => '6 vani', 'rendita' => 'R.Euro:1781,78', 'id' => 'NjcxMzk0NSM2NzEzOTQ1I0YjNDA1IzI1NiNINTAxIyM1MjVjICNST01B'],
];
if (isset($subRealParams[$subStr])) {
$meta = $subRealParams[$subStr];
$isSoppressa = $meta['rendita'] === 'Soppressa';
$pianoVal = $isSoppressa ? '' : ($meta['int'] && str_contains($meta['int'], 'Piano') ? $meta['int'] : 'Piano 5');
$intestati = $isSoppressa ? [] : [
[
'denominazione' => 'ATER COMUNE DI ROMA con sede in ROMA (RM)',
'cf' => '00410700587',
'proprieta' => 'Proprieta\'',
'quota' => '1000/1000'
]
];
if ($subStr === '524') {
$pianoVal = 'Piano 5';
$intestati = [
[
'denominazione' => 'BENEDETTO DANIELA',
'cf' => 'BNDDNL86M58H224O',
'proprieta' => 'Proprieta\'',
'quota' => '1/2'
],
[
'denominazione' => 'FERRANTE BIAGIO',
'cf' => 'FRRBGI87E25I438V',
'proprieta' => 'Proprieta\'',
'quota' => '1/2'
]
];
} elseif ($subStr === '2') {
$pianoVal = 'Piano S1 - T';
$intestati = [
[
'denominazione' => 'SCILLIA MICHELE',
'cf' => 'SCLMHL48C23H501F',
'proprieta' => 'Proprieta\'',
'quota' => '1/1'
]
];
} elseif ($subStr === '503') {
$pianoVal = 'Piano T - S1';
$intestati = [
[
'denominazione' => 'IMPROTA RAFFAELLA',
'cf' => 'MPRRFL70A01H501A',
'proprieta' => 'Proprieta\'',
'quota' => '1/2'
],
[
'denominazione' => 'CONCILIO CHIARA',
'cf' => 'CNCCHR75A01H501B',
'proprieta' => 'Proprieta\'',
'quota' => '1/2'
]
];
} elseif ($subStr === '4') {
$pianoVal = 'Piano 1-S1';
$intestati = [
[
'denominazione' => 'MARSILI GIADA',
'cf' => 'MRSGDI07M65H501D',
'proprieta' => 'Proprieta\'',
'quota' => '1/1'
],
[
'denominazione' => 'MARSILI ROBERTO',
'cf' => 'MRSRRT70B21H501A',
'proprieta' => 'Abitazione',
'quota' => '1/1'
]
];
}
$mockResult = [
'immobili' => [
[
'sezioneUrb' => '',
'foglio' => '405',
'particella' => '256',
'subalterno' => $subStr,
'indirizzo' => 'VIALE DELLE MILIZIE n. 3 ' . ($meta['scala'] ? 'Scala ' . $meta['scala'] . ' ' : '') . ($meta['int'] ? 'Interno ' . $meta['int'] : '') . ($pianoVal ? ' Piano ' . str_ireplace('Piano', '', $pianoVal) : ''),
'zonacensuaria' => '002',
'categoria' => self::formattaCategoriaCatastale($meta['cat']),
'classamento' => self::formattaCategoriaCatastale($meta['cat']),
'classe' => $isSoppressa ? '' : '03',
'consistenza' => $meta['cons'],
'rendita' => $isSoppressa ? '' : $meta['rendita'],
'partita' => $isSoppressa ? 'Soppressa' : '02303' . $subStr,
'piano' => $pianoVal,
'idImmobile' => $meta['id'],
'intestati' => $intestati
]
]
];
Storage::disk('public')->put($jsonPath, json_encode($mockResult, JSON_PRETTY_PRINT));
Notification::make()->title('Dati Catastali caricati per Sub ' . $subStr)->success()->send();
$this->loadUnitaList();
$this->selectUnita($this->selectedUnitaId);
return;
}
if ($this->editParticella === '253') {
$real253Specs = [
'1' => ['cat' => 'A02', 'int' => '1', 'piano' => 'T-S1', 'rendita' => 'R.Euro:2227,22', 'cons' => '7,5 vani'],
'3' => ['cat' => 'A02', 'int' => '3', 'piano' => '1-S1', 'rendita' => 'R.Euro:1633,29', 'cons' => '5,5 vani'],
'4' => ['cat' => 'A02', 'int' => '4', 'piano' => 'S1-1', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'5' => ['cat' => 'A02', 'int' => '5', 'piano' => '2-S1', 'rendita' => 'R.Euro:1633,29', 'cons' => '5,5 vani'],
'6' => ['cat' => 'A02', 'int' => '6', 'piano' => '2-S1', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'7' => ['cat' => 'A02', 'int' => '7', 'piano' => '3-S1', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'8' => ['cat' => '', 'int' => '', 'piano' => '', 'rendita' => 'Soppressa', 'cons' => ''],
'9' => ['cat' => '', 'int' => '', 'piano' => '', 'rendita' => 'Soppressa', 'cons' => ''],
'10' => ['cat' => 'A02', 'int' => '10', 'piano' => '4-S1', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'11' => ['cat' => 'A02', 'int' => '11', 'piano' => '5-S1', 'rendita' => 'R.Euro:1633,29', 'cons' => '5,5 vani'],
'12' => ['cat' => 'A02', 'int' => '12', 'piano' => '5-S1', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'13' => ['cat' => 'A02', 'int' => '', 'piano' => '6-S1', 'rendita' => 'R.Euro:1781,78', 'cons' => '6 vani'],
'14' => ['cat' => 'A02', 'int' => '14', 'piano' => '6-S1', 'rendita' => 'R.Euro:1484,81', 'cons' => '5 vani'],
'501' => ['cat' => 'A02', 'int' => '2', 'piano' => 'T-S1', 'rendita' => 'R.Euro:1633,29', 'cons' => '5,5 vani'],
'502' => ['cat' => 'C02', 'int' => 'Magazzino 9 mq', 'piano' => 'S1', 'rendita' => 'R.Euro:118,50', 'cons' => '9 mq'],
'503' => ['cat' => 'A02', 'int' => '9', 'piano' => '4', 'rendita' => 'R.Euro:2375,70', 'cons' => '8 vani'],
'504' => ['cat' => 'C02', 'int' => 'Magazzino 8 mq', 'piano' => 'S1', 'rendita' => 'R.Euro:105,77', 'cons' => '8 mq'],
'505' => ['cat' => 'A02', 'int' => '8', 'piano' => '3', 'rendita' => 'R.Euro:2078,74', 'cons' => '7 vani'],
'506' => ['cat' => 'C02', 'int' => 'Magazzino 8 mq', 'piano' => 'S1', 'rendita' => 'R.Euro:105,77', 'cons' => '8 mq']
];
if (isset($real253Specs[$subStr])) {
$meta = $real253Specs[$subStr];
$isSoppressa = $meta['rendita'] === 'Soppressa';
$pianoVal = $isSoppressa ? '' : 'Piano ' . $meta['piano'];
$intestati = [];
$uReal = \App\Models\UnitaImmobiliare::where('stabile_id', $this->stabileAttivo->id)
->where('scala', 'D')
->where('subalterno', $subStr)
->first();
if ($uReal) {
$proprietariAttivi = DB::table('persone_unita_relazioni as pur')
->join('persone as p', 'p.id', '=', 'pur.persona_id')
->where('pur.unita_id', $uReal->id)
->where('pur.tipo_relazione', 'proprietario')
->where('pur.attivo', 1)
->get();
foreach ($proprietariAttivi as $pa) {
$intestati[] = [
'denominazione' => trim($pa->cognome . ' ' . $pa->nome),
'cf' => $pa->codice_fiscale,
'proprieta' => 'Proprieta\'',
'quota' => '1/1'
];
}
}
if ($subStr === '14') {
$intestati = [
[
'denominazione' => "D'ANNA LUISA",
'cf' => 'DNNLSU64L71H501D',
'proprieta' => 'Proprieta\'',
'quota' => '1/1'
]
];
}
if (empty($intestati) && !$isSoppressa) {
Notification::make()
->title('Errore Catastale Bloccante')
->body('Nessun intestatario reale o comproprietario attivo configurato per il Subalterno ' . $subStr . '.')
->danger()
->persistent()
->send();
return;
}
$mockResult = [
'immobili' => [
[
'sezioneUrb' => '',
'foglio' => '405',
'particella' => '253',
'subalterno' => $subStr,
'indirizzo' => 'VIALE DELLE MILIZIE n. 3 Scala D' . (!empty($meta['int']) ? ' Interno ' . $meta['int'] : '') . ($pianoVal ? ' ' . $pianoVal : ''),
'zonacensuaria' => '002',
'categoria' => self::formattaCategoriaCatastale($meta['cat']),
'classamento' => self::formattaCategoriaCatastale($meta['cat']),
'classe' => $isSoppressa ? '' : '03',
'consistenza' => $meta['cons'],
'rendita' => $isSoppressa ? '' : $meta['rendita'],
'partita' => $isSoppressa ? 'Soppressa' : '02303' . $subStr,
'piano' => $pianoVal,
'idImmobile' => 'NjU1Mzg4IzY1NTM4OCNGIzQwNSMyNTMjSDUwMSM' . $subStr . 'M',
'intestati' => $intestati
]
]
];
Storage::disk('public')->put($jsonPath, json_encode($mockResult, JSON_PRETTY_PRINT));
Notification::make()->title('Dati Catastali caricati per Sub ' . $subStr)->success()->send();
$this->loadUnitaList();
$this->selectUnita($this->selectedUnitaId);
return;
}
}
// 3. Fallback su staging condomin
if (Schema::connection('gescon_import')->hasTable('condomin')) {
$rStaging = DB::connection('gescon_import')->table('condomin')
->where('cod_stabile', $this->stabileAttivo->codice_stabile)
->where('catasto_sub', $subStr)
->first();
if ($rStaging) {
$rendVal = is_numeric($rStaging->catasto_rendita) ? (float)$rStaging->catasto_rendita : 0.0;
$renditaStr = $rendVal > 0.01 ? 'R.Euro:' . number_format($rendVal, 2, '.', '') : '';
$cat = strtoupper(trim((string)$rStaging->catasto_categoria)) ?: 'A02';
$mockResult = [
'immobili' => [
[
'sezioneUrb' => '',
'foglio' => '405',
'particella' => '256',
'subalterno' => $subStr,
'indirizzo' => 'VIALE DELLE MILIZIE n. 3 Scala ' . ($rStaging->scala ?: 'B') . ' Interno ' . ($rStaging->interno ?: '1'),
'zonacensuaria' => '002',
'categoria' => $cat,
'classamento' => $cat,
'classe' => '03',
'consistenza' => $rStaging->catasto_consistenza ?: '6 vani',
'rendita' => $renditaStr,
'partita' => '02303' . $subStr,
'piano' => $rStaging->piano ?: 'Piano T',
'idImmobile' => 'NDUxMjQ0NSM0NTEyNDQ1I0YjNDA1IzI1NiNINTAxIyM' . $subStr . 'IyM#ROMA',
'intestati' => [
[
'denominazione' => $rStaging->proprietario_denominazione ?: 'ATER COMUNE DI ROMA',
'cf' => $rStaging->cond_cod_fisc ?: '00410700587',
'proprieta' => 'Proprieta\'',
'quota' => '1000/1000'
]
]
]
]
];
Storage::disk('public')->put($jsonPath, json_encode($mockResult, JSON_PRETTY_PRINT));
Notification::make()->title('Dati Catastali caricati per Sub ' . $subStr)->success()->send();
$this->loadUnitaList();
$this->selectUnita($this->selectedUnitaId);
return;
}
}
// 4. Chiamata reale a Sister API
// 2. Chiamata reale a Sister API
try {
$tipoCatasto = 'F';
$uffProvinciale = 'ROMA Territorio-RM';
$denomComune = $service->getComuneCat($this->stabileAttivo->id, $uffProvinciale, $this->stabileAttivo->citta ?? 'ROMA');
$cittaName = $this->stabileAttivo->citta ?? 'ROMA';
$denomComune = $service->getComuneCat($this->stabileAttivo->id, $uffProvinciale, $cittaName);
$result = $service->ricercaIntestatiImmobile(
$this->stabileAttivo->id,
@ -1485,23 +981,38 @@ public function interrogaSister(bool $force = false): void
(string)$this->editParticella,
$this->editSubalterno,
null,
null
$this->editSezioneUrbana ?: null
);
if (!empty($result)) {
if (!empty($result) && !isset($result['err'])) {
Storage::disk('public')->put($jsonPath, json_encode($result, JSON_PRETTY_PRINT));
Notification::make()->title('Dati Catastali caricati da Sister API')->success()->send();
$this->loadUnitaList();
$this->selectUnita($this->selectedUnitaId);
return;
}
} catch (Exception $e) {}
// Se arriviamo qui, nessun dato è reperibile (Garbage In - Garbage Out)
if (isset($result['err'])) {
Notification::make()
->title('Risposta Sister API')
->body('Sister ha risposto: ' . $result['err'])
->warning()
->send();
return;
}
} catch (Exception $e) {
Notification::make()
->title('Errore Connessione Sister API')
->body('Impossibile contattare Sister API: ' . $e->getMessage())
->danger()
->send();
return;
}
Notification::make()
->title('Dati Catastali Non Disponibili (Garbage In - Garbage Out)')
->body("Impossibile reperire i dati catastali ufficiali per il Subalterno {$subStr}. Nessun fallback matematico o stima probabilistica applicata.")
->danger()
->title('Dati Catastali Non Disponibili')
->body("Nessun dato catastale ufficiale reperito per Foglio {$this->editFoglio}, Particella {$this->editParticella}, Subalterno {$subStr}. Nessun fallback probabilistico applicato.")
->warning()
->send();
}
@ -2160,9 +1671,10 @@ public function caricaVisuraProprietariRt(): void
$service = new SisterCatastoService();
$dirPath = $service->getStoragePath($this->stabileAttivo->id);
$files = Storage::disk('public')->files($dirPath);
$soggetti = [];
// 1. Lettura dai file JSON Sister presenti per lo stabile attivo
$files = Storage::disk('public')->files($dirPath);
foreach ($files as $file) {
if (str_contains($file, 'risultato_immobile_') && str_ends_with($file, '.json')) {
try {
@ -2220,58 +1732,54 @@ public function caricaVisuraProprietariRt(): void
}
}
// cointestatari storici per sub 503 (Interno 3)
$cfImprota = 'MPRRFL75C12H501Z';
if (!isset($soggetti[$cfImprota])) {
$soggetti[$cfImprota] = [
'cf' => $cfImprota,
'nominativo' => 'IMPROTA RAFFAELLA',
'unita' => []
];
}
$soggetti[$cfImprota]['unita'][] = [
'descrizione' => 'Scala A · Int. 3',
'dettagli' => 'Foglio 405 Part. 256 Sub. 503',
'titolo' => 'Comproprietà',
'quota' => '1/2'
];
// 2. Integrazione da anagrafiche e titolarità consolidate a DB per lo stabile attivo
if (Schema::hasTable('unita_anagrafica_periodo') && Schema::hasTable('anagrafiche')) {
$unitaIds = collect($this->unitaList)->pluck('id')->all();
$dbOwners = DB::table('unita_anagrafica_periodo as uap')
->join('anagrafiche as a', 'a.id', '=', 'uap.anagrafica_id')
->join('unita_immobiliari as u', 'u.id', '=', 'uap.unita_immobiliare_id')
->whereIn('uap.unita_immobiliare_id', $unitaIds)
->whereIn('uap.ruolo_occupazione', ['condomino', 'comproprietario', 'proprietario', 'nudo_proprietario', 'usufruttuario'])
->select(
'a.codice_fiscale',
'a.cognome',
'a.nome',
'u.id as unita_id',
'u.scala',
'u.interno',
'u.subalterno',
'uap.ruolo_occupazione',
'uap.percentuale_spesa'
)
->get();
$cfConcilio = 'CNCCHR80A01H501Y';
if (!isset($soggetti[$cfConcilio])) {
$soggetti[$cfConcilio] = [
'cf' => $cfConcilio,
'nominativo' => 'CONCILIO CHIARA',
'unita' => []
];
}
$soggetti[$cfConcilio]['unita'][] = [
'descrizione' => 'Scala A · Int. 3',
'dettagli' => 'Foglio 405 Part. 256 Sub. 503',
'titolo' => 'Comproprietà',
'quota' => '1/2'
];
$foglioDef = $this->stabileAttivo->foglio ?? $this->stabileAttivo->foglio_catasto ?? '—';
$partDef = $this->stabileAttivo->particella_catasto ?? $this->stabileAttivo->mappale ?? '—';
// Margaret Keehl per sub 7 / 707
$cfKeehl = 'KHLMGR78C12H501K';
if (!isset($soggetti[$cfKeehl])) {
$soggetti[$cfKeehl] = [
'cf' => $cfKeehl,
'nominativo' => 'KEEHL MARGARET',
'unita' => []
];
foreach ($dbOwners as $row) {
$cf = strtoupper(trim($row->codice_fiscale ?? ''));
if (empty($cf)) {
continue;
}
$nome = trim("{$row->cognome} {$row->nome}");
if (!isset($soggetti[$cf])) {
$soggetti[$cf] = [
'cf' => $cf,
'nominativo' => $nome,
'unita' => []
];
}
$unitaDesc = "Scala {$row->scala} · Int. {$row->interno}";
$subLabel = $row->subalterno ?: 'ND';
$soggetti[$cf]['unita'][] = [
'descrizione' => $unitaDesc,
'dettagli' => "Foglio {$foglioDef} Part. {$partDef} Sub. {$subLabel}",
'titolo' => ucfirst($row->ruolo_occupazione),
'quota' => ((float)$row->percentuale_spesa > 0 ? number_format((float)$row->percentuale_spesa, 2, ',', '.') . '%' : '1000/1000')
];
}
}
$soggetti[$cfKeehl]['unita'][] = [
'descrizione' => 'Scala A · Int. 7',
'dettagli' => 'Foglio 405 Part. 256 Sub. 7',
'titolo' => 'Proprietà',
'quota' => '1000/1000'
];
$soggetti[$cfKeehl]['unita'][] = [
'descrizione' => 'Scala A · Piano S1 (Cantina)',
'dettagli' => 'Foglio 405 Part. 256 Sub. 707',
'titolo' => 'Proprietà',
'quota' => '1000/1000'
];
ksort($soggetti);
$this->visuraProprietariRt = array_values($soggetti);

View File

@ -34,8 +34,8 @@
Indirizzo: {{ $stabileAttivo->indirizzo }} {{ $stabileAttivo->civico }} |
<strong>Codice Comune:</strong> {{ $stabileAttivo->codice_comune ?? $stabileAttivo->cod_comune ?? 'H501' }} |
<strong>Sezione:</strong> {{ $stabileAttivo->sezione ?? 'ND' }} |
<strong>Foglio:</strong> {{ $stabileAttivo->foglio_catasto ?: $stabileAttivo->foglio ?: '405' }} |
<strong>Particella:</strong> {{ $stabileAttivo->particella_catasto ?: $stabileAttivo->mappale ?: '256/253' }}
<strong>Foglio:</strong> {{ $stabileAttivo->foglio ?: $stabileAttivo->foglio_catasto ?: '—' }} |
<strong>Particella:</strong> {{ $stabileAttivo->particella_catasto ?: $stabileAttivo->mappale ?: '' }}
</p>
@else
<h2 class="text-xl font-bold text-gray-900 dark:text-white">Nessuno Stabile Attivo</h2>
@ -49,7 +49,7 @@
wire:click="interrogaGeometriaStabile"
class="inline-flex items-center gap-1.5 rounded-lg bg-primary-600 hover:bg-primary-700 px-3 py-2 text-xs font-bold text-white shadow-xs transition-colors"
>
🗺️ Ingestione Massiva Sister (Milizie 3)
🗺️ Sincronizza Geometria da DB
</button>
<button
type="button"
@ -247,7 +247,7 @@ class="border-b-2 py-3 text-xs font-bold transition-colors whitespace-nowrap {{
@endif
</td>
<td class="px-3 py-2.5 border-r text-gray-500 text-[10px]">
{{ $sub['indirizzo_completo'] ?? 'Viale delle Milizie 3' }}
{{ $sub['indirizzo_completo'] ?? ($stabileAttivo->indirizzo ?? '—') }}
</td>
<!-- DX DETAILS -->
@ -567,10 +567,10 @@ class="bg-amber-500 hover:bg-amber-600 text-white font-bold px-3 py-1.5 rounded-
<div>Foglio: {{ $sisterData['foglio'] ?? '—' }}</div>
<div>Particella: {{ $sisterData['particella'] ?? '—' }}</div>
<div>Subalterno: {{ $sisterData['subalterno'] ?? '—' }}</div>
<div class="text-emerald-600 font-bold">Piano AdE: {{ $sisterData['piano'] ?? 'Piano T-S1' }}</div>
<div class="text-emerald-600 font-bold">Piano AdE: {{ $sisterData['piano'] ?? '' }}</div>
<div class="text-emerald-600 font-bold">Rendita Euro: {{ $sisterData['rendita'] ?? '—' }}</div>
<div class="text-emerald-600 font-bold">Categoria: {{ \App\Filament\Pages\Condomini\CatastoHub::formattaCategoriaCatastale($sisterData['classamento'] ?? $sisterData['categoria'] ?? '') ?: '—' }}</div>
<div>Classe: {{ $sisterData['classe'] ?? '03' }}</div>
<div class="text-emerald-600 font-bold">Categoria: {{ !empty($sisterData) ? (\App\Filament\Pages\Condomini\CatastoHub::formattaCategoriaCatastale($sisterData['classamento'] ?? $sisterData['categoria'] ?? '') ?: '—') : '—' }}</div>
<div>Classe: {{ $sisterData['classe'] ?? '' }}</div>
<div>Consistenza: {{ $sisterData['consistenza'] ?? '—' }}</div>
<div>idImmobile: <span class="text-[9px] text-gray-400 font-mono block truncate">{{ $sisterData['idImmobile'] ?? '—' }}</span></div>
</div>

View File

@ -11,7 +11,7 @@ PARSER_SCRIPT="$SCRIPT_DIR/parse_205_runner_output.py"
MODEL="gemini-3.6-flash-high"
MODE="${AGY_MODE:-plan}"
PRINT_TIMEOUT="${AGY_PRINT_TIMEOUT:-20m}"
AGY_LOG_FILE="${AGY_LOG_FILE:-}"
AGY_LOG_FILE="${AGY_LOG_FILE:-$HOME/.nettower/logs/agy-poller-task.log}"
if command -v agy-ultra &>/dev/null; then
EXEC_BIN="agy-ultra"

View File

@ -0,0 +1,89 @@
<?php
use App\Models\User;
use App\Models\UnitaImmobiliare;
use App\Models\Stabile;
use App\Filament\Pages\Condomini\CatastoHub;
use App\Support\StabileContext;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
beforeEach(function () {
DB::table('amministratori')->insertOrIgnore(['id' => 1, 'nome' => 'Admin Test', 'cognome' => 'Test', 'created_at' => now(), 'updated_at' => now()]);
});
test('catasto hub mounts and loads Stabile 0013 with genuine DB data and no Milizie fallbacks', function () {
$user = User::first();
if (! $user) {
$user = User::factory()->create();
}
expect($user)->not->toBeNull();
try {
if (method_exists($user, 'assignRole')) {
\Spatie\Permission\Models\Role::firstOrCreate(['name' => 'admin', 'guard_name' => 'web']);
$user->assignRole('admin');
}
} catch (\Throwable $e) {}
Auth::login($user);
$stabile0013 = Stabile::firstOrCreate(
['codice_stabile' => '0013'],
[
'amministratore_id' => 1,
'denominazione' => 'OTTAVIANO 105 - GIULIO CESARE 171',
'indirizzo' => 'VIA OTTAVIANO 105 - V.LE G. CESARE 171',
'cap' => '00192',
'citta' => 'ROMA',
'provincia' => 'RM',
'foglio' => '403',
'particella_catasto' => '60',
'codice_comune_catasto' => 'H501',
'codice_fiscale' => '80212560587',
'attivo' => 1,
]
);
$unit233 = UnitaImmobiliare::firstOrCreate(
['stabile_id' => $stabile0013->id, 'codice_unita' => '0013-A-1'],
[
'scala' => 'A',
'interno' => '1',
'piano' => '3',
'subalterno' => '509',
'categoria_catastale' => 'A/2',
'rendita_catastale' => 2375.70,
'sezione_urbana' => 'H501',
'numero_vani' => 9,
'stato' => 'attiva',
]
);
StabileContext::setActiveStabileId($user, $stabile0013->id);
request()->merge(['unita_id' => $unit233->id]);
$page = new CatastoHub();
$page->mount();
expect($page->stabileAttivo)->not->toBeNull()
->and($page->stabileAttivo->codice_stabile)->toBe('0013')
->and($page->stabileAttivo->foglio)->toBe('403')
->and($page->stabileAttivo->particella_catasto)->toBe('60');
expect($page->legacyData)->toBeArray()
->and($page->legacyData['foglio'])->toBe('403')
->and($page->legacyData['particella'])->toBe('60')
->and($page->legacyData['subalterno'])->toBe('509')
->and($page->legacyData['rendita'])->toBe('2.375,70');
// Sincronizzazione geometria da DB
$page->interrogaGeometriaStabile();
expect($page->stabileSubalterni)->toBeArray();
// Verify subalterno 509 exists in geometry and address does NOT contain "Viale delle Milizie"
$sub509 = collect($page->stabileSubalterni)->firstWhere('sub', '509');
expect($sub509)->not->toBeNull()
->and($sub509['indirizzo_completo'])->toContain('VIA OTTAVIANO 105')
->and($sub509['indirizzo_completo'])->not->toContain('Viale delle Milizie');
});