stabileId = $stabileId; $this->mountInteractsWithTable(); } protected function getTableQuery(): Builder { $user = Auth::user(); if (! $user instanceof User) { return DatiBancari::query()->whereRaw('1 = 0'); } $allowed = StabileContext::accessibleStabili($user)->pluck('id')->map(fn($v) => (int) $v)->all(); if (! in_array((int) $this->stabileId, $allowed, true)) { return DatiBancari::query()->whereRaw('1 = 0'); } return DatiBancari::query() ->with(['contatto']) ->where('stabile_id', (int) $this->stabileId) ->orderByDesc('is_nostro_conto') ->orderBy('denominazione_banca') ->orderByDesc('id'); } public function table(Table $table): Table { return $table ->striped() ->columns([ TextColumn::make('denominazione_banca') ->label('Banca / Cassa') ->wrap() ->searchable(), TextColumn::make('tipo_conto') ->label('Tipo') ->sortable(), TextColumn::make('iban') ->label('IBAN') ->copyable() ->toggleable(), IconColumn::make('is_nostro_conto') ->label('Nostro') ->boolean() ->toggleable(), TextColumn::make('stato_conto') ->label('Stato') ->sortable(), TextColumn::make('saldo_iniziale') ->label('Saldo iniz.') ->money('EUR') ->toggleable(isToggledHiddenByDefault: true), TextColumn::make('data_saldo_iniziale') ->label('Data saldo') ->date('d/m/Y') ->toggleable(isToggledHiddenByDefault: true), TextColumn::make('saldo_ufficiale') ->label('Saldo ufficiale') ->getStateUsing(fn(DatiBancari $record): ?float => $this->resolveLatestSaldoSnapshotValue($record)) ->money('EUR') ->toggleable(), TextColumn::make('ultimo_estratto') ->label('Ultimo estratto') ->getStateUsing(fn(DatiBancari $record): string => $this->resolveLatestSaldoSnapshotLabel($record)) ->wrap() ->toggleable(), TextColumn::make('contatto.nome_completo') ->label('Contatto') ->wrap() ->toggleable(isToggledHiddenByDefault: true), ]) ->headerActions([ Action::make('importa_da_stabili_mdb') ->label('Importa da Stabili.mdb') ->icon('heroicon-o-arrow-down-tray') ->color('warning') ->requiresConfirmation() ->action(function (): void { $user = Auth::user(); if (! $user instanceof User) { Notification::make()->title('Utente non valido')->danger()->send(); return; } $stabile = Stabile::query()->with('amministratore')->find((int) $this->stabileId); if (! $stabile) { Notification::make()->title('Stabile non trovato')->danger()->send(); return; } $amm = $stabile->amministratore; $adminCode = $amm ? (string) ($amm->codice_univoco ?: $amm->codice_amministratore ?: ''): ''; $adminCode = trim($adminCode); if ($adminCode === '') { $adminCode = $amm ? ('ID' . $amm->id) : 'ID0'; } $adminCode = preg_replace('/[^A-Za-z0-9_-]+/', '_', $adminCode) ?: $adminCode; $adminCode = trim((string) $adminCode, '_'); if ($adminCode === '') { $adminCode = $amm ? ('ID' . $amm->id) : 'ID0'; } $basePath = '/mnt/gescon-archives/gescon/' . $adminCode; $legacyCode = (string) ($stabile->codice_stabile ?? ($stabile->cod_stabile ?? '')); $legacyCode = str_pad(trim($legacyCode), 4, '0', STR_PAD_LEFT); try { (new StabileEnrichmentService())->enrich($legacyCode, $basePath); } catch (\Throwable $e) { Notification::make() ->title('Import fallito') ->body($e->getMessage()) ->danger() ->send(); return; } Notification::make() ->title('Import completato') ->body('Dati bancari aggiornati da Stabili.mdb') ->success() ->send(); }), Action::make('nuovo_conto') ->label('Nuovo conto') ->icon('heroicon-o-plus') ->color('success') ->modalHeading('Nuovo conto') ->form($this->formSchema()) ->action(function (array $data): void { $user = Auth::user(); $uid = $user instanceof User ? (int) $user->id : null; $payload = $this->normalizePayload($data); $payload['stabile_id'] = (int) $this->stabileId; if ($uid) { $payload['creato_da'] = $uid; $payload['modificato_da'] = $uid; } DatiBancari::create($payload); Notification::make() ->title('Conto creato') ->success() ->send(); }), ]) ->actions([ Action::make('movimenti') ->label('Movimenti') ->icon('heroicon-o-receipt-percent') ->url(function (DatiBancari $record): string { $base = CasseBancheMovimenti::getUrl(panel: 'admin-filament'); $contoId = (int) ($record->id ?? 0); if ($contoId <= 0) { return $base; } return $base . '?' . http_build_query([ 'conto_id' => $contoId, ]); }) ->openUrlInNewTab(), Action::make('saldi') ->label('Saldi') ->icon('heroicon-o-scale') ->url(fn(DatiBancari $record): string => SaldiContiArchivio::getUrl(['conto_id' => (int) $record->id], panel: 'admin-filament')) ->openUrlInNewTab(), Action::make('contatto') ->label('Contatto') ->icon('heroicon-o-identification') ->visible(fn(DatiBancari $record): bool => (int) ($record->contatto_id ?? 0) > 0) ->url(fn(DatiBancari $record): string => RubricaUniversaleScheda::getUrl([ 'record' => (int) $record->contatto_id, ], panel: 'admin-filament')), Action::make('modifica') ->label('Modifica') ->icon('heroicon-o-pencil-square') ->modalHeading('Modifica conto') ->fillForm(fn(DatiBancari $record): array=> [ 'denominazione_banca' => $record->denominazione_banca, 'tipo_conto' => $record->tipo_conto, 'stato_conto' => $record->stato_conto, 'iban' => $record->iban, 'numero_conto' => $record->numero_conto, 'abi' => $record->abi, 'cab' => $record->cab, 'cin' => $record->cin, 'bic_swift' => $record->bic_swift, 'cuc' => $record->cuc, 'sia' => $record->sia, 'intestazione_conto' => $record->intestazione_conto, 'data_saldo_iniziale' => $record->data_saldo_iniziale, 'saldo_iniziale' => $record->saldo_iniziale, 'valuta' => $record->valuta, 'is_nostro_conto' => (bool) $record->is_nostro_conto, 'contatto_id' => $record->contatto_id, 'note' => $record->note, ]) ->form($this->formSchema()) ->action(function (DatiBancari $record, array $data): void { $user = Auth::user(); $uid = $user instanceof User ? (int) $user->id : null; $payload = $this->normalizePayload($data); if ($uid) { $payload['modificato_da'] = $uid; } $record->fill($payload); if ($record->isDirty()) { $record->save(); } Notification::make() ->title('Conto aggiornato') ->success() ->send(); }), Action::make('elimina') ->label('Elimina') ->icon('heroicon-o-trash') ->color('danger') ->requiresConfirmation() ->action(function (DatiBancari $record): void { $record->delete(); Notification::make() ->title('Conto eliminato') ->success() ->send(); }), ]); } public function makeFilamentTranslatableContentDriver(): ?TranslatableContentDriver { return null; } /** * @return array */ private function formSchema(): array { return [ TextInput::make('denominazione_banca') ->label('Denominazione banca/cassa') ->required() ->maxLength(255), Select::make('tipo_conto') ->label('Tipo conto') ->options([ 'corrente' => 'Corrente', 'deposito' => 'Deposito', 'risparmio' => 'Risparmio', ]) ->default('corrente') ->required(), Select::make('stato_conto') ->label('Stato') ->options([ 'attivo' => 'Attivo', 'sospeso' => 'Sospeso', 'chiuso' => 'Chiuso', ]) ->default('attivo') ->required(), TextInput::make('iban') ->label('IBAN') ->maxLength(64), TextInput::make('numero_conto') ->label('Numero conto') ->maxLength(64), TextInput::make('abi') ->label('ABI') ->maxLength(16), TextInput::make('cab') ->label('CAB') ->maxLength(16), TextInput::make('cin') ->label('CIN') ->maxLength(8), TextInput::make('bic_swift') ->label('BIC / SWIFT') ->maxLength(32), TextInput::make('cuc') ->label('CUC') ->maxLength(32), TextInput::make('sia') ->label('SIA') ->maxLength(32), TextInput::make('intestazione_conto') ->label('Intestazione conto') ->maxLength(255), DatePicker::make('data_saldo_iniziale') ->label('Data saldo iniziale'), TextInput::make('saldo_iniziale') ->label('Saldo iniziale') ->numeric() ->default(0), TextInput::make('valuta') ->label('Valuta') ->maxLength(8) ->default('EUR'), Toggle::make('is_nostro_conto') ->label('Nostro conto') ->default(false), Select::make('contatto_id') ->label('Contatto banca (rubrica)') ->searchable() ->options(function (): array { $records = RubricaUniversale::query() ->categoria('banca') ->attivi() ->orderBy('ragione_sociale') ->orderBy('cognome') ->orderBy('nome') ->get(['id', 'ragione_sociale', 'nome', 'cognome']); return $records ->mapWithKeys(function (RubricaUniversale $r): array { $label = trim((string) ($r->nome_completo ?? '')); if ($label === '') { $label = trim((string) ($r->ragione_sociale ?? '')); } if ($label === '') { $label = trim((string) (($r->cognome ?? '') . ' ' . ($r->nome ?? ''))); } if ($label === '') { $label = 'Contatto #' . (int) $r->id; } return [(int) $r->id => $label]; }) ->all(); }) ->placeholder('—'), Textarea::make('note') ->label('Note') ->rows(3), ]; } /** * @param array $data * @return array */ private function normalizePayload(array $data): array { return [ 'denominazione_banca' => isset($data['denominazione_banca']) ? trim((string) $data['denominazione_banca']) : null, 'tipo_conto' => isset($data['tipo_conto']) ? (string) $data['tipo_conto'] : null, 'stato_conto' => isset($data['stato_conto']) ? (string) $data['stato_conto'] : null, 'iban' => isset($data['iban']) ? trim((string) $data['iban']) : null, 'numero_conto' => isset($data['numero_conto']) ? trim((string) $data['numero_conto']) : null, 'abi' => isset($data['abi']) ? trim((string) $data['abi']) : null, 'cab' => isset($data['cab']) ? trim((string) $data['cab']) : null, 'cin' => isset($data['cin']) ? trim((string) $data['cin']) : null, 'bic_swift' => isset($data['bic_swift']) ? trim((string) $data['bic_swift']) : null, 'cuc' => isset($data['cuc']) ? trim((string) $data['cuc']) : null, 'sia' => isset($data['sia']) ? trim((string) $data['sia']) : null, 'intestazione_conto' => isset($data['intestazione_conto']) ? trim((string) $data['intestazione_conto']) : null, 'data_saldo_iniziale' => $data['data_saldo_iniziale'] ?? null, 'saldo_iniziale' => $data['saldo_iniziale'] ?? null, 'valuta' => isset($data['valuta']) ? trim((string) $data['valuta']) : null, 'is_nostro_conto' => (bool) ($data['is_nostro_conto'] ?? false), 'contatto_id' => ! empty($data['contatto_id']) ? (int) $data['contatto_id'] : null, 'note' => isset($data['note']) ? trim((string) $data['note']) : null, ]; } private function resolveLatestSaldoSnapshot(DatiBancari $record): ?SaldoConto { static $cache = []; $cacheKey = (int) $record->id; if (array_key_exists($cacheKey, $cache)) { return $cache[$cacheKey]; } return $cache[$cacheKey] = SaldoConto::query() ->with('documento') ->where('stabile_id', (int) $record->stabile_id) ->where('conto_id', (int) $record->id) ->orderByDesc('data_saldo') ->orderByDesc('id') ->first(); } private function resolveLatestSaldoSnapshotValue(DatiBancari $record): ?float { $snapshot = $this->resolveLatestSaldoSnapshot($record); return $snapshot ? (float) $snapshot->saldo : null; } private function resolveLatestSaldoSnapshotLabel(DatiBancari $record): string { $snapshot = $this->resolveLatestSaldoSnapshot($record); if (! $snapshot) { return 'Nessun estratto registrato'; } $parts = []; if ($snapshot->tipo_estratto) { $parts[] = match ($snapshot->tipo_estratto) { 'estratto_conto' => 'Estratto conto', 'saldo_banca' => 'Saldo banca', 'riconciliazione' => 'Riconciliazione', default => 'Altro', }; } if ($snapshot->periodo_da || $snapshot->periodo_a) { $from = $snapshot->periodo_da?->format('d/m/Y') ?: '...'; $to = $snapshot->periodo_a?->format('d/m/Y') ?: '...'; $parts[] = $from . ' - ' . $to; } else { $parts[] = $snapshot->data_saldo?->format('d/m/Y') ?: 'Data non indicata'; } if ($snapshot->documento?->nome_originale) { $parts[] = $snapshot->documento->nome_originale; } return implode(' · ', $parts); } }