fix(unita): enforce deterministic timeline gestion spans, import tabelle/dett_tab millesimal tables from legacy MDB and document Commercialista rule (task-d095b0e23b)

This commit is contained in:
michele 2026-08-27 14:08:01 +02:00
parent 81734c7eea
commit f345abc3ba
3 changed files with 185 additions and 39 deletions

View File

@ -1377,13 +1377,8 @@ private function populateNominativiStoriciFallback(): void
$grouped = [];
foreach ($mirrorRows as $m) {
$yearStr = match ($m->source_year) {
'0004', '0914' => '2026',
'0003', '0913' => '2025',
'0001', '0912' => '2024',
default => 'Gestione ' . $m->source_year,
};
$yLabel = $yearStr . ' (' . $m->source_year . ')';
$gCode = (string) $m->source_year;
$yLabel = 'Gestione ' . $gCode;
$owner = trim((string) ($m->nom_cond ?? $m->proprietario ?? ''));
if ($owner !== '') {
@ -1394,17 +1389,15 @@ private function populateNominativiStoriciFallback(): void
'nome' => $owner,
'codice_fiscale'=> ! empty($m->codice_fiscale) ? $m->codice_fiscale : null,
'tipo_relazione'=> 'proprietario',
'gestioni' => [$yLabel],
'years' => [$yearStr],
'gestioni' => [$gCode],
'quota' => ($m->source_table === 'comproprietari' || (float)($m->perc_diritto_reale ?? 0) > 0) ? (float)($m->perc_diritto_reale ?? 50) : 100.0,
'quota_label' => ($m->source_table === 'comproprietari' || (float)($m->perc_diritto_reale ?? 0) > 0) ? number_format((float)($m->perc_diritto_reale ?? 50), 2, ',', '.') : '100,00',
'attivo' => false,
'fonte' => $m->source_table === 'comproprietari' ? 'Legacy comproprietari' : 'Archivio storico legacy',
];
} else {
if (! in_array($yLabel, $grouped[$key]['gestioni'])) {
$grouped[$key]['gestioni'][] = $yLabel;
$grouped[$key]['years'][] = $yearStr;
if (! in_array($gCode, $grouped[$key]['gestioni'])) {
$grouped[$key]['gestioni'][] = $gCode;
}
}
}
@ -1419,26 +1412,25 @@ private function populateNominativiStoriciFallback(): void
'nome' => $inquil,
'codice_fiscale'=> $inqCf !== '' ? $inqCf : null,
'tipo_relazione'=> 'inquilino',
'gestioni' => [$yLabel],
'years' => [$yearStr],
'gestioni' => [$gCode],
'quota' => 100.0,
'quota_label' => '100,00',
'attivo' => false,
'fonte' => 'Archivio storico legacy',
];
} else {
if (! in_array($yLabel, $grouped[$key]['gestioni'])) {
$grouped[$key]['gestioni'][] = $yLabel;
$grouped[$key]['years'][] = $yearStr;
if (! in_array($gCode, $grouped[$key]['gestioni'])) {
$grouped[$key]['gestioni'][] = $gCode;
}
}
}
}
foreach ($grouped as $item) {
$gSpan = count($item['gestioni']) > 1
? implode(' → ', [$item['years'][0], end($item['years'])]) . ' (Gestioni ' . implode(', ', array_map(fn($g) => explode(' ', $g)[1] ?? $g, $item['gestioni'])) . ')'
: $item['gestioni'][0];
$gList = $item['gestioni'];
$gSpan = count($gList) > 1
? 'Gestioni ' . implode(', ', $gList) . ' (Titolare storico)'
: 'Gestione ' . $gList[0];
$fallbackList[] = [
'persona_id' => $item['persona_id'],
@ -2814,6 +2806,128 @@ protected function hydrateMillesimi(): void
})
->values()
->all();
if (empty($this->millesimiPerTabella)) {
$this->millesimiPerTabella = $this->loadMillesimiFromLegacyMdb();
}
}
protected function loadMillesimiFromLegacyMdb(): array
{
if (! $this->unita || ! $this->unita->stabile) {
return [];
}
$codStabile = trim((string) $this->unita->stabile->codice_stabile);
$scala = trim((string) $this->unita->scala);
$interno = trim((string) $this->unita->interno);
if ($codStabile === '' || $interno === '') {
return [];
}
$idCond = null;
if ($this->hasImportConnection() && DbSchema::connection('gescon_import')->hasTable('condomin_mirror')) {
$q = DB::connection('gescon_import')->table('condomin_mirror')
->where('cod_stabile', $codStabile)
->where('interno', $interno);
if ($scala !== '') {
$q->where('scala', $scala);
}
$mRow = $q->first();
if ($mRow) {
$idCond = $mRow->id_cond ?: $mRow->cod_cond;
}
}
if (! $idCond) {
return [];
}
$candidatePaths = [
"/mnt/gescon-archives/gescon/{$codStabile}/0914/singolo_anno.mdb",
"/mnt/gescon-archives/gescon/{$codStabile}/0004/singolo_anno.mdb",
];
$mdbPath = null;
foreach ($candidatePaths as $p) {
if (file_exists($p) && is_readable($p)) {
$mdbPath = $p;
break;
}
}
if (! $mdbPath) {
return [];
}
try {
$tabRows = $this->extractMdbTableRows($mdbPath, 'tabelle');
$dettRows = $this->extractMdbTableRows($mdbPath, 'dett_tab');
$tabMap = [];
foreach ($tabRows as $tr) {
$c = isset($tr['cod_tab']) ? (string) $tr['cod_tab'] : null;
if ($c) {
$tabMap[$c] = isset($tr['des_tab']) && trim((string) $tr['des_tab']) !== '' ? (string) $tr['des_tab'] : $c;
}
}
$res = [];
foreach ($dettRows as $dr) {
$drIdCond = isset($dr['id_cond']) ? (string) $dr['id_cond'] : null;
$drInq = isset($dr['cond_inquil']) ? (string) $dr['cond_inquil'] : 'C';
$mm = isset($dr['mm']) && is_numeric($dr['mm']) ? (float) $dr['mm'] : 0.0;
if ($drIdCond === (string) $idCond && strtoupper($drInq) === 'C' && $mm > 0) {
$cTab = isset($dr['cod_tab']) ? (string) $dr['cod_tab'] : 'TAB';
$nomeTab = $tabMap[$cTab] ?? $cTab;
$res[] = [
'tipo' => str_contains(strtolower($cTab), 'risc') ? 'riscaldamento' : (str_contains(strtolower($cTab), 'stra') ? 'straordinaria' : 'ordinaria'),
'gruppo' => $this->mapTipoToGruppo($cTab),
'id' => null,
'codice' => $cTab,
'nome' => $nomeTab,
'millesimi' => $mm,
];
}
}
return $res;
} catch (\Throwable $e) {
return [];
}
}
private function extractMdbTableRows(string $mdbPath, string $tableName): array
{
$proc = new \Symfony\Component\Process\Process(['mdb-export', $mdbPath, $tableName]);
$proc->run();
if (! $proc->isSuccessful()) {
return [];
}
$csvContent = $proc->getOutput();
$stream = fopen('php://memory', 'r+');
fwrite($stream, $csvContent);
rewind($stream);
$header = fgetcsv($stream);
if (! $header) {
fclose($stream);
return [];
}
$rows = [];
while (($data = fgetcsv($stream)) !== false) {
if (count($data) === count($header)) {
$rows[] = array_combine($header, $data);
}
}
fclose($stream);
return $rows;
}
public function setMillesimiGruppo(string $gruppo): void
@ -3326,7 +3440,7 @@ protected function hydrateRelazioni(): void
'ruolo_label' => 'Proprietario 100%',
'quota' => 100.0,
'quota_label' => '100,00',
'data_inizio' => '01/01/' . $activeAnno,
'data_inizio' => 'Gestione ' . $activeAnno,
'data_fine' => null,
'attivo' => true,
'fonte' => 'legacy_condomin_mirror',

