netgescon-day0/app/Filament/Pages/Condomini/Components/GestioniStabileTable.php

708 lines
29 KiB
PHP
Executable File

<?php
namespace App\Filament\Pages\Condomini\Components;
use App\Models\GestioneContabile;
use App\Models\User;
use App\Support\StabileContext;
use Filament\Actions\Action;
use Filament\Actions\BulkAction;
use Filament\Actions\Concerns\InteractsWithActions;
use Filament\Actions\Contracts\HasActions;
use Filament\Forms\Components\CheckboxList;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Support\Contracts\TranslatableContentDriver;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\TextInputColumn;
use Filament\Tables\Columns\DatePickerColumn;
use Filament\Tables\Columns\SelectColumn;
use Filament\Tables\Columns\ToggleColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Livewire\Component;
class GestioniStabileTable extends Component implements HasForms, HasTable, HasActions
{
use InteractsWithTable;
use InteractsWithForms;
use InteractsWithActions;
protected $listeners = [
'refresh-gestioni-stabile' => '$refresh',
];
public string $scope = 'aperte';
private bool $legacySyncAttempted = false;
public ?string $tipo = null;
public function mount(): void
{
$this->mountInteractsWithTable();
}
/**
* Query principale tabella gestioni.
* Applica ambito (aperte/archivio), tipo gestione e filtro riscaldamento.
*/
protected function getTableQuery(): Builder
{
$user = Auth::user();
if (! $user instanceof User) {
return GestioneContabile::query()->whereRaw('1 = 0');
}
$activeStabileId = StabileContext::resolveActiveStabileId($user);
if (! $activeStabileId) {
return GestioneContabile::query()->whereRaw('1 = 0');
}
$stabile = \App\Models\Stabile::query()
->select('id', 'configurazione_avanzata')
->find($activeStabileId);
$this->syncLegacyOrdinarieForStabile($stabile);
$mostraRiscaldamento = (bool) data_get($stabile?->configurazione_avanzata, 'mostra_riscaldamento', false);
$query = GestioneContabile::query()
->where('stabile_id', $activeStabileId);
$tenantId = trim((string) ($user->tenant_id ?? ''));
if ($tenantId !== '') {
$query->where(function (Builder $builder) use ($tenantId): void {
$builder->where('tenant_id', $tenantId)
->orWhere('tenant_id', '00000000');
});
}
if ($this->scope === 'archivio') {
$query->whereIn('stato', ['chiusa', 'consolidata']);
} elseif ($this->scope === 'aperte') {
$query->aperte();
}
if ($this->tipo) {
$query->where('tipo_gestione', $this->tipo);
}
if (! $mostraRiscaldamento) {
$query->where('tipo_gestione', '!=', 'riscaldamento');
}
$query->whereRaw("gestioni_contabili.id = (
select gc2.id
from gestioni_contabili gc2
where gc2.tenant_id = gestioni_contabili.tenant_id
and gc2.stabile_id = gestioni_contabili.stabile_id
and gc2.anno_gestione = gestioni_contabili.anno_gestione
and gc2.tipo_gestione = gestioni_contabili.tipo_gestione
and (
(gc2.numero_straordinaria is null and gestioni_contabili.numero_straordinaria is null)
or gc2.numero_straordinaria = gestioni_contabili.numero_straordinaria
)
order by
case when gc2.denominazione like '%/%' then 1 else 0 end desc,
gc2.data_fine desc,
gc2.id desc
limit 1
)");
return $query;
}
private function syncLegacyOrdinarieForStabile( ? \App\Models\Stabile $stabile) : void
{
if ($this->legacySyncAttempted) {
return;
}
$this->legacySyncAttempted = true;
if (! $stabile) {
return;
}
if (! Schema::connection('gescon_import')->hasTable('gestioni_annuali')) {
return;
}
$legacyCode = trim((string) ($stabile->codice_stabile ?? ''));
if ($legacyCode === '') {
return;
}
$legacyRows = DB::connection('gescon_import')
->table('gestioni_annuali')
->where('cod_stabile', $legacyCode)
->orderByDesc('anno_ordinario')
->get(['anno_ordinario', 'anno_riscaldamento', 'descrizione', 'ordinaria_dal', 'ordinaria_al', 'riscaldamento_dal', 'riscaldamento_al', 'cartella']);
if ($legacyRows->isEmpty()) {
return;
}
$tenantId = GestioneContabile::query()
->where('stabile_id', (int) $stabile->id)
->value('tenant_id');
if (! is_string($tenantId) || trim($tenantId) === '') {
$tenantId = (string) (Auth::user()?->tenant_id ?? 'default');
}
$mostraRiscaldamento = (bool) data_get($stabile->configurazione_avanzata, 'mostra_riscaldamento', false);
$latestYear = (int) $legacyRows
->map(fn($row) => is_numeric($row->anno_ordinario ?? null) ? (int) $row->anno_ordinario : null)
->filter()
->max();
foreach ($legacyRows as $row) {
$descrizione = trim((string) ($row->descrizione ?? $row->anno_ordinario ?? ''));
$labelOrd = $descrizione !== '' ? preg_replace('/\s+/', '', $descrizione) : trim((string) ($row->anno_ordinario ?? ''));
$labelRisc = trim((string) ($row->anno_riscaldamento ?? '')) ?: $labelOrd;
$year = is_numeric($row->anno_ordinario ?? null) ? (int) $row->anno_ordinario : null;
if (! $year) {
continue;
}
$records = [
[
'tipo' => 'ordinaria',
'inizio' => $row->ordinaria_dal ?: sprintf('%04d-01-01', $year),
'fine' => $row->ordinaria_al ?: sprintf('%04d-12-31', $year),
'prefix' => 'O' . $year,
'label' => $labelOrd,
],
];
if ($mostraRiscaldamento) {
$records[] = [
'tipo' => 'riscaldamento',
'inizio' => $row->riscaldamento_dal ?: ($row->ordinaria_dal ?: sprintf('%04d-01-01', $year)),
'fine' => $row->riscaldamento_al ?: ($row->ordinaria_al ?: sprintf('%04d-12-31', $year)),
'prefix' => 'R' . $year,
'label' => $labelRisc,
];
}
foreach ($records as $recordMeta) {
$lbl = $recordMeta['label'];
GestioneContabile::query()->updateOrCreate(
[
'tenant_id' => $tenantId,
'stabile_id' => (int) $stabile->id,
'anno_gestione' => $year,
'tipo_gestione' => $recordMeta['tipo'],
'numero_straordinaria' => null,
],
[
'denominazione' => Str::headline($recordMeta['tipo']) . ' ' . ($lbl !== '' ? $lbl : $year),
'data_inizio' => $recordMeta['inizio'],
'data_fine' => $recordMeta['fine'],
'stato' => $year === $latestYear ? 'aperta' : 'chiusa',
'protocollo_prefix' => $recordMeta['prefix'],
'gestione_attiva' => $year === $latestYear,
'codice_archivio_legacy' => 'legacy:gestioni_annuali:' . (trim((string) ($row->cartella ?? '')) ?: $year),
]
);
}
}
}
public function table(Table $table): Table
{
return $table
->striped()
->defaultSort('anno_gestione', 'desc')
->recordUrl(fn(GestioneContabile $record): string => \App\Filament\Pages\Condomini\StabilePage::getUrl(panel: 'admin-filament') . '?tab=gestioni&gestioni_tab=periodi&gestione_id=' . $record->id)
->headerActions([
Action::make('nuova_gestione')
->label('Nuova gestione')
->icon('heroicon-o-plus')
->modalHeading('Nuova gestione')
->form([
\Filament\Forms\Components\Select::make('tipo_gestione')
->label('Tipo')
->options([
'ordinaria' => 'Ordinaria',
'riscaldamento' => 'Riscaldamento',
'straordinaria' => 'Straordinaria',
])
->required(),
\Filament\Forms\Components\TextInput::make('anno_gestione')
->label('Anno')
->numeric()
->required(),
\Filament\Forms\Components\TextInput::make('denominazione')
->label('Denominazione')
->maxLength(255),
\Filament\Forms\Components\DatePicker::make('data_inizio')
->label('Data inizio'),
\Filament\Forms\Components\DatePicker::make('data_fine')
->label('Data fine'),
CheckboxList::make('mesi')
->label('Mesi scadenza rate')
->options([
1 => 'Gen',
2 => 'Feb',
3 => 'Mar',
4 => 'Apr',
5 => 'Mag',
6 => 'Giu',
7 => 'Lug',
8 => 'Ago',
9 => 'Set',
10 => 'Ott',
11 => 'Nov',
12 => 'Dic',
])
->columns(6),
])
->action(function (array $data): void {
$user = Auth::user();
if (! $user instanceof User) {
return;
}
$stabileId = StabileContext::resolveActiveStabileId($user);
if (! $stabileId) {
return;
}
$tenantId = GestioneContabile::query()
->where('stabile_id', $stabileId)
->value('tenant_id');
if (! is_string($tenantId) || trim($tenantId) === '') {
$tenantId = (string) ($user->tenant_id ?? 'default');
}
$gestione = new GestioneContabile();
$gestione->tenant_id = $tenantId;
$gestione->stabile_id = $stabileId;
$gestione->anno_gestione = (int) ($data['anno_gestione'] ?? now()->year);
$gestione->tipo_gestione = (string) ($data['tipo_gestione'] ?? 'ordinaria');
$gestione->denominazione = $data['denominazione'] ?? null;
$gestione->data_inizio = $data['data_inizio'] ?? null;
$gestione->data_fine = $data['data_fine'] ?? null;
$gestione->stato = 'aperta';
$gestione->gestione_attiva = true;
$mesi = array_values(array_unique(array_map('intval', (array) ($data['mesi'] ?? []))));
sort($mesi);
$field = match ($gestione->tipo_gestione) {
'ordinaria' => 'mesi_rate_ordinaria',
'riscaldamento' => 'mesi_rate_riscaldamento',
'straordinaria' => 'mesi_rate_straordinaria',
default => 'mesi_rate_ordinaria',
};
$gestione->{$field} = $mesi;
$gestione->save();
}),
Action::make('duplica_gestione')
->label('Duplica gestione')
->icon('heroicon-o-squares-plus')
->modalHeading('Duplica gestione')
->form([
\Filament\Forms\Components\Select::make('gestione_id')
->label('Gestione da duplicare')
->options(fn() => GestioneContabile::query()
->where('stabile_id', StabileContext::resolveActiveStabileId(Auth::user()))
->orderByDesc('anno_gestione')
->get()
->mapWithKeys(fn(GestioneContabile $g) => [$g->id => ($g->anno_gestione . ' · ' . ($g->denominazione ?: $g->tipo_gestione))]))
->searchable()
->required(),
\Filament\Forms\Components\TextInput::make('anno_gestione')
->label('Anno nuovo')
->numeric()
->required(),
])
->action(function (array $data): void {
$gestione = GestioneContabile::query()->find((int) ($data['gestione_id'] ?? 0));
if (! $gestione) {
return;
}
$clone = $gestione->replicate([
'gestione_attiva',
'stato',
'created_at',
'updated_at',
]);
$clone->anno_gestione = (int) ($data['anno_gestione'] ?? $gestione->anno_gestione);
$clone->stato = 'aperta';
$clone->gestione_attiva = true;
$clone->save();
}),
])
->columns([
TextColumn::make('gestione_label')
->label('Gestione')
->getStateUsing(fn(GestioneContabile $record): string => $record->gestione_label)
->sortable(),
TextColumn::make('tipo_gestione')
->label('Tipo')
->sortable(),
TextColumn::make('numero_straordinaria')
->label('N° Straord.')
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextInputColumn::make('denominazione')
->label('Denominazione')
->searchable(),
DatePickerColumn::make('data_inizio')
->label('Data inizio')
->native(false),
DatePickerColumn::make('data_fine')
->label('Data fine')
->native(false),
SelectColumn::make('stato')
->label('Stato')
->options([
'aperta' => 'Aperta',
'consolidata' => 'Consolidata',
'chiusa' => 'Chiusa',
]),
ToggleColumn::make('gestione_attiva')
->label('Attiva'),
TextColumn::make('mesi_rate')
->label('Mesi rate')
->getStateUsing(function (GestioneContabile $record): string {
$mesi = match ($record->tipo_gestione) {
'ordinaria' => (array) ($record->mesi_rate_ordinaria ?? []),
'riscaldamento' => (array) ($record->mesi_rate_riscaldamento ?? []),
'straordinaria' => (array) ($record->mesi_rate_straordinaria ?? []),
default => [],
};
$mesi = array_values(array_unique(array_map('intval', $mesi)));
sort($mesi);
return $mesi ? implode(', ', $mesi) : '—';
})
->wrap(),
])
->actions([
Action::make('imposta_mesi_rate')
->label('Mesi rate')
->icon('heroicon-o-calendar')
->modalHeading('Imposta mesi emissione rate')
->fillForm(function (GestioneContabile $record): array {
$mesi = match ($record->tipo_gestione) {
'ordinaria' => (array) ($record->mesi_rate_ordinaria ?? []),
'riscaldamento' => (array) ($record->mesi_rate_riscaldamento ?? []),
'straordinaria' => (array) ($record->mesi_rate_straordinaria ?? []),
default => [],
};
$mesi = array_values(array_unique(array_map('intval', $mesi)));
sort($mesi);
return [
'mesi' => $mesi,
'replica_tutti_anni' => true,
];
})
->form([
CheckboxList::make('mesi')
->label('Mesi in cui emettere le rate')
->options([
1 => 'Gen',
2 => 'Feb',
3 => 'Mar',
4 => 'Apr',
5 => 'Mag',
6 => 'Giu',
7 => 'Lug',
8 => 'Ago',
9 => 'Set',
10 => 'Ott',
11 => 'Nov',
12 => 'Dic',
])
->columns(6)
->required(),
Toggle::make('replica_tutti_anni')
->label('Replica questi mesi su tutti gli anni (stesso tipo)')
->default(true),
])
->action(function (GestioneContabile $record, array $data): void {
$mesi = array_values(array_unique(array_map('intval', (array) ($data['mesi'] ?? []))));
sort($mesi);
$field = match ($record->tipo_gestione) {
'ordinaria' => 'mesi_rate_ordinaria',
'riscaldamento' => 'mesi_rate_riscaldamento',
'straordinaria' => 'mesi_rate_straordinaria',
default => 'mesi_rate_ordinaria',
};
$query = GestioneContabile::query()
->where('stabile_id', $record->stabile_id)
->where('tipo_gestione', $record->tipo_gestione);
if (empty($data['replica_tutti_anni'])) {
$query->whereKey($record->getKey());
}
$query->each(function (GestioneContabile $g) use ($field, $mesi): void {
$g->{$field} = $mesi;
$g->save();
});
}),
Action::make('apri_archivio_gestioni')
->label('Apri archivio')
->url(fn() => \App\Filament\Pages\Condomini\StabilePage::getUrl(panel: 'admin-filament') . '?tab=gestioni&gestioni_tab=archivio'),
Action::make('archivia_gestione')
->label('Sposta in archivio')
->icon('heroicon-o-archive-box')
->requiresConfirmation()
->visible(fn() => $this->scope !== 'archivio')
->action(fn(GestioneContabile $record) => $this->archiveGestione($record)),
Action::make('ripristina_gestione')
->label('Ripristina in gestione')
->icon('heroicon-o-arrow-path')
->requiresConfirmation()
->visible(fn() => $this->scope === 'archivio')
->action(fn(GestioneContabile $record) => $this->restoreGestione($record)),
])
->bulkActions([
BulkAction::make('imposta_mesi_rate')
->label('Imposta mesi rate')
->icon('heroicon-o-calendar')
->modalHeading('Imposta mesi emissione rate (selezionati)')
->form([
CheckboxList::make('mesi')
->label('Mesi in cui emettere le rate')
->options([
1 => 'Gen',
2 => 'Feb',
3 => 'Mar',
4 => 'Apr',
5 => 'Mag',
6 => 'Giu',
7 => 'Lug',
8 => 'Ago',
9 => 'Set',
10 => 'Ott',
11 => 'Nov',
12 => 'Dic',
])
->columns(6)
->required(),
])
->action(function ($records, array $data): void {
$mesi = array_values(array_unique(array_map('intval', (array) ($data['mesi'] ?? []))));
sort($mesi);
foreach ($records as $record) {
if (! $record instanceof GestioneContabile) {
continue;
}
$field = match ($record->tipo_gestione) {
'ordinaria' => 'mesi_rate_ordinaria',
'riscaldamento' => 'mesi_rate_riscaldamento',
'straordinaria' => 'mesi_rate_straordinaria',
default => 'mesi_rate_ordinaria',
};
$record->{$field} = $mesi;
$record->save();
}
}),
BulkAction::make('archivia_gestioni')
->label('Sposta in archivio')
->icon('heroicon-o-archive-box')
->requiresConfirmation()
->visible(fn() => $this->scope !== 'archivio')
->action(function ($records): void {
foreach ($records as $record) {
if (! $record instanceof GestioneContabile) {
continue;
}
$this->archiveGestione($record);
}
}),
BulkAction::make('ripristina_gestioni')
->label('Ripristina in gestione')
->icon('heroicon-o-arrow-path')
->requiresConfirmation()
->visible(fn() => $this->scope === 'archivio')
->action(function ($records): void {
foreach ($records as $record) {
if (! $record instanceof GestioneContabile) {
continue;
}
$this->restoreGestione($record);
}
}),
]);
}
public function render()
{
return view('filament.pages.condomini.components.gestioni-stabile-table');
}
public function makeFilamentTranslatableContentDriver(): ?TranslatableContentDriver
{
return null;
}
/**
* Sposta una gestione in archivio logico (chiusa + non attiva).
*/
private function archiveGestione(GestioneContabile $gestione): void
{
if (in_array($gestione->stato, ['chiusa', 'consolidata'], true)) {
$gestione->gestione_attiva = false;
$gestione->save();
return;
}
$gestione->stato = 'chiusa';
$gestione->gestione_attiva = false;
$gestione->save();
}
/**
* Ripristina una gestione in elenco attivo.
*/
private function restoreGestione(GestioneContabile $gestione): void
{
$gestione->stato = 'aperta';
$gestione->gestione_attiva = true;
$gestione->save();
}
private function restoreBestGestioneYear(int $year): void
{
$user = Auth::user();
if (! $user instanceof User) {
return;
}
$stabileId = StabileContext::resolveActiveStabileId($user);
if (! $stabileId) {
return;
}
$gestioni = GestioneContabile::query()
->where('stabile_id', $stabileId)
->where('anno_gestione', $year)
->get();
if ($gestioni->isEmpty()) {
return;
}
$keep = $this->selectEffectiveGestione($gestioni);
$gestioni->each(function (GestioneContabile $g) use ($keep): void {
if ($keep && $g->id === $keep->id) {
$this->restoreGestione($g);
return;
}
$this->archiveGestione($g);
});
}
/**
* Seleziona la gestione "migliore" del gruppo anno/tipo usando punteggio attività.
*/
private function selectEffectiveGestione(Collection $group): ?GestioneContabile
{
if ($group->isEmpty()) {
return null;
}
$ranked = $group->map(function (GestioneContabile $g): array {
return [
'gestione' => $g,
'score' => $this->getGestioneActivityScore($g),
'attiva' => $g->gestione_attiva ? 1 : 0,
'data_fine' => $g->data_fine?->timestamp ?? 0,
'data_inizio' => $g->data_inizio?->timestamp ?? 0,
'id' => $g->id,
];
})->sortByDesc(function (array $row): array {
return [$row['score'], $row['attiva'], $row['data_fine'], $row['data_inizio'], $row['id']];
})->values();
return $ranked->first()['gestione'] ?? $group->first();
}
/**
* Calcola un punteggio attività interrogando tabelle contabili collegate.
*/
private function getGestioneActivityScore(GestioneContabile $gestione): int
{
$score = 0;
if (Schema::hasTable('operazioni_contabili') && Schema::hasColumn('operazioni_contabili', 'gestione_id')) {
if (DB::table('operazioni_contabili')->where('gestione_id', $gestione->id)->exists()) {
$score += 100;
}
}
if (Schema::hasTable('incassi') && Schema::hasColumn('incassi', 'gestione_id')) {
if (DB::table('incassi')->where('gestione_id', $gestione->id)->exists()) {
$score += 50;
}
}
if (Schema::hasTable('incassi_estratto_conto') && Schema::hasColumn('incassi_estratto_conto', 'gestione_id')) {
if (DB::table('incassi_estratto_conto')->where('gestione_id', $gestione->id)->exists()) {
$score += 30;
}
}
if (Schema::hasTable('registro_ritenute_acconto') && Schema::hasColumn('registro_ritenute_acconto', 'gestione_id')) {
if (DB::table('registro_ritenute_acconto')->where('gestione_id', $gestione->id)->exists()) {
$score += 10;
}
}
if (Schema::hasTable('voci_spesa') && Schema::hasColumn('voci_spesa', 'gestione_contabile_id')) {
$exists = DB::table('voci_spesa')
->where('gestione_contabile_id', $gestione->id)
->exists();
if ($exists) {
$score += 20;
}
}
return $score;
}
}