441 lines
28 KiB
PHP
Executable File
441 lines
28 KiB
PHP
Executable File
<x-filament-panels::page>
|
|
<div class="mx-auto max-w-7xl space-y-4">
|
|
<div class="flex items-center gap-2 text-lg font-semibold text-gray-900">
|
|
<x-filament::icon icon="heroicon-o-table-cells" class="h-5 w-5 text-primary-600" />
|
|
<span>Tabelle millesimali</span>
|
|
</div>
|
|
|
|
<div class="flex flex-wrap gap-2">
|
|
<button
|
|
type="button"
|
|
wire:click="$set('tab','archivio')"
|
|
class="inline-flex items-center rounded-lg border px-4 py-2 text-sm font-semibold transition
|
|
{{ ($tab ?? 'archivio') === 'archivio' ? 'border-primary-300 bg-primary-50 text-primary-700 shadow-sm' : 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50' }}"
|
|
>
|
|
Archivio
|
|
</button>
|
|
<button
|
|
type="button"
|
|
wire:click="$set('tab','prospetto')"
|
|
class="inline-flex items-center rounded-lg border px-4 py-2 text-sm font-semibold transition
|
|
{{ ($tab ?? 'archivio') === 'prospetto' ? 'border-primary-300 bg-primary-50 text-primary-700 shadow-sm' : 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50' }}"
|
|
>
|
|
Prospetto
|
|
</button>
|
|
<button
|
|
type="button"
|
|
wire:click="$set('tab','voci')"
|
|
class="inline-flex items-center rounded-lg border px-4 py-2 text-sm font-semibold transition
|
|
{{ ($tab ?? 'archivio') === 'voci' ? 'border-primary-300 bg-primary-50 text-primary-700 shadow-sm' : 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50' }}"
|
|
>
|
|
Voci di spesa
|
|
</button>
|
|
</div>
|
|
|
|
@if(($tab ?? 'archivio') === 'archivio')
|
|
<x-filament::section class="p-4!">
|
|
<div class="flex items-start justify-between gap-4 flex-wrap">
|
|
<div class="space-y-1">
|
|
<div class="text-sm font-semibold text-gray-900">Pulizia archivi millesimali</div>
|
|
<div class="text-sm text-gray-600">Usa `Sel.` per marcare gli archivi sporchi. La cancellazione elimina solo le tabelle orfane e blocca quelle ancora collegate a dettagli, voci o altri riferimenti attivi.</div>
|
|
</div>
|
|
<div class="rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800">
|
|
Selezionate: {{ count($selectedArchiveIds ?? []) }}
|
|
</div>
|
|
</div>
|
|
</x-filament::section>
|
|
|
|
{{ $this->table }}
|
|
@elseif(($tab ?? 'archivio') === 'prospetto')
|
|
@php
|
|
$baseUrl = static::getUrl(panel: 'admin-filament');
|
|
$baseProspettoUrl = $baseUrl . '?tab=prospetto';
|
|
@endphp
|
|
|
|
<x-filament::section class="p-4!">
|
|
<div class="mt-1 flex items-start justify-between gap-4 flex-wrap">
|
|
<div class="space-y-1">
|
|
<div class="flex items-center gap-2 text-lg font-semibold text-gray-900">
|
|
<x-filament::icon icon="heroicon-o-chart-pie" class="h-5 w-5 text-primary-600" />
|
|
<span>Prospetto tabelle</span>
|
|
</div>
|
|
<div class="text-sm text-gray-700">Seleziona una tabella per vedere i dettagli (unità, millesimi, percentuali).</div>
|
|
</div>
|
|
</div>
|
|
</x-filament::section>
|
|
|
|
<div class="grid gap-4 lg:grid-cols-3">
|
|
<x-filament::section class="lg:col-span-1">
|
|
<x-slot name="heading">Elenco tabelle</x-slot>
|
|
<x-slot name="description">Stabile attivo</x-slot>
|
|
|
|
@if(empty($tabelle))
|
|
<div class="rounded-xl border border-dashed border-gray-200 bg-white p-6 text-center text-gray-500">Nessuna tabella millesimale.</div>
|
|
@else
|
|
<div class="space-y-2">
|
|
@foreach($tabelle as $t)
|
|
@php $selected = (int) $tabellaId === (int) $t['id']; @endphp
|
|
<a
|
|
href="{{ $baseProspettoUrl }}&tabella_id={{ (int) $t['id'] }}"
|
|
class="block rounded-xl border p-3 transition
|
|
{{ $selected ? 'border-primary-300 bg-primary-50' : 'border-gray-200 bg-white hover:bg-gray-50' }}"
|
|
>
|
|
<div class="flex items-start justify-between gap-3">
|
|
<div>
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<div class="text-sm font-semibold {{ $selected ? 'text-primary-700' : 'text-gray-900' }}">
|
|
{{ $t['codice'] }}
|
|
</div>
|
|
@if(isset($t['nord']) && $t['nord'] !== null)
|
|
<span class="inline-flex items-center rounded-full bg-slate-100 px-2 py-0.5 text-[10px] font-semibold text-slate-700">Ord. {{ $t['nord'] }}</span>
|
|
@endif
|
|
</div>
|
|
<div class="text-xs text-gray-600">{{ $t['nome'] }}</div>
|
|
<div class="mt-1 text-[11px] font-medium text-primary-600">{{ $t['voci_count'] ?? 0 }} voci collegate</div>
|
|
@if(!empty($t['tipo']) || !empty($t['calcolo']))
|
|
<div class="mt-1 text-xs text-gray-500">
|
|
{{ $t['tipo'] ? ('Tipo: ' . $t['tipo']) : '' }}
|
|
{{ $t['tipo'] && $t['calcolo'] ? ' · ' : '' }}
|
|
{{ $t['calcolo'] ? ('Calcolo: ' . $t['calcolo']) : '' }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class="text-right">
|
|
<div class="text-xs text-gray-500">{{ !empty($t['is_consumo']) ? 'A consumo' : 'Bilanciata' }}</div>
|
|
@if(!empty($t['is_consumo']))
|
|
<div class="text-sm font-semibold text-emerald-700">SI</div>
|
|
@else
|
|
<div class="text-sm font-semibold {{ $t['is_bilanciata'] ? 'text-emerald-700' : 'text-amber-700' }}">
|
|
{{ $t['is_bilanciata'] ? 'SI' : 'NO' }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</a>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
</x-filament::section>
|
|
|
|
<x-filament::section class="lg:col-span-2">
|
|
<x-slot name="heading">Dettaglio</x-slot>
|
|
<x-slot name="description">Unità e millesimi</x-slot>
|
|
|
|
@if(!$tabellaInfo)
|
|
<div class="text-sm text-gray-500">Seleziona una tabella per vedere il dettaglio.</div>
|
|
@else
|
|
<div class="space-y-3">
|
|
<div class="flex items-center justify-end gap-2">
|
|
@if(empty($righe))
|
|
<button
|
|
type="button"
|
|
wire:click="inizializzaRighe"
|
|
class="fi-btn fi-btn-color-gray fi-btn-size-sm"
|
|
>
|
|
Inizializza righe
|
|
</button>
|
|
@endif
|
|
<button
|
|
type="button"
|
|
wire:click="saveRighe"
|
|
class="fi-btn fi-btn-color-primary fi-btn-size-sm"
|
|
>
|
|
Salva
|
|
</button>
|
|
</div>
|
|
|
|
<div class="rounded-xl border bg-gray-50 p-3">
|
|
<div class="flex items-start justify-between gap-3 flex-wrap">
|
|
<div>
|
|
<div class="text-sm font-semibold text-gray-900">{{ $tabellaInfo['codice'] }} — {{ $tabellaInfo['nome'] }}</div>
|
|
<div class="text-xs text-gray-600">
|
|
@if(!empty($tabellaInfo['tipo'])) Tipo: {{ $tabellaInfo['tipo'] }} @endif
|
|
@if(!empty($tabellaInfo['tipo']) && !empty($tabellaInfo['calcolo'])) · @endif
|
|
@if(!empty($tabellaInfo['calcolo'])) Calcolo: {{ $tabellaInfo['calcolo'] }} @endif
|
|
</div>
|
|
@if(!empty($tabellaInfo['preventivo']) || !empty($tabellaInfo['consuntivo']))
|
|
<div class="text-xs text-gray-500">
|
|
@if(!empty($tabellaInfo['preventivo'])) Prev: {{ number_format((float) $tabellaInfo['preventivo'], 2, ',', '.') }} @endif
|
|
@if(!empty($tabellaInfo['preventivo']) && !empty($tabellaInfo['consuntivo'])) · @endif
|
|
@if(!empty($tabellaInfo['consuntivo'])) Cons: {{ number_format((float) $tabellaInfo['consuntivo'], 2, ',', '.') }} @endif
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class="text-right">
|
|
<div class="text-xs text-gray-500">Totale</div>
|
|
<div class="text-sm font-semibold text-gray-900">{{ number_format((float) ($tabellaInfo['totale'] ?? 0), 3, ',', '.') }}</div>
|
|
@if(!empty($tabellaInfo['is_consumo']))
|
|
<div class="text-xs text-emerald-700">A consumo</div>
|
|
@else
|
|
<div class="text-xs {{ ($tabellaInfo['is_bilanciata'] ?? false) ? 'text-emerald-700' : 'text-amber-700' }}">
|
|
{{ ($tabellaInfo['is_bilanciata'] ?? false) ? 'Bilanciata (≈1000)' : 'Non bilanciata' }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if(empty($righe))
|
|
<div class="rounded-xl border border-dashed border-gray-200 bg-white p-6 text-center text-gray-500">Nessun dettaglio millesimi.</div>
|
|
@else
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full text-sm">
|
|
<thead>
|
|
<tr class="border-b text-left text-gray-500">
|
|
<th class="py-2">Unità</th>
|
|
<th class="py-2">Posizione</th>
|
|
<th class="py-2">Ruolo</th>
|
|
<th class="py-2 text-right">NORD</th>
|
|
<th class="py-2 text-center">Partecipa</th>
|
|
<th class="py-2 text-right">Millesimi</th>
|
|
<th class="py-2 text-right">%</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y">
|
|
@foreach($righe as $r)
|
|
@php
|
|
$unitaUrl = \App\Filament\Pages\UnitaImmobiliarePage::getUrl(panel: 'admin-filament')
|
|
. '?unita_id=' . (int) $r['unita_id']
|
|
. '&back=' . urlencode(request()->fullUrl());
|
|
@endphp
|
|
<tr>
|
|
<td class="py-2">
|
|
<a href="{{ $unitaUrl }}" class="text-gray-900 font-semibold hover:text-primary-700">
|
|
{{ $r['codice_unita_display'] ?? $r['codice_unita'] ?? ('Unità #' . $r['unita_id']) }}
|
|
</a>
|
|
@if(!empty($r['denominazione']))
|
|
<div class="text-xs text-gray-500">{{ $r['denominazione'] }}</div>
|
|
@endif
|
|
</td>
|
|
<td class="py-2 text-gray-700">
|
|
Pal. {{ $r['palazzina'] ?? '—' }} · Scala {{ $r['scala'] ?? '—' }} · Piano {{ $r['piano'] ?? '—' }} · Int. {{ $r['interno'] ?? '—' }}
|
|
</td>
|
|
<td class="py-2">
|
|
<select wire:model.defer="righe.{{ $loop->index }}.ruolo_legacy" class="rounded-md border-gray-300 py-1 text-xs block w-24">
|
|
<option value="">Nessuno</option>
|
|
<option value="C">Condomino (C)</option>
|
|
<option value="I">Inquilino (I)</option>
|
|
</select>
|
|
</td>
|
|
<td class="py-2 text-right">
|
|
<input
|
|
type="number"
|
|
class="rounded-md border-gray-300 py-1 text-xs block w-16 text-right ml-auto"
|
|
wire:model.defer="righe.{{ $loop->index }}.nord"
|
|
/>
|
|
</td>
|
|
<td class="py-2 text-center">
|
|
<input
|
|
type="checkbox"
|
|
class="fi-checkbox-input"
|
|
wire:model.defer="righe.{{ $loop->index }}.partecipa"
|
|
/>
|
|
</td>
|
|
<td class="py-2 text-right">
|
|
<input
|
|
type="number"
|
|
step="0.001"
|
|
inputmode="decimal"
|
|
class="fi-input block w-28 text-right ml-auto"
|
|
wire:model.defer="righe.{{ $loop->index }}.millesimi"
|
|
/>
|
|
</td>
|
|
<td class="py-2 text-right text-gray-900">{{ number_format((float) $r['percentuale'], 2, ',', '.') }}%</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
</x-filament::section>
|
|
</div>
|
|
@else
|
|
@php
|
|
$baseUrl = static::getUrl(panel: 'admin-filament');
|
|
$baseVociUrl = $baseUrl . '?tab=voci';
|
|
@endphp
|
|
|
|
<div class="grid gap-4 lg:grid-cols-3">
|
|
<x-filament::section class="lg:col-span-1">
|
|
<x-slot name="heading">Elenco tabelle</x-slot>
|
|
<x-slot name="description">Stabile attivo</x-slot>
|
|
|
|
@if(empty($tabelle))
|
|
<div class="rounded-xl border border-dashed border-gray-200 bg-white p-6 text-center text-gray-500">Nessuna tabella millesimale.</div>
|
|
@else
|
|
<div class="space-y-2">
|
|
@foreach($tabelle as $t)
|
|
@php $selected = (int) $tabellaId === (int) $t['id']; @endphp
|
|
<a
|
|
href="{{ $baseVociUrl }}&tabella_id={{ (int) $t['id'] }}"
|
|
class="block rounded-xl border p-3 transition
|
|
{{ $selected ? 'border-primary-300 bg-primary-50' : 'border-gray-200 bg-white hover:bg-gray-50' }}"
|
|
>
|
|
<div class="flex items-start justify-between gap-3">
|
|
<div>
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<div class="text-sm font-semibold {{ $selected ? 'text-primary-700' : 'text-gray-900' }}">
|
|
{{ $t['codice'] }}
|
|
</div>
|
|
@if(isset($t['nord']) && $t['nord'] !== null)
|
|
<span class="inline-flex items-center rounded-full bg-slate-100 px-2 py-0.5 text-[10px] font-semibold text-slate-700">Ord. {{ $t['nord'] }}</span>
|
|
@endif
|
|
</div>
|
|
<div class="text-xs text-gray-600">{{ $t['nome'] }}</div>
|
|
<div class="mt-1 text-[11px] font-medium text-primary-600">{{ $t['voci_count'] ?? 0 }} voci collegate</div>
|
|
@if(!empty($t['tipo']) || !empty($t['calcolo']))
|
|
<div class="mt-1 text-xs text-gray-500">
|
|
{{ $t['tipo'] ? ('Tipo: ' . $t['tipo']) : '' }}
|
|
{{ $t['tipo'] && $t['calcolo'] ? ' · ' : '' }}
|
|
{{ $t['calcolo'] ? ('Calcolo: ' . $t['calcolo']) : '' }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</a>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
</x-filament::section>
|
|
|
|
<x-filament::section class="lg:col-span-2">
|
|
<x-slot name="heading">Voci di spesa</x-slot>
|
|
<x-slot name="description">Collegate alla tabella selezionata</x-slot>
|
|
|
|
@if(empty($vociSpesa))
|
|
<div class="text-sm text-gray-500">Nessuna voce collegata a questa tabella.</div>
|
|
@else
|
|
<div class="flex items-center justify-between gap-3 mb-2 flex-wrap">
|
|
<div class="flex items-center gap-2 flex-wrap">
|
|
<label class="text-xs text-gray-500">Preset ripartizione</label>
|
|
<select
|
|
class="fi-input block text-xs"
|
|
wire:model.live="presetRipartizione"
|
|
>
|
|
<option value="manuale">Manuale</option>
|
|
<option value="confedilizia">Confedilizia</option>
|
|
<option value="custom">Preset salvato</option>
|
|
</select>
|
|
@if(($presetRipartizione ?? 'manuale') === 'custom')
|
|
<select
|
|
class="fi-input block text-xs"
|
|
wire:model.live="presetId"
|
|
>
|
|
<option value="">Seleziona preset…</option>
|
|
@foreach($presetOptions as $id => $label)
|
|
<option value="{{ $id }}">{{ $label }}</option>
|
|
@endforeach
|
|
</select>
|
|
@endif
|
|
<button
|
|
type="button"
|
|
wire:click="applyPresetRipartizione"
|
|
class="fi-btn fi-btn-color-gray fi-btn-size-xs"
|
|
>
|
|
Applica
|
|
</button>
|
|
@if(($presetRipartizione ?? 'manuale') === 'custom' && !empty($presetId))
|
|
<button
|
|
type="button"
|
|
wire:click="updatePreset"
|
|
class="fi-btn fi-btn-color-gray fi-btn-size-xs"
|
|
>
|
|
Aggiorna preset
|
|
</button>
|
|
@endif
|
|
</div>
|
|
<button
|
|
type="button"
|
|
wire:click="saveVociSpesa"
|
|
class="fi-btn fi-btn-color-primary fi-btn-size-sm"
|
|
>
|
|
Salva
|
|
</button>
|
|
</div>
|
|
|
|
@if(!empty($presetDirty))
|
|
<div class="mb-2 text-xs text-amber-600">Preset modificato: aggiorna il preset o salva come nuovo.</div>
|
|
@endif
|
|
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<input
|
|
type="text"
|
|
class="fi-input block text-xs w-60"
|
|
placeholder="Nome nuovo preset"
|
|
wire:model.defer="presetNome"
|
|
/>
|
|
<button
|
|
type="button"
|
|
wire:click="savePresetAsNew"
|
|
class="fi-btn fi-btn-color-gray fi-btn-size-xs"
|
|
>
|
|
Crea preset
|
|
</button>
|
|
</div>
|
|
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full text-xs">
|
|
<thead>
|
|
<tr class="border-b text-left text-gray-500">
|
|
<th class="py-2 w-20">Modifica</th>
|
|
<th class="py-2">Codice</th>
|
|
<th class="py-2">Descrizione</th>
|
|
<th class="py-2">Tipo</th>
|
|
<th class="py-2">Categoria</th>
|
|
<th class="py-2 text-right">% Cond.</th>
|
|
<th class="py-2 text-right">% Inq.</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y">
|
|
@foreach($vociSpesa as $row)
|
|
<tr>
|
|
<td class="py-2">
|
|
<label class="inline-flex items-center gap-2 text-xs text-gray-600">
|
|
<input
|
|
type="checkbox"
|
|
class="fi-checkbox-input"
|
|
wire:model.live="vociSpesa.{{ $loop->index }}.modifica"
|
|
/>
|
|
Abilita
|
|
</label>
|
|
</td>
|
|
<td class="py-2 text-gray-700">{{ $row['codice'] ?? '—' }}</td>
|
|
<td class="py-2">
|
|
<div class="text-xs font-semibold text-gray-900">{{ $row['descrizione'] ?? '—' }}</div>
|
|
</td>
|
|
<td class="py-2 text-gray-700">{{ $row['tipo_gestione'] ?? '—' }}</td>
|
|
<td class="py-2 text-gray-700">{{ $row['categoria'] ?? '—' }}</td>
|
|
<td class="py-2 text-right">
|
|
<input
|
|
type="number"
|
|
step="0.01"
|
|
inputmode="decimal"
|
|
class="fi-input block w-20 text-right"
|
|
wire:model.live="vociSpesa.{{ $loop->index }}.percentuale_condomino"
|
|
@disabled(empty($row['modifica']))
|
|
/>
|
|
</td>
|
|
<td class="py-2 text-right">
|
|
<input
|
|
type="number"
|
|
step="0.01"
|
|
inputmode="decimal"
|
|
class="fi-input block w-20 text-right"
|
|
wire:model.live="vociSpesa.{{ $loop->index }}.percentuale_inquilino"
|
|
@disabled(empty($row['modifica']))
|
|
/>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@endif
|
|
</x-filament::section>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</x-filament-panels::page>
|