fix(millesimi): align comproprietari matching across all stabili (OTTAVIANO B/1), load voc_spe voci di spesa, fix dropdown owner display and canonicalabile code folder paths (task-d095b0e23b)

This commit is contained in:
michele 2026-08-27 16:43:03 +02:00
parent 836e59ea4e
commit 39dd7e515c
3 changed files with 191 additions and 94 deletions

View File

@ -1089,7 +1089,23 @@ protected function loadDettaglioTabella(): void
$dettagliByUnita = $tabella->dettagliMillesimali->keyBy('unita_immobiliare_id'); $dettagliByUnita = $tabella->dettagliMillesimali->keyBy('unita_immobiliare_id');
$periodOccupants = \Illuminate\Support\Facades\DB::table('unita_anagrafica_periodo') $rubricaOccupants = DB::table('rubrica_ruoli as rr')
->join('rubrica_universale as ru', 'ru.id', '=', 'rr.rubrica_id')
->whereIn('rr.unita_immobiliare_id', $tutteUnita->pluck('id')->all())
->where('rr.stabile_id', $this->stabileId)
->where('rr.is_attivo', 1)
->select([
'rr.unita_immobiliare_id',
'rr.ruolo_standard',
'ru.nome_completo',
'ru.ragione_sociale',
'ru.cognome',
'ru.nome',
])
->get()
->groupBy('unita_immobiliare_id');
$periodOccupants = DB::table('unita_anagrafica_periodo')
->join('anagrafiche', 'anagrafiche.id', '=', 'unita_anagrafica_periodo.anagrafica_id') ->join('anagrafiche', 'anagrafiche.id', '=', 'unita_anagrafica_periodo.anagrafica_id')
->whereIn('unita_anagrafica_periodo.unita_immobiliare_id', $tutteUnita->pluck('id')->all()) ->whereIn('unita_anagrafica_periodo.unita_immobiliare_id', $tutteUnita->pluck('id')->all())
->select([ ->select([
@ -1113,27 +1129,58 @@ protected function loadDettaglioTabella(): void
(int) $u->id (int) $u->id
); );
}) })
->map(function ($u) use ($dettagliByUnita, $periodOccupants, $totale) { ->map(function ($u) use ($dettagliByUnita, $rubricaOccupants, $periodOccupants, $totale) {
$d = $dettagliByUnita->get($u->id); $d = $dettagliByUnita->get($u->id);
$millesimi = $d ? (float) ($d->millesimi ?? 0) : 0.0; $millesimi = $d ? (float) ($d->millesimi ?? 0) : 0.0;
$percentuale = $totale > 0 ? ($millesimi / $totale) * 100 : 0; $percentuale = $totale > 0 ? ($millesimi / $totale) * 100 : 0;
$occupants = $periodOccupants->get($u->id, collect()); $rOcc = $rubricaOccupants->get($u->id, collect());
$proprietarioList = $rOcc->filter(fn($p) => in_array(strtolower(trim((string)$p->ruolo_standard)), ['condomino', 'proprietario', 'comproprietario', 'nudo_proprietario', 'usufruttuario'], true));
$proprietarioList = $occupants->filter(fn($p) => strtoupper(trim((string)$p->ruolo_occupazione)) === 'C');
$propNames = []; $propNames = [];
foreach ($proprietarioList as $p) { foreach ($proprietarioList as $p) {
$propNames[] = trim($p->ragione_sociale ?: ($p->cognome . ' ' . $p->nome)); $n = trim((string) ($p->nome_completo ?: ($p->ragione_sociale ?: ($p->cognome . ' ' . $p->nome))));
if ($n !== '' && ! in_array($n, $propNames, true)) {
$propNames[] = $n;
}
} }
if ($propNames === []) {
$occupants = $periodOccupants->get($u->id, collect());
$pList = $occupants->filter(fn($p) => strtoupper(trim((string)$p->ruolo_occupazione)) === 'C');
foreach ($pList as $p) {
$n = trim($p->ragione_sociale ?: ($p->cognome . ' ' . $p->nome));
if ($n !== '' && ! in_array($n, $propNames, true)) {
$propNames[] = $n;
}
}
}
if ($propNames === [] && trim((string)$u->denominazione) !== '') {
$propNames[] = trim((string)$u->denominazione);
}
$proprietarioName = count($propNames) > 0 ? implode(' / ', $propNames) : '—'; $proprietarioName = count($propNames) > 0 ? implode(' / ', $propNames) : '—';
$inquilinoList = $occupants->filter(fn($p) => strtoupper(trim((string)$p->ruolo_occupazione)) === 'I'); $inquilinoList = $rOcc->filter(fn($p) => in_array(strtolower(trim((string)$p->ruolo_standard)), ['inquilino', 'locatario', 'conduttore'], true));
$inqNames = []; $inqNames = [];
foreach ($inquilinoList as $p) { foreach ($inquilinoList as $p) {
$inqNames[] = trim($p->ragione_sociale ?: ($p->cognome . ' ' . $p->nome)); $n = trim((string) ($p->nome_completo ?: ($p->ragione_sociale ?: ($p->cognome . ' ' . $p->nome))));
if ($n !== '' && ! in_array($n, $inqNames, true)) {
$inqNames[] = $n;
}
}
if ($inqNames === []) {
$occupants = $periodOccupants->get($u->id, collect());
$iList = $occupants->filter(fn($p) => strtoupper(trim((string)$p->ruolo_occupazione)) === 'I');
foreach ($iList as $p) {
$n = trim($p->ragione_sociale ?: ($p->cognome . ' ' . $p->nome));
if ($n !== '' && ! in_array($n, $inqNames, true)) {
$inqNames[] = $n;
}
}
} }
$inquilinoName = count($inqNames) > 0 ? implode(' / ', $inqNames) : '—';
$inquilinoName = count($inqNames) > 0 ? implode(' / ', $inqNames) : '—';
$ruolo = $d ? ($d->ruolo_legacy ?: 'C') : 'C'; $ruolo = $d ? ($d->ruolo_legacy ?: 'C') : 'C';
return [ return [
@ -1196,9 +1243,97 @@ protected function loadVociSpesa(): void
]; ];
})->all(); })->all();
if (empty($this->vociSpesa)) {
$this->vociSpesa = $this->loadVociSpesaFromLegacyMdb();
}
$this->presetDirty = false; $this->presetDirty = false;
} }
protected function loadVociSpesaFromLegacyMdb(): array
{
if (! $this->stabileId || ! $this->tabellaId) {
return [];
}
$codStabile = $this->codiceStabile;
if (! $codStabile) {
return [];
}
$tabellaCod = $this->tabellaInfo['codice'] ?? null;
if (! $tabellaCod) {
return [];
}
$gYear = sprintf('%04d', $this->annoGestione ?: 2026);
$candidatePaths = [
"/mnt/gescon-archives/gescon/{$codStabile}/{$gYear}/singolo_anno.mdb",
"/mnt/gescon-archives/gescon/{$codStabile}/0914/singolo_anno.mdb",
"/mnt/gescon-archives/gescon/{$codStabile}/0015/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 {
$proc = new \Symfony\Component\Process\Process(['mdb-export', $mdbPath, 'voc_spe']);
$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 [];
}
$res = [];
while (($data = fgetcsv($stream)) !== false) {
if (count($data) === count($header)) {
$row = array_combine($header, $data);
$tCod = trim((string) ($row['tabella'] ?? ''));
if (strcasecmp($tCod, $tabellaCod) === 0 || strcasecmp($tCod, 'TAB.' . $tabellaCod) === 0) {
$pInq = is_numeric($row['Perc_Inquilino'] ?? null) ? (float) $row['Perc_Inquilino'] : 0.0;
$pProp = is_numeric($row['Perc_proprietario'] ?? null) && (float) $row['Perc_proprietario'] > 0 ? (float) $row['Perc_proprietario'] : (100.0 - $pInq);
$res[] = [
'id' => null,
'codice' => trim((string) ($row['cod'] ?? 'VOC')),
'descrizione' => trim((string) ($row['descriz'] ?? 'Voce di spesa')),
'categoria' => trim((string) ($row['v_ors'] ?? 'O')),
'tipo_gestione' => trim((string) ($row['v_ors'] ?? 'O')),
'percentuale_condomino' => $pProp,
'percentuale_inquilino' => $pInq,
'attiva' => true,
'modifica' => false,
];
}
}
}
fclose($stream);
return $res;
} catch (\Throwable $e) {
return [];
}
}
protected function loadPresetOptions(): void protected function loadPresetOptions(): void
{ {
$this->presetOptions = []; $this->presetOptions = [];

View File

@ -758,68 +758,61 @@ protected function refreshUnitaOptions(): void
} }
$currentNominativoByUnita = []; $currentNominativoByUnita = [];
if (DbSchema::hasTable('rubrica_ruoli') && DbSchema::hasTable('rubrica_universale')) {
try {
$rows = DB::table('rubrica_ruoli as rr')
->join('rubrica_universale as ru', 'rr.rubrica_id', '=', 'ru.id')
->whereIn('rr.unita_immobiliare_id', $unita->pluck('id'))
->where('rr.stabile_id', $this->stabileId)
->where('rr.is_attivo', 1)
->whereIn(DB::raw('LOWER(rr.ruolo_standard)'), ['condomino', 'proprietario', 'comproprietario', 'nudo_proprietario', 'usufruttuario'])
->select('rr.unita_immobiliare_id', 'ru.nome_completo', 'ru.cognome', 'ru.nome', 'ru.ragione_sociale')
->get();
foreach ($rows as $row) {
$unitId = (int) ($row->unita_immobiliare_id ?? 0);
if ($unitId <= 0) {
continue;
}
$name = trim((string) ($row->nome_completo ?: ($row->ragione_sociale ?: ($row->cognome . ' ' . $row->nome))));
if ($name !== '') {
if (! isset($currentNominativoByUnita[$unitId])) {
$currentNominativoByUnita[$unitId] = [];
}
if (! in_array($name, $currentNominativoByUnita[$unitId], true)) {
$currentNominativoByUnita[$unitId][] = $name;
}
}
}
} catch (\Throwable $e) {
// Ignore fallback error
}
}
if (DbSchema::hasTable('unita_immobiliare_nominativi')) { if (DbSchema::hasTable('unita_immobiliare_nominativi')) {
try { try {
$today = now()->toDateString(); $rows = DB::table('unita_immobiliare_nominativi')
$rows = DB::table('unita_immobiliare_nominativi')
->whereIn('unita_immobiliare_id', $unita->pluck('id')) ->whereIn('unita_immobiliare_id', $unita->pluck('id'))
->where(function ($query) use ($today): void { ->whereIn('ruolo', ['C', 'P', 'CP'])
$query->whereNull('data_inizio')->orWhere('data_inizio', '<=', $today);
})
->where(function ($query) use ($today): void {
$query->whereNull('data_fine')->orWhere('data_fine', '>=', $today);
})
->orderByRaw("CASE WHEN ruolo = 'C' THEN 0 ELSE 1 END")
->orderByDesc('data_inizio')
->orderByDesc('id') ->orderByDesc('id')
->get(['unita_immobiliare_id', 'nominativo']); ->get(['unita_immobiliare_id', 'nominativo']);
foreach ($rows as $row) { foreach ($rows as $row) {
$unitId = (int) ($row->unita_immobiliare_id ?? 0); $unitId = (int) ($row->unita_immobiliare_id ?? 0);
if ($unitId <= 0 || isset($currentNominativoByUnita[$unitId])) { if ($unitId <= 0) {
continue; continue;
} }
$name = trim((string) ($row->nominativo ?? '')); $name = trim((string) ($row->nominativo ?? ''));
if ($name !== '') { if ($name !== '') {
$currentNominativoByUnita[$unitId] = $name; if (! isset($currentNominativoByUnita[$unitId])) {
} $currentNominativoByUnita[$unitId] = [];
} }
} catch (\Throwable $e) { if (! in_array($name, $currentNominativoByUnita[$unitId], true)) {
$currentNominativoByUnita = []; $currentNominativoByUnita[$unitId][] = $name;
} }
}
if (DbSchema::hasTable('persone_unita_relazioni') && DbSchema::hasTable('persone')) {
try {
$today = now()->toDateString();
$rows = DB::table('persone_unita_relazioni as pur')
->join('persone as p', 'pur.persona_id', '=', 'p.id')
->whereIn('pur.unita_id', $unita->pluck('id'))
->where(function ($query) use ($today): void {
$query->whereNull('pur.data_inizio')->orWhere('pur.data_inizio', '<=', $today);
})
->where(function ($query) use ($today): void {
$query->whereNull('pur.data_fine')->orWhere('pur.data_fine', '>=', $today);
})
->where('pur.attivo', 1)
->orderByRaw("CASE WHEN pur.tipo_relazione = 'proprietario' THEN 0 ELSE 1 END")
->orderByDesc('pur.data_inizio')
->select('pur.unita_id', 'p.cognome', 'p.nome', 'p.ragione_sociale')
->get();
foreach ($rows as $row) {
$unitId = (int) ($row->unita_id ?? 0);
if ($unitId <= 0 || isset($currentNominativoByUnita[$unitId])) {
continue;
}
$name = trim((string) ($row->ragione_sociale ?? ''));
if ($name === '') {
$name = trim((string) ($row->cognome ?? '') . ' ' . (string) ($row->nome ?? ''));
}
if ($name !== '') {
$currentNominativoByUnita[$unitId] = $name;
} }
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
@ -827,42 +820,11 @@ protected function refreshUnitaOptions(): void
} }
} }
if (DbSchema::hasTable('rubrica_ruoli') && DbSchema::hasTable('rubrica_universale')) { $formattedNominativoByUnita = [];
try { foreach ($currentNominativoByUnita as $uId => $namesArr) {
$today = now()->toDateString(); $formattedNominativoByUnita[$uId] = is_array($namesArr) ? implode(' / ', $namesArr) : (string) $namesArr;
$rows = DB::table('rubrica_ruoli as rr')
->join('rubrica_universale as ru', 'rr.rubrica_id', '=', 'ru.id')
->whereIn('rr.unita_id', $unita->pluck('id'))
->where(function ($query) use ($today): void {
$query->whereNull('rr.data_inizio')->orWhere('rr.data_inizio', '<=', $today);
})
->where(function ($query) use ($today): void {
$query->whereNull('rr.data_fine')->orWhere('rr.data_fine', '>=', $today);
})
->where('rr.attivo', 1)
->orderByRaw("CASE WHEN rr.ruolo = 'proprietario' THEN 0 ELSE 1 END")
->orderByDesc('rr.data_inizio')
->select('rr.unita_id', 'ru.cognome', 'ru.nome', 'ru.ragione_sociale')
->get();
foreach ($rows as $row) {
$unitId = (int) ($row->unita_id ?? 0);
if ($unitId <= 0 || isset($currentNominativoByUnita[$unitId])) {
continue;
}
$name = trim((string) ($row->ragione_sociale ?? ''));
if ($name === '') {
$name = trim((string) ($row->cognome ?? '') . ' ' . (string) ($row->nome ?? ''));
}
if ($name !== '') {
$currentNominativoByUnita[$unitId] = $name;
}
}
} catch (\Throwable $e) {
// Ignore fallback error
}
} }
$currentNominativoByUnita = $formattedNominativoByUnita;
$sorted = $unita->sort(function (UnitaImmobiliare $a, UnitaImmobiliare $b): int { $sorted = $unita->sort(function (UnitaImmobiliare $a, UnitaImmobiliare $b): int {
$pa = (int) ($a->palazzina_id ?? 0); $pa = (int) ($a->palazzina_id ?? 0);

View File

@ -42,7 +42,7 @@ public function stabileCode(Stabile | string $stabile): string
return $code; return $code;
} }
$code = trim((string) ($stabile->codice_univoco ?: $stabile->codice_stabile ?: '')); $code = trim((string) ($stabile->codice_stabile ?: $stabile->codice_univoco ?: ''));
if ($code === '') { if ($code === '') {
throw new InvalidArgumentException('Stabile senza codice archivio canonico.'); throw new InvalidArgumentException('Stabile senza codice archivio canonico.');