View File

@ -1,36 +1,55 @@
# ASCII Wireframe - Scheda Unità Immobiliare
# ASCII Wireframe - Scheda Unità Immobiliare (Timeline e Tabelle Millesimali)
URL: `http://192.168.0.205:8000/admin-filament/unita-immobiliare`
URL: `http://192.168.0.205:8000/admin-filament/unita-immobiliare?unita_id=321`
```text
+-----------------------------------------------------------------------------------------------------------------------+
| NETGESCON · CENTRO STELLA [ Utente: Cecilia Tordini v ] |
+-----------------------------------------------------------------------------------------------------------------------+
| UNITA IMMOBILIARE |
| UNITA IMMOBILIARI > SCHEDA UNITA IMMOBILIARE (Scala B - Int. 4 - Piano 2) |
| |
| BARRA DI SELEZIONE E TIMELINE |
| [<- Prec.] Unità: [ Scala A - Int. 11 (Piano 4 - Abitazione) [A-11] BENEDETTO DANIELA v ] [Succ. ->] |
| |
| [ Riepilogo ] [ Tabelle Millesimali ] [ Catasto ] [ Economico ] [ Estratto Conto ] [ Acqua ] |
| Stabile: [ 0016 - GERMANICO 96 v ] Anno Gestione: [ 0914 (Anno 2026 - Attiva) v ] |
| Barra selezione unità: [<- Prec.] Unità: [ Scala B - Int. 4 (ID 321) v ] [Succ. ->] |
| |
| +-----------------------------------------------------------------------------------------------------------------+ |
| | DETTAGLI UNITA IMMOBILIARE & ANAGRAFICA ATTIVA | |
| +-----------------------------------------------------------------------------------------------------------------+ |
| | Codice: 0021-A-11 | Scala: A | Piano: 4 | Interno: 11 | Tipo: Abitazione | |
| | Proprietario Attivo: BENEDETTO DANIELA (Quota 100,00%) - Dal 09/06/2026 | |
| | TAB: [ Riepilogo (Active) ] [ Tabelle Millesimali ] [ Estratto Conto ] [ Ripartizione Spese ] [ Dati Catasto ] | |
| +-----------------------------------------------------------------------------------------------------------------+ |
| |
| +-----------------------------------------------------------------------------------------------------------------+ |
| | TIMELINE SUBENTRI E GESTIONI ANTERIORE (Cronistoria Titolari) | |
| | TITOLARI E COMPROPRIETARI IN CARICA (Gestione 0914 - Anno 2026) | |
| +-----------------------------------------------------------------------------------------------------------------+ |
| | [ Da 09/06/2026 ] BENEDETTO DANIELA (Proprietario 100%) · Fonte: Anagrafica consolidata | |
| | Dettaglio: CF BNDDNL86M58H224O | |
| | - BARONE MICHELE | Proprietario | Quota: 50,00% | CF: BRNMHL69M11H501K | [Apri Anagrafica] | |
| | - FRANCIONI CLAUDIA / | Comproprietario | Quota: 50,00% | CF: FRNCLD49P51H501B | [Apri Anagrafica] | |
| | [Email: cecilia.tordini@gmail.com] | |
| +-----------------------------------------------------------------------------------------------------------------+ |
| |
| +-----------------------------------------------------------------------------------------------------------------+ |
| | TIMELINE SUBENTRI E GESTIONI ANTERIORI (Determinismo Certificato Legacy) | |
| +-----------------------------------------------------------------------------------------------------------------+ |
| | Cronistoria titolari ed avvicendamenti per questa unità immobiliare tra le diverse gestioni | |
| | | |
| | [ Fino a 08/06/2026 ] ATER C/O UFFICIO CONDOMINI PASSIVI (Proprietario 100%) · Fonte: Legacy condomin | |
| | Dettaglio: CF 01234567890 · Gestione 2026 (0004) | |
| | [•] Proprietario: BARONE MICHELE (Quota: 50,00%) | |
| | Intervallo: Gestioni 0004, 0005, 0909, 0912, 0913, 0914 (Titolare storico consolidato) | |
| | | |
| | [ Gestione 0003 ] ATER C/O UFFICIO CONDOMINI PASSIVI (Proprietario 100%) · Anno 2025 | |
| | [ Gestione 0001 ] ATER C/O UFFICIO CONDOMINI PASSIVI (Proprietario 100%) · Anno 2024 | |
| | [•] Comproprietario: FRANCIONI CLAUDIA (Quota: 50,00%) | |
| | Intervallo: Gestioni 0004, 0005, 0909, 0912, 0913, 0914 (Legacy comproprietari) | |
| | CF: FRNCLD49P51H501B · Email: cecilia.tordini@gmail.com | |
| +-----------------------------------------------------------------------------------------------------------------+ |
| |
| +-----------------------------------------------------------------------------------------------------------------+ |
| | TABELLE MILLESIMALI E ATTRIBUZIONE QUOTE (Da singolo_anno.mdb: tabelle & dett_tab) | |
| +-----------------------------------------------------------------------------------------------------------------+ |
| | CODICE TABELLA | NOME TABELLA / CATEGORIA | VALORE MILLESIMI (mm) | SOGGETTO ATTRIBUTO | |
| +-----------------+------------------------------------+-----------------------+------------------------------------+ |
| | TAB.A | Tabella Millesimale A (Proprietà) | 15,2780 mm | Condomino (BARONE MICHELE/FRANCIONI| |
| | TAB.BB | Tabella Millesimale Scala B | 21,6060 mm | Condomino (BARONE MICHELE/FRANCIONI| |
| | TABA.P | Tabella Millesimale Proprietà P. | 15,2780 mm | Condomino (BARONE MICHELE/FRANCIONI| |
| | DET_18 | Detrazione Fiscali 18% | 15,2780 mm | Condomino (BARONE MICHELE/FRANCIONI| |
| | REND | Rendita Fabbricati | 15,2780 mm | Condomino (BARONE MICHELE/FRANCIONI| |
| | RI.ASS | Ripartizione Assicurazione | 15,2780 mm | Condomino (BARONE MICHELE/FRANCIONI| |
| | ANTICI | Anticipi Condominiali | 15,2780 mm | Condomino (BARONE MICHELE/FRANCIONI| |
| | L02.ST | Lavori Straordinari L02 | 15,2780 mm | Condomino (BARONE MICHELE/FRANCIONI| |
| | L03.ST | Lavori Straordinari L03 | 15,2780 mm | Condomino (BARONE MICHELE/FRANCIONI| |
| +-----------------------------------------------------------------------------------------------------------------+ |
+-----------------------------------------------------------------------------------------------------------------------+
```

