493 lines
20 KiB
PHP
493 lines
20 KiB
PHP
<?php
|
|
namespace App\Livewire\Condomini;
|
|
|
|
use App\Filament\Pages\Contabilita\CasseBancheMovimenti;
|
|
use App\Filament\Pages\Contabilita\SaldiContiArchivio;
|
|
use App\Filament\Pages\Gescon\RubricaUniversaleScheda;
|
|
use App\Models\DatiBancari;
|
|
use App\Models\RubricaUniversale;
|
|
use App\Models\Stabile;
|
|
use App\Models\User;
|
|
use App\Modules\Contabilita\Models\SaldoConto;
|
|
use App\Services\GesconImport\StabileEnrichmentService;
|
|
use App\Support\StabileContext;
|
|
use Filament\Actions\Action;
|
|
use Filament\Forms\Components\DatePicker;
|
|
use Filament\Forms\Components\Select;
|
|
use Filament\Forms\Components\Textarea;
|
|
use Filament\Forms\Components\TextInput;
|
|
use Filament\Forms\Components\Toggle;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Support\Contracts\TranslatableContentDriver;
|
|
use Filament\Tables\Columns\IconColumn;
|
|
use Filament\Tables\Columns\TextColumn;
|
|
use Filament\Tables\Table;
|
|
use Filament\Tables\TableComponent;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
class StabileDatiBancariTable extends TableComponent
|
|
{
|
|
public int $stabileId;
|
|
|
|
public function mount(int $stabileId): void
|
|
{
|
|
$this->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<int, \Filament\Forms\Components\Component>
|
|
*/
|
|
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<string,mixed> $data
|
|
* @return array<string,mixed>
|
|
*/
|
|
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);
|
|
}
|
|
}
|