View File

@ -27,3 +27,16 @@ ## 4. Dati Catastali Stabile (`Stabili.mdb` - Prefisso `AC_`)
- Negli stabili articolati o supercondomini (es. Milizie 3 / 0021 con Palazzina D in altra sezione/particella), la configurazione avanzata dello stabile conserva l'array `altri_riferimenti_catastali`.
- Ciascun riferimento sezionale o di palazzina specifica `descrizione`, `foglio`, `particella`, `sezione`, `subalterno`, consentendo l'aggancio diretto alle rispettive unità immobiliari.
## 5. Tabelle Millesimali da Legacy (`tabelle` e `dett_tab` in `singolo_anno.mdb`)
- **Tabella definizioni**: `tabelle` con campi `cod_tab`, `des_tab`, `totale`.
- **Tabella dettagli ed attribuzione**: `dett_tab` con campi `cod_tab`, `id_cond`, `cond_inquil`, `mm`.
- **Regola di collegamento**:
- `tabelle.cod_tab` si lega a `dett_tab.cod_tab`.
- `dett_tab.id_cond` si lega tassativamente a `condomin.id_cond` dell'unità immobiliare per quella specifica gestione.
- `dett_tab.cond_inquil` (`'C'` Condomino, `'I'` Inquilino) attribuisce i millesimi (`mm`) al soggetto titolare.
## 6. Principio di Deterministicità Assoluta (Regola Commercialista)
- **Divieto di Fallback o Date Fittizie**: È tassativamente vietato inventare date fittizie (es. `01/01/2000` o `01/01/2026` ripetute) o stimare ruoli non presenti negli archivi MDB.
- **Trattamento dati mancanti**: Se una relazione o una data di decorrenza non è presente negli archivi consolidati, il sistema deve riportare unicamente le gestioni storiche certe (es. `Gestioni 0004, 0005, 0909, 0912, 0913, 0914`) oppure restituire `BLOCCO_DATI`. I dati devono essere certificabili e certi al 100